@lesscap/baton-cli 0.7.0 → 0.7.3
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/LICENSE +21 -0
- package/README.md +18 -8
- package/dist/baton.mjs +266 -124
- package/package.json +11 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 bencode
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -24,8 +24,12 @@ in (the worker spawns it to run each turn). Override the path with
|
|
|
24
24
|
cd /path/to/your-repo
|
|
25
25
|
|
|
26
26
|
# 2. register this machine as a worker for a project
|
|
27
|
-
baton worker register
|
|
28
|
-
|
|
27
|
+
baton worker register \
|
|
28
|
+
--url https://baton.fmap.dev/api \
|
|
29
|
+
--project <projectId> \
|
|
30
|
+
--name my-machine \
|
|
31
|
+
--baseBranch main
|
|
32
|
+
# → generates ./.baton.json with server, project, base branch, and worker token
|
|
29
33
|
|
|
30
34
|
# 3. run the worker daemon (foreground; Ctrl-C to stop)
|
|
31
35
|
baton worker run
|
|
@@ -35,12 +39,18 @@ The worker authenticates with its own bearer token — **no login needed**. Once
|
|
|
35
39
|
running, it shows up (alive) under the project's workers in the web back-office;
|
|
36
40
|
new sessions / inbound DingTalk messages are then executed by it.
|
|
37
41
|
|
|
38
|
-
`.baton.json` is per-directory local state (gitignored by convention)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
`.baton.json` is per-directory local state (gitignored by convention) and is
|
|
43
|
+
generated by `baton worker register`; `baton init` is optional. `--baseBranch`
|
|
44
|
+
wins over `BATON_BASE_BRANCH`, then an existing saved value. If none is set,
|
|
45
|
+
registration records the repo's current branch, falling back to `main`.
|
|
46
|
+
|
|
47
|
+
Before creating each new session worktree, the worker fetches that branch and
|
|
48
|
+
forks from the updated `origin/<branch>` ref without moving the source checkout.
|
|
49
|
+
Repositories without an `origin` use their local branch. Environments that keep
|
|
50
|
+
Git credentials in a separate broker can set `BATON_GIT_SYNC_BIN` to a trusted
|
|
51
|
+
executable; Baton invokes it as `<executable> <repo> <branch>`, then verifies the
|
|
52
|
+
updated remote-tracking ref before creating the worktree. The helper must print
|
|
53
|
+
the synced commit hash to stdout.
|
|
44
54
|
|
|
45
55
|
## Other commands
|
|
46
56
|
|
package/dist/baton.mjs
CHANGED
|
@@ -4684,11 +4684,11 @@ var channelClient = (baseUrl) => {
|
|
|
4684
4684
|
if (!res.ok) throw new Error(`GET channel \u2192 ${res.status}: ${await res.text()}`);
|
|
4685
4685
|
return await res.json();
|
|
4686
4686
|
},
|
|
4687
|
-
update: async (channelId,
|
|
4687
|
+
update: async (channelId, patch) => {
|
|
4688
4688
|
const res = await fetch(u(`/channels/${channelId}`), {
|
|
4689
4689
|
method: "PATCH",
|
|
4690
4690
|
headers: JSON_CT,
|
|
4691
|
-
body: JSON.stringify(
|
|
4691
|
+
body: JSON.stringify(patch)
|
|
4692
4692
|
});
|
|
4693
4693
|
if (!res.ok) throw new Error(`PATCH channel \u2192 ${res.status}: ${await res.text()}`);
|
|
4694
4694
|
return await res.json();
|
|
@@ -4797,10 +4797,6 @@ var saveProjectConfig = (path, config) => {
|
|
|
4797
4797
|
writeFileSync(path, `${JSON.stringify(config, null, 2)}
|
|
4798
4798
|
`, "utf8");
|
|
4799
4799
|
};
|
|
4800
|
-
var patch = (path, fn) => {
|
|
4801
|
-
saveProjectConfig(path, fn(loadProjectConfig(path)));
|
|
4802
|
-
};
|
|
4803
|
-
var setWorker = (path, w) => patch(path, (cfg) => ({ ...cfg, worker: w }));
|
|
4804
4800
|
var requireBase = (cfg) => {
|
|
4805
4801
|
if (!cfg.server || !cfg.project || !cfg.worker)
|
|
4806
4802
|
throw new Error("project-config missing server/project/worker \u2014 run `baton worker register`");
|
|
@@ -4811,6 +4807,7 @@ var viewWorker = (cfg) => {
|
|
|
4811
4807
|
return {
|
|
4812
4808
|
server,
|
|
4813
4809
|
projectId: project2,
|
|
4810
|
+
baseBranch: cfg.baseBranch,
|
|
4814
4811
|
workerId: worker2.id,
|
|
4815
4812
|
agentKind: worker2.agentKind ?? "claude-code",
|
|
4816
4813
|
name: worker2.name,
|
|
@@ -4821,6 +4818,7 @@ var viewWorker = (cfg) => {
|
|
|
4821
4818
|
var worktreeConfig = (cfg) => ({
|
|
4822
4819
|
server: cfg.server,
|
|
4823
4820
|
project: cfg.projectId,
|
|
4821
|
+
baseBranch: cfg.baseBranch,
|
|
4824
4822
|
worker: {
|
|
4825
4823
|
id: cfg.workerId,
|
|
4826
4824
|
name: cfg.name,
|
|
@@ -4944,7 +4942,7 @@ var projectClient = (baseUrl) => {
|
|
|
4944
4942
|
create: (input) => request(u("/projects"), { method: "POST", body: input }),
|
|
4945
4943
|
listByWorkspace: (workspaceId) => request(u(`/workspaces/${workspaceId}/projects`), { method: "GET" }),
|
|
4946
4944
|
get: (id) => request(u(`/projects/${id}`), { method: "GET" }),
|
|
4947
|
-
update: (id,
|
|
4945
|
+
update: (id, patch) => request(u(`/projects/${id}`), { method: "PATCH", body: patch }),
|
|
4948
4946
|
remove: (id) => request(u(`/projects/${id}`), { method: "DELETE" })
|
|
4949
4947
|
};
|
|
4950
4948
|
};
|
|
@@ -4967,7 +4965,7 @@ var requirementClient = (baseUrl) => {
|
|
|
4967
4965
|
listByProject: (projectId) => request(u(`/projects/${projectId}/requirements`), { method: "GET" }),
|
|
4968
4966
|
get: (id) => request(u(`/requirements/${id}`), { method: "GET" }),
|
|
4969
4967
|
getByCode: (projectId, code) => fetchItemByCode(baseUrl, projectId, code, "requirement"),
|
|
4970
|
-
update: (id,
|
|
4968
|
+
update: (id, patch) => request(u(`/requirements/${id}`), { method: "PATCH", body: patch }),
|
|
4971
4969
|
setStatus: (id, status) => request(u(`/requirements/${id}`), { method: "PATCH", body: { status } }),
|
|
4972
4970
|
remove: (id) => request(u(`/requirements/${id}`), { method: "DELETE" })
|
|
4973
4971
|
};
|
|
@@ -5061,7 +5059,7 @@ var taskClient = (baseUrl) => {
|
|
|
5061
5059
|
listByRequirement: (requirementId) => request(u(`/requirements/${requirementId}/tasks`), { method: "GET" }),
|
|
5062
5060
|
get: (id) => request(u(`/tasks/${id}`), { method: "GET" }),
|
|
5063
5061
|
getByCode: (projectId, code) => fetchItemByCode(baseUrl, projectId, code, "task"),
|
|
5064
|
-
update: (id,
|
|
5062
|
+
update: (id, patch) => request(u(`/tasks/${id}`), { method: "PATCH", body: patch }),
|
|
5065
5063
|
setStatus: (id, status) => request(u(`/tasks/${id}`), { method: "PATCH", body: { status } }),
|
|
5066
5064
|
remove: (id) => request(u(`/tasks/${id}`), { method: "DELETE" }),
|
|
5067
5065
|
listComments: (id) => request(u(`/tasks/${id}/comments`), { method: "GET" }),
|
|
@@ -5097,7 +5095,7 @@ var workspaceClient = (baseUrl) => {
|
|
|
5097
5095
|
create: (input) => request(u("/workspaces"), { method: "POST", body: input }),
|
|
5098
5096
|
list: () => request(u("/workspaces"), { method: "GET" }),
|
|
5099
5097
|
get: (id) => request(u(`/workspaces/${id}`), { method: "GET" }),
|
|
5100
|
-
update: (id,
|
|
5098
|
+
update: (id, patch) => request(u(`/workspaces/${id}`), { method: "PATCH", body: patch }),
|
|
5101
5099
|
remove: (id) => request(u(`/workspaces/${id}`), { method: "DELETE" })
|
|
5102
5100
|
};
|
|
5103
5101
|
};
|
|
@@ -5855,11 +5853,11 @@ var loopSetCommand = defineCommand({
|
|
|
5855
5853
|
},
|
|
5856
5854
|
run: async ({ args }) => {
|
|
5857
5855
|
const c = clientFor(args);
|
|
5858
|
-
const
|
|
5859
|
-
if (args.message !== void 0)
|
|
5860
|
-
if (args.every !== void 0 && args.every !== "")
|
|
5861
|
-
if (args.name !== void 0)
|
|
5862
|
-
const loop2 = await c.loops.update(Number(args.loop),
|
|
5856
|
+
const patch = {};
|
|
5857
|
+
if (args.message !== void 0) patch.message = args.message;
|
|
5858
|
+
if (args.every !== void 0 && args.every !== "") patch.intervalSec = parseDuration(args.every);
|
|
5859
|
+
if (args.name !== void 0) patch.name = args.name;
|
|
5860
|
+
const loop2 = await c.loops.update(Number(args.loop), patch);
|
|
5863
5861
|
console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
|
|
5864
5862
|
}
|
|
5865
5863
|
});
|
|
@@ -5882,7 +5880,7 @@ var loop = defineCommand({
|
|
|
5882
5880
|
var createProject = (c, input, json) => c.projects.create(input).then((p) => renderOne(p, fmtProject, json));
|
|
5883
5881
|
var listProjects = (c, workspaceId, json) => c.projects.listByWorkspace(workspaceId).then((ps) => renderList(ps, fmtProject, json));
|
|
5884
5882
|
var getProject = (c, id, json) => c.projects.get(id).then((p) => renderOne(p, fmtProject, json));
|
|
5885
|
-
var updateProject = (c, id,
|
|
5883
|
+
var updateProject = (c, id, patch, json) => c.projects.update(id, patch).then((p) => renderOne(p, fmtProject, json));
|
|
5886
5884
|
var removeProject = (c, id, json) => c.projects.remove(id).then(() => removed("project", id, json));
|
|
5887
5885
|
var project = defineCommand({
|
|
5888
5886
|
meta: { name: "project", description: "manage projects" },
|
|
@@ -5932,13 +5930,13 @@ var project = defineCommand({
|
|
|
5932
5930
|
...common
|
|
5933
5931
|
},
|
|
5934
5932
|
run: async ({ args }) => {
|
|
5935
|
-
const
|
|
5933
|
+
const patch = {
|
|
5936
5934
|
...args.name !== void 0 ? { name: args.name } : {},
|
|
5937
5935
|
...args.desc !== void 0 ? { description: args.desc } : {}
|
|
5938
5936
|
};
|
|
5939
|
-
if (Object.keys(
|
|
5937
|
+
if (Object.keys(patch).length === 0) throw new Error("pass --name and/or --desc");
|
|
5940
5938
|
console.log(
|
|
5941
|
-
await updateProject(clientFor(args), Number(args.id),
|
|
5939
|
+
await updateProject(clientFor(args), Number(args.id), patch, Boolean(args.json))
|
|
5942
5940
|
);
|
|
5943
5941
|
}
|
|
5944
5942
|
}),
|
|
@@ -6131,7 +6129,7 @@ var relay = defineCommand({
|
|
|
6131
6129
|
|
|
6132
6130
|
// src/commands/requirement.ts
|
|
6133
6131
|
var createRequirement = (c, input, json) => c.requirements.create(input).then((r) => renderOne(r, fmtRequirement, json));
|
|
6134
|
-
var updateRequirement = (c, id,
|
|
6132
|
+
var updateRequirement = (c, id, patch, json) => c.requirements.update(id, patch).then((r) => renderOne(r, fmtRequirement, json));
|
|
6135
6133
|
var listRequirements = (c, projectId, json) => c.requirements.listByProject(projectId).then((rs) => renderList(rs, fmtRequirement, json));
|
|
6136
6134
|
var getRequirement = (c, id, json) => c.requirements.get(id).then((r) => renderOne(r, fmtRequirement, json));
|
|
6137
6135
|
var setRequirementStatus = (c, id, status, json) => c.requirements.setStatus(id, status).then((r) => renderOne(r, fmtRequirement, json));
|
|
@@ -6174,14 +6172,14 @@ var requirement = defineCommand({
|
|
|
6174
6172
|
...common
|
|
6175
6173
|
},
|
|
6176
6174
|
run: async ({ args }) => {
|
|
6177
|
-
const
|
|
6175
|
+
const patch = {
|
|
6178
6176
|
...args.title !== void 0 ? { title: args.title } : {},
|
|
6179
6177
|
...args.body !== void 0 ? { body: args.body } : {}
|
|
6180
6178
|
};
|
|
6181
|
-
if (Object.keys(
|
|
6179
|
+
if (Object.keys(patch).length === 0) throw new Error("pass --title and/or --body");
|
|
6182
6180
|
const c = clientFor(args);
|
|
6183
6181
|
const id = await resolveByCode(c, resolveProjectId(args), args.code);
|
|
6184
|
-
console.log(await updateRequirement(c, id,
|
|
6182
|
+
console.log(await updateRequirement(c, id, patch, Boolean(args.json)));
|
|
6185
6183
|
}
|
|
6186
6184
|
}),
|
|
6187
6185
|
ls: defineCommand({
|
|
@@ -7096,6 +7094,7 @@ var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => conso
|
|
|
7096
7094
|
};
|
|
7097
7095
|
|
|
7098
7096
|
// src/session/runner.ts
|
|
7097
|
+
var isAgentConversationResumable = (config, transcriptPath = findTranscriptPath) => config.agentKind === "codex" ? !config.agentSessionId.startsWith("pending:") : transcriptPath(config.agentSessionId) !== null;
|
|
7099
7098
|
var subscribeStream = (url, ESCtor, seen, log, onUserMessage, onInterrupt, onReady) => {
|
|
7100
7099
|
const es = new ESCtor(url);
|
|
7101
7100
|
es.onopen = onReady;
|
|
@@ -7132,10 +7131,7 @@ var runDaemon = async (config, deps, signal) => {
|
|
|
7132
7131
|
const ESCtor = deps.eventSourceImpl ?? EventSource;
|
|
7133
7132
|
log(`bin: ${claudeExecutable() ?? "(sdk bundled)"} cwd: ${config.worktreePath}`);
|
|
7134
7133
|
log(`runtime env keys: ${maskedEnvKeys(deps.env)}`);
|
|
7135
|
-
const state = {
|
|
7136
|
-
firstSpawnDone: config.agentKind === "codex" ? !config.agentSessionId.startsWith("pending:") : findTranscriptPath(config.agentSessionId) !== null,
|
|
7137
|
-
seen: /* @__PURE__ */ new Set()
|
|
7138
|
-
};
|
|
7134
|
+
const state = { seen: /* @__PURE__ */ new Set() };
|
|
7139
7135
|
const pendingQueue = [];
|
|
7140
7136
|
let busy = false;
|
|
7141
7137
|
let reconciling = false;
|
|
@@ -7148,7 +7144,7 @@ var runDaemon = async (config, deps, signal) => {
|
|
|
7148
7144
|
while (pendingQueue.length > 0 && !signal.aborted) {
|
|
7149
7145
|
const msg = pendingQueue.shift();
|
|
7150
7146
|
if (!msg) break;
|
|
7151
|
-
const resuming =
|
|
7147
|
+
const resuming = isAgentConversationResumable(config);
|
|
7152
7148
|
log(`\u25B6 msg #${msg.sequence} (${resuming ? "resume" : "first"})`);
|
|
7153
7149
|
const turnAbort = new AbortController();
|
|
7154
7150
|
currentTurn = turnAbort;
|
|
@@ -7164,7 +7160,6 @@ var runDaemon = async (config, deps, signal) => {
|
|
|
7164
7160
|
turnAbort.signal
|
|
7165
7161
|
);
|
|
7166
7162
|
currentTurn = void 0;
|
|
7167
|
-
state.firstSpawnDone = true;
|
|
7168
7163
|
lastActivity = Date.now();
|
|
7169
7164
|
log(`\u2714 msg #${msg.sequence}${code === 0 ? "" : ` (exit ${code})`}`);
|
|
7170
7165
|
}
|
|
@@ -7426,7 +7421,7 @@ var session = defineCommand({
|
|
|
7426
7421
|
|
|
7427
7422
|
// src/commands/task.ts
|
|
7428
7423
|
var createTask = (c, input, json) => c.tasks.create(input).then((t) => renderOne(t, fmtTask, json));
|
|
7429
|
-
var updateTask = (c, id,
|
|
7424
|
+
var updateTask = (c, id, patch, json) => c.tasks.update(id, patch).then((t) => renderOne(t, fmtTask, json));
|
|
7430
7425
|
var listTasks = (c, requirementId, json) => c.tasks.listByRequirement(requirementId).then((ts) => renderList(ts, fmtTask, json));
|
|
7431
7426
|
var getTask = (c, id, json) => c.tasks.get(id).then((t) => renderOne(t, fmtTask, json));
|
|
7432
7427
|
var setTaskStatus = (c, id, status, json) => c.tasks.setStatus(id, status).then((t) => renderOne(t, fmtTask, json));
|
|
@@ -7480,14 +7475,14 @@ var task = defineCommand({
|
|
|
7480
7475
|
...common
|
|
7481
7476
|
},
|
|
7482
7477
|
run: async ({ args }) => {
|
|
7483
|
-
const
|
|
7478
|
+
const patch = {
|
|
7484
7479
|
...args.title !== void 0 ? { title: args.title } : {},
|
|
7485
7480
|
...args.body !== void 0 ? { body: args.body } : {}
|
|
7486
7481
|
};
|
|
7487
|
-
if (Object.keys(
|
|
7482
|
+
if (Object.keys(patch).length === 0) throw new Error("pass --title and/or --body");
|
|
7488
7483
|
const c = clientFor(args);
|
|
7489
7484
|
const id = await resolveTaskByCode(c, resolveProjectId(args), args.code);
|
|
7490
|
-
console.log(await updateTask(c, id,
|
|
7485
|
+
console.log(await updateTask(c, id, patch, Boolean(args.json)));
|
|
7491
7486
|
}
|
|
7492
7487
|
}),
|
|
7493
7488
|
ls: defineCommand({
|
|
@@ -7586,6 +7581,155 @@ var task = defineCommand({
|
|
|
7586
7581
|
// src/commands/worker/index.ts
|
|
7587
7582
|
import { hostname as osHostname } from "node:os";
|
|
7588
7583
|
|
|
7584
|
+
// src/session/worktree.ts
|
|
7585
|
+
import { execFile, execFileSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
7586
|
+
import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
7587
|
+
import { dirname, isAbsolute as isAbsolute2, resolve } from "node:path";
|
|
7588
|
+
import { promisify } from "node:util";
|
|
7589
|
+
var git = (repo, args) => spawnSync2("git", ["-C", repo, ...args], {
|
|
7590
|
+
stdio: "pipe",
|
|
7591
|
+
encoding: "utf8"
|
|
7592
|
+
});
|
|
7593
|
+
var commandError = (result) => {
|
|
7594
|
+
const raw = result.stderr || result.stdout || result.error?.message || `exit ${result.status}`;
|
|
7595
|
+
return raw.trim().replace(/(https?:\/\/)[^/@\s]+@/g, "$1***@");
|
|
7596
|
+
};
|
|
7597
|
+
var execFileAsync = promisify(execFile);
|
|
7598
|
+
var DEFAULT_SYNC_TIMEOUT_MS = 6e4;
|
|
7599
|
+
var asyncCommandError = (error, timeoutMs) => {
|
|
7600
|
+
const failure = error;
|
|
7601
|
+
if (failure.killed || failure.code === "ETIMEDOUT") return `timed out after ${timeoutMs}ms`;
|
|
7602
|
+
const raw = failure.stderr || failure.stdout || failure.message || String(error);
|
|
7603
|
+
return raw.trim().replace(/(https?:\/\/)[^/@\s]+@/g, "$1***@");
|
|
7604
|
+
};
|
|
7605
|
+
var validateBaseBranch = (branch) => {
|
|
7606
|
+
const normalized = branch.trim();
|
|
7607
|
+
const valid = spawnSync2("git", ["check-ref-format", "--branch", normalized], {
|
|
7608
|
+
stdio: "pipe",
|
|
7609
|
+
encoding: "utf8"
|
|
7610
|
+
});
|
|
7611
|
+
if (valid.status !== 0)
|
|
7612
|
+
throw new Error(`invalid base branch "${normalized}": ${commandError(valid)}`);
|
|
7613
|
+
return normalized;
|
|
7614
|
+
};
|
|
7615
|
+
var createWorktree = (input) => {
|
|
7616
|
+
if (existsSync4(input.worktreePath))
|
|
7617
|
+
throw new Error(`worktree path already exists: ${input.worktreePath}`);
|
|
7618
|
+
const head = spawnSync2("git", ["-C", input.repo, "rev-parse", "--verify", "-q", "HEAD"], {
|
|
7619
|
+
stdio: "pipe",
|
|
7620
|
+
encoding: "utf8"
|
|
7621
|
+
});
|
|
7622
|
+
if (head.status !== 0)
|
|
7623
|
+
throw new Error(
|
|
7624
|
+
`repo has no commits (unborn HEAD) \u2014 create an initial commit before starting sessions`
|
|
7625
|
+
);
|
|
7626
|
+
mkdirSync3(dirname(input.worktreePath), { recursive: true });
|
|
7627
|
+
const branch = `baton/${input.sessionCode}`;
|
|
7628
|
+
const r = spawnSync2(
|
|
7629
|
+
"git",
|
|
7630
|
+
["-C", input.repo, "worktree", "add", "-b", branch, input.worktreePath, input.base],
|
|
7631
|
+
{ stdio: "pipe", encoding: "utf8" }
|
|
7632
|
+
);
|
|
7633
|
+
if (r.status !== 0) {
|
|
7634
|
+
throw new Error(`git worktree add failed: ${r.stderr || r.stdout || `exit ${r.status}`}`);
|
|
7635
|
+
}
|
|
7636
|
+
};
|
|
7637
|
+
var repoHeadBranch = (repo) => {
|
|
7638
|
+
const r = git(repo, ["symbolic-ref", "--short", "HEAD"]);
|
|
7639
|
+
const branch = r.status === 0 ? r.stdout.trim() : "";
|
|
7640
|
+
return branch || "main";
|
|
7641
|
+
};
|
|
7642
|
+
var syncBaseBranch = async (repo, branch, options = {}) => {
|
|
7643
|
+
const checkedBranch = validateBaseBranch(branch);
|
|
7644
|
+
const env = options.env ?? process.env;
|
|
7645
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_SYNC_TIMEOUT_MS;
|
|
7646
|
+
const origin = git(repo, ["remote", "get-url", "origin"]);
|
|
7647
|
+
if (origin.status !== 0) {
|
|
7648
|
+
const localRef = `refs/heads/${checkedBranch}`;
|
|
7649
|
+
const verified2 = git(repo, ["rev-parse", "--verify", "-q", localRef]);
|
|
7650
|
+
if (verified2.status !== 0) throw new Error(`local base branch does not exist: ${checkedBranch}`);
|
|
7651
|
+
return localRef;
|
|
7652
|
+
}
|
|
7653
|
+
const helper = env.BATON_GIT_SYNC_BIN?.trim();
|
|
7654
|
+
const command = helper ?? "git";
|
|
7655
|
+
const args = helper ? [repo, checkedBranch] : [
|
|
7656
|
+
"-C",
|
|
7657
|
+
repo,
|
|
7658
|
+
"fetch",
|
|
7659
|
+
"--no-tags",
|
|
7660
|
+
"origin",
|
|
7661
|
+
`+refs/heads/${checkedBranch}:refs/remotes/origin/${checkedBranch}`
|
|
7662
|
+
];
|
|
7663
|
+
let synced;
|
|
7664
|
+
try {
|
|
7665
|
+
synced = await execFileAsync(command, args, {
|
|
7666
|
+
encoding: "utf8",
|
|
7667
|
+
env: { ...env, GIT_TERMINAL_PROMPT: "0" },
|
|
7668
|
+
timeout: timeoutMs
|
|
7669
|
+
});
|
|
7670
|
+
} catch (error) {
|
|
7671
|
+
const via = helper ? ` via ${helper}` : "";
|
|
7672
|
+
throw new Error(
|
|
7673
|
+
`git sync failed for origin/${checkedBranch}${via}: ${asyncCommandError(error, timeoutMs)}`
|
|
7674
|
+
);
|
|
7675
|
+
}
|
|
7676
|
+
const remoteRef = `refs/remotes/origin/${checkedBranch}`;
|
|
7677
|
+
const verified = git(repo, ["rev-parse", "--verify", "-q", remoteRef]);
|
|
7678
|
+
if (verified.status !== 0) throw new Error(`git sync succeeded but ${remoteRef} does not exist`);
|
|
7679
|
+
if (helper) {
|
|
7680
|
+
const reportedCommit = synced.stdout.trim();
|
|
7681
|
+
const actualCommit = verified.stdout.trim();
|
|
7682
|
+
if (!/^[0-9a-f]{40,64}$/i.test(reportedCommit))
|
|
7683
|
+
throw new Error("git sync helper did not report the synced commit on stdout");
|
|
7684
|
+
if (reportedCommit !== actualCommit)
|
|
7685
|
+
throw new Error(`git sync helper reported a commit that does not match ${remoteRef}`);
|
|
7686
|
+
}
|
|
7687
|
+
return remoteRef;
|
|
7688
|
+
};
|
|
7689
|
+
var ensureExcluded = (repo, pattern) => {
|
|
7690
|
+
try {
|
|
7691
|
+
const r = spawnSync2("git", ["-C", repo, "rev-parse", "--git-path", "info/exclude"], {
|
|
7692
|
+
stdio: "pipe",
|
|
7693
|
+
encoding: "utf8"
|
|
7694
|
+
});
|
|
7695
|
+
if (r.status !== 0) return;
|
|
7696
|
+
const rel = r.stdout.trim();
|
|
7697
|
+
const path = isAbsolute2(rel) ? rel : resolve(repo, rel);
|
|
7698
|
+
const current = existsSync4(path) ? readFileSync5(path, "utf8") : "";
|
|
7699
|
+
if (current.split("\n").includes(pattern)) return;
|
|
7700
|
+
mkdirSync3(dirname(path), { recursive: true });
|
|
7701
|
+
const sep = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
7702
|
+
appendFileSync(path, `${sep}${pattern}
|
|
7703
|
+
`, "utf8");
|
|
7704
|
+
} catch {
|
|
7705
|
+
}
|
|
7706
|
+
};
|
|
7707
|
+
var removeWorktree = (repo, worktreePath) => {
|
|
7708
|
+
if (!existsSync4(worktreePath)) return;
|
|
7709
|
+
try {
|
|
7710
|
+
execFileSync("git", ["-C", repo, "worktree", "remove", "--force", worktreePath], {
|
|
7711
|
+
stdio: "pipe"
|
|
7712
|
+
});
|
|
7713
|
+
} catch {
|
|
7714
|
+
}
|
|
7715
|
+
};
|
|
7716
|
+
var restoreWorktree = async (repo, worktreePath, sessionCode, baseBranch = repoHeadBranch(repo)) => {
|
|
7717
|
+
spawnSync2("git", ["-C", repo, "worktree", "prune"], { stdio: "pipe" });
|
|
7718
|
+
mkdirSync3(dirname(worktreePath), { recursive: true });
|
|
7719
|
+
const branch = `baton/${sessionCode}`;
|
|
7720
|
+
const reuse = spawnSync2("git", ["-C", repo, "worktree", "add", worktreePath, branch], {
|
|
7721
|
+
stdio: "pipe",
|
|
7722
|
+
encoding: "utf8"
|
|
7723
|
+
});
|
|
7724
|
+
if (reuse.status === 0) return;
|
|
7725
|
+
createWorktree({
|
|
7726
|
+
repo,
|
|
7727
|
+
worktreePath,
|
|
7728
|
+
sessionCode,
|
|
7729
|
+
base: await syncBaseBranch(repo, baseBranch)
|
|
7730
|
+
});
|
|
7731
|
+
};
|
|
7732
|
+
|
|
7589
7733
|
// src/worker/watchdog.ts
|
|
7590
7734
|
var stale = (okAt, now, thresholdMs) => now - okAt > thresholdMs;
|
|
7591
7735
|
var streamWedged = (isOpen, okAt, now, thresholdMs) => !isOpen && stale(okAt, now, thresholdMs);
|
|
@@ -7729,79 +7873,6 @@ var generateTitle = async (input) => {
|
|
|
7729
7873
|
return !title || isDeclined(title) ? { kind: "declined" } : { kind: "titled", title };
|
|
7730
7874
|
};
|
|
7731
7875
|
|
|
7732
|
-
// src/session/worktree.ts
|
|
7733
|
-
import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
7734
|
-
import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
7735
|
-
import { dirname, isAbsolute as isAbsolute2, resolve } from "node:path";
|
|
7736
|
-
var createWorktree = (input) => {
|
|
7737
|
-
if (existsSync4(input.worktreePath))
|
|
7738
|
-
throw new Error(`worktree path already exists: ${input.worktreePath}`);
|
|
7739
|
-
const head = spawnSync2("git", ["-C", input.repo, "rev-parse", "--verify", "-q", "HEAD"], {
|
|
7740
|
-
stdio: "pipe",
|
|
7741
|
-
encoding: "utf8"
|
|
7742
|
-
});
|
|
7743
|
-
if (head.status !== 0)
|
|
7744
|
-
throw new Error(
|
|
7745
|
-
`repo has no commits (unborn HEAD) \u2014 create an initial commit before starting sessions`
|
|
7746
|
-
);
|
|
7747
|
-
mkdirSync3(dirname(input.worktreePath), { recursive: true });
|
|
7748
|
-
const branch = `baton/${input.sessionCode}`;
|
|
7749
|
-
const r = spawnSync2(
|
|
7750
|
-
"git",
|
|
7751
|
-
["-C", input.repo, "worktree", "add", "-b", branch, input.worktreePath, input.base],
|
|
7752
|
-
{ stdio: "pipe", encoding: "utf8" }
|
|
7753
|
-
);
|
|
7754
|
-
if (r.status !== 0) {
|
|
7755
|
-
throw new Error(`git worktree add failed: ${r.stderr || r.stdout || `exit ${r.status}`}`);
|
|
7756
|
-
}
|
|
7757
|
-
};
|
|
7758
|
-
var repoHeadBranch = (repo) => {
|
|
7759
|
-
const r = spawnSync2("git", ["-C", repo, "symbolic-ref", "--short", "HEAD"], {
|
|
7760
|
-
stdio: "pipe",
|
|
7761
|
-
encoding: "utf8"
|
|
7762
|
-
});
|
|
7763
|
-
const branch = r.status === 0 ? r.stdout.trim() : "";
|
|
7764
|
-
return branch || "main";
|
|
7765
|
-
};
|
|
7766
|
-
var ensureExcluded = (repo, pattern) => {
|
|
7767
|
-
try {
|
|
7768
|
-
const r = spawnSync2("git", ["-C", repo, "rev-parse", "--git-path", "info/exclude"], {
|
|
7769
|
-
stdio: "pipe",
|
|
7770
|
-
encoding: "utf8"
|
|
7771
|
-
});
|
|
7772
|
-
if (r.status !== 0) return;
|
|
7773
|
-
const rel = r.stdout.trim();
|
|
7774
|
-
const path = isAbsolute2(rel) ? rel : resolve(repo, rel);
|
|
7775
|
-
const current = existsSync4(path) ? readFileSync5(path, "utf8") : "";
|
|
7776
|
-
if (current.split("\n").includes(pattern)) return;
|
|
7777
|
-
mkdirSync3(dirname(path), { recursive: true });
|
|
7778
|
-
const sep = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
7779
|
-
appendFileSync(path, `${sep}${pattern}
|
|
7780
|
-
`, "utf8");
|
|
7781
|
-
} catch {
|
|
7782
|
-
}
|
|
7783
|
-
};
|
|
7784
|
-
var removeWorktree = (repo, worktreePath) => {
|
|
7785
|
-
if (!existsSync4(worktreePath)) return;
|
|
7786
|
-
try {
|
|
7787
|
-
execFileSync("git", ["-C", repo, "worktree", "remove", "--force", worktreePath], {
|
|
7788
|
-
stdio: "pipe"
|
|
7789
|
-
});
|
|
7790
|
-
} catch {
|
|
7791
|
-
}
|
|
7792
|
-
};
|
|
7793
|
-
var restoreWorktree = (repo, worktreePath, sessionCode) => {
|
|
7794
|
-
spawnSync2("git", ["-C", repo, "worktree", "prune"], { stdio: "pipe" });
|
|
7795
|
-
mkdirSync3(dirname(worktreePath), { recursive: true });
|
|
7796
|
-
const branch = `baton/${sessionCode}`;
|
|
7797
|
-
const reuse = spawnSync2("git", ["-C", repo, "worktree", "add", worktreePath, branch], {
|
|
7798
|
-
stdio: "pipe",
|
|
7799
|
-
encoding: "utf8"
|
|
7800
|
-
});
|
|
7801
|
-
if (reuse.status === 0) return;
|
|
7802
|
-
createWorktree({ repo, worktreePath, sessionCode, base: repoHeadBranch(repo) });
|
|
7803
|
-
};
|
|
7804
|
-
|
|
7805
7876
|
// src/worker/proc.ts
|
|
7806
7877
|
var killProcessGroup = (proc) => {
|
|
7807
7878
|
if (proc.pid === void 0) return;
|
|
@@ -7820,7 +7891,26 @@ var binPath = () => {
|
|
|
7820
7891
|
var createSessionSupervisor = (deps) => {
|
|
7821
7892
|
const { client, cfg, repo, log, hasTerminal, closeTerminal } = deps;
|
|
7822
7893
|
const worktreeDir = defaultWorktreeDir();
|
|
7894
|
+
const baseBranch = cfg.baseBranch ?? repoHeadBranch(repo);
|
|
7895
|
+
const syncBase = deps.syncBase ?? syncBaseBranch;
|
|
7896
|
+
let syncInFlight = null;
|
|
7897
|
+
const syncedBase = () => {
|
|
7898
|
+
if (syncInFlight) return syncInFlight;
|
|
7899
|
+
const pending = syncBase(repo, baseBranch);
|
|
7900
|
+
syncInFlight = pending;
|
|
7901
|
+
const clear = () => {
|
|
7902
|
+
if (syncInFlight === pending) syncInFlight = null;
|
|
7903
|
+
};
|
|
7904
|
+
void pending.then(clear, clear);
|
|
7905
|
+
return pending;
|
|
7906
|
+
};
|
|
7823
7907
|
const children = /* @__PURE__ */ new Map();
|
|
7908
|
+
const starts = /* @__PURE__ */ new Map();
|
|
7909
|
+
const startEpochs = /* @__PURE__ */ new Map();
|
|
7910
|
+
const currentStartEpoch = (sessionId) => startEpochs.get(sessionId) ?? 0;
|
|
7911
|
+
const cancelStart = (sessionId) => {
|
|
7912
|
+
startEpochs.set(sessionId, currentStartEpoch(sessionId) + 1);
|
|
7913
|
+
};
|
|
7824
7914
|
const spawnChild = (sessionId, worktreePath) => {
|
|
7825
7915
|
if (children.has(sessionId)) return;
|
|
7826
7916
|
const child = spawn(process.execPath, [binPath(), "session", "run", String(sessionId)], {
|
|
@@ -7837,26 +7927,33 @@ var createSessionSupervisor = (deps) => {
|
|
|
7837
7927
|
});
|
|
7838
7928
|
});
|
|
7839
7929
|
};
|
|
7840
|
-
const
|
|
7930
|
+
const startOne = async (sessionId, name, epoch) => {
|
|
7931
|
+
const wasCanceled = () => currentStartEpoch(sessionId) !== epoch;
|
|
7932
|
+
if (wasCanceled()) return;
|
|
7841
7933
|
if (hasTerminal(sessionId))
|
|
7842
7934
|
return log(`session #${sessionId} has an open terminal \u2014 skipping headless start`);
|
|
7843
7935
|
if (children.has(sessionId)) return log(`session #${sessionId} already running`);
|
|
7844
7936
|
const session2 = await client.sessions.get(sessionId);
|
|
7937
|
+
if (wasCanceled()) return log(`session #${sessionId} start canceled before materializing`);
|
|
7845
7938
|
let worktreePath = session2.worktreePath;
|
|
7846
7939
|
if (!session2.agentSessionId || !worktreePath) {
|
|
7847
7940
|
const sessionCode = randomUUID();
|
|
7848
7941
|
const agentSessionId = session2.agentKind === "codex" ? `pending:${sessionCode}` : sessionCode;
|
|
7849
7942
|
worktreePath = join7(worktreeDir, slug(`${name}-${sessionCode.slice(0, 8)}`));
|
|
7943
|
+
const base = await syncedBase();
|
|
7944
|
+
if (wasCanceled()) return log(`session #${sessionId} start canceled during git sync`);
|
|
7850
7945
|
createWorktree({
|
|
7851
7946
|
repo,
|
|
7852
7947
|
worktreePath,
|
|
7853
7948
|
sessionCode: sessionCode.slice(0, 8),
|
|
7854
|
-
base
|
|
7949
|
+
base
|
|
7855
7950
|
});
|
|
7856
7951
|
await client.sessions.materialize(sessionId, { agentSessionId, worktreePath }, cfg.apiToken);
|
|
7952
|
+
if (wasCanceled()) return log(`session #${sessionId} start canceled after materializing`);
|
|
7857
7953
|
log(`materialized session #${sessionId} \u2192 ${worktreePath}`);
|
|
7858
7954
|
} else if (!existsSync5(worktreePath)) {
|
|
7859
|
-
restoreWorktree(repo, worktreePath, session2.agentSessionId.slice(0, 8));
|
|
7955
|
+
await restoreWorktree(repo, worktreePath, session2.agentSessionId.slice(0, 8), baseBranch);
|
|
7956
|
+
if (wasCanceled()) return log(`session #${sessionId} start canceled during worktree restore`);
|
|
7860
7957
|
log(`recreated session #${sessionId} worktree (was missing) \u2192 ${worktreePath}`);
|
|
7861
7958
|
}
|
|
7862
7959
|
ensureExcluded(repo, PROJECT_CONFIG_NAME);
|
|
@@ -7865,7 +7962,23 @@ var createSessionSupervisor = (deps) => {
|
|
|
7865
7962
|
return log(`session #${sessionId} terminal opened mid-start \u2014 skipping headless start`);
|
|
7866
7963
|
spawnChild(sessionId, worktreePath);
|
|
7867
7964
|
};
|
|
7965
|
+
const start = (sessionId, name) => {
|
|
7966
|
+
const epoch = currentStartEpoch(sessionId);
|
|
7967
|
+
const existing = starts.get(sessionId);
|
|
7968
|
+
if (existing?.epoch === epoch) return existing.promise;
|
|
7969
|
+
const pending = existing ? existing.promise.then(
|
|
7970
|
+
() => startOne(sessionId, name, epoch),
|
|
7971
|
+
() => startOne(sessionId, name, epoch)
|
|
7972
|
+
) : startOne(sessionId, name, epoch);
|
|
7973
|
+
starts.set(sessionId, { epoch, promise: pending });
|
|
7974
|
+
const clear = () => {
|
|
7975
|
+
if (starts.get(sessionId)?.promise === pending) starts.delete(sessionId);
|
|
7976
|
+
};
|
|
7977
|
+
void pending.then(clear, clear);
|
|
7978
|
+
return pending;
|
|
7979
|
+
};
|
|
7868
7980
|
const stop = (sessionId) => {
|
|
7981
|
+
cancelStart(sessionId);
|
|
7869
7982
|
closeTerminal(sessionId);
|
|
7870
7983
|
const entry = children.get(sessionId);
|
|
7871
7984
|
if (!entry) return;
|
|
@@ -7874,6 +7987,7 @@ var createSessionSupervisor = (deps) => {
|
|
|
7874
7987
|
log(`stopped session #${sessionId}`);
|
|
7875
7988
|
};
|
|
7876
7989
|
const remove = (sessionId, worktreePath) => {
|
|
7990
|
+
cancelStart(sessionId);
|
|
7877
7991
|
closeTerminal(sessionId);
|
|
7878
7992
|
const entry = children.get(sessionId);
|
|
7879
7993
|
if (entry) {
|
|
@@ -8149,6 +8263,11 @@ var parseAgentKind = (value) => {
|
|
|
8149
8263
|
if (raw === "claude-code" || raw === "codex") return raw;
|
|
8150
8264
|
throw new Error(`invalid agent kind "${raw}" (expected claude-code or codex)`);
|
|
8151
8265
|
};
|
|
8266
|
+
var nonEmptyString = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
|
|
8267
|
+
var resolveBaseBranch = (input) => {
|
|
8268
|
+
const branch = nonEmptyString(input.flag) ?? nonEmptyString(input.env) ?? nonEmptyString(input.saved) ?? repoHeadBranch(input.repo ?? process.cwd());
|
|
8269
|
+
return validateBaseBranch(branch);
|
|
8270
|
+
};
|
|
8152
8271
|
var registerWorker = async (client, input, cfgPath = projectConfigPath()) => {
|
|
8153
8272
|
const out = await client.workers.register({
|
|
8154
8273
|
projectId: input.projectId,
|
|
@@ -8157,17 +8276,19 @@ var registerWorker = async (client, input, cfgPath = projectConfigPath()) => {
|
|
|
8157
8276
|
name: input.name,
|
|
8158
8277
|
hostname: input.hostname
|
|
8159
8278
|
});
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
8279
|
+
const existing = loadProjectConfigOrNull(cfgPath) ?? {};
|
|
8280
|
+
saveProjectConfig(cfgPath, {
|
|
8281
|
+
...existing,
|
|
8282
|
+
server: input.server,
|
|
8283
|
+
project: input.projectId,
|
|
8284
|
+
baseBranch: input.baseBranch,
|
|
8285
|
+
worker: {
|
|
8286
|
+
id: out.worker.id,
|
|
8287
|
+
name: out.worker.name,
|
|
8288
|
+
agentKind: out.worker.agentKind,
|
|
8289
|
+
machineId: out.worker.machineId,
|
|
8290
|
+
apiToken: out.apiToken
|
|
8291
|
+
}
|
|
8171
8292
|
});
|
|
8172
8293
|
return { out, configPath: cfgPath };
|
|
8173
8294
|
};
|
|
@@ -8190,6 +8311,10 @@ var worker = defineCommand({
|
|
|
8190
8311
|
type: "string",
|
|
8191
8312
|
description: "agent engine for new sessions: claude-code or codex"
|
|
8192
8313
|
},
|
|
8314
|
+
baseBranch: {
|
|
8315
|
+
type: "string",
|
|
8316
|
+
description: "branch new session worktrees start from (default: current branch, then main)"
|
|
8317
|
+
},
|
|
8193
8318
|
...common
|
|
8194
8319
|
},
|
|
8195
8320
|
run: async ({ args }) => {
|
|
@@ -8200,9 +8325,15 @@ var worker = defineCommand({
|
|
|
8200
8325
|
);
|
|
8201
8326
|
if (!args.json) console.log(`registering against ${server}`);
|
|
8202
8327
|
const cfgPath = configPathFromArgs(args);
|
|
8203
|
-
const
|
|
8328
|
+
const savedConfig = loadProjectConfigOrNull(cfgPath);
|
|
8329
|
+
const fileToken = savedConfig?.worker?.apiToken;
|
|
8204
8330
|
const auth = resolveAuth(process.env, fileToken);
|
|
8205
8331
|
const c = createClient(server, auth && auth !== "cookie" ? auth : void 0);
|
|
8332
|
+
const baseBranch = resolveBaseBranch({
|
|
8333
|
+
flag: args.baseBranch,
|
|
8334
|
+
env: process.env.BATON_BASE_BRANCH,
|
|
8335
|
+
saved: savedConfig?.baseBranch
|
|
8336
|
+
});
|
|
8206
8337
|
const hostname = osHostname();
|
|
8207
8338
|
const machineId = readOrCreateMachineId();
|
|
8208
8339
|
const name = args.name ?? hostname;
|
|
@@ -8215,16 +8346,18 @@ var worker = defineCommand({
|
|
|
8215
8346
|
server,
|
|
8216
8347
|
hostname,
|
|
8217
8348
|
machineId,
|
|
8218
|
-
agentKind
|
|
8349
|
+
agentKind,
|
|
8350
|
+
baseBranch
|
|
8219
8351
|
},
|
|
8220
8352
|
cfgPath
|
|
8221
8353
|
);
|
|
8222
8354
|
if (args.json) {
|
|
8223
|
-
console.log(toJson({ ...out, configPath, machineIdPath: machineIdPath() }));
|
|
8355
|
+
console.log(toJson({ ...out, baseBranch, configPath, machineIdPath: machineIdPath() }));
|
|
8224
8356
|
return;
|
|
8225
8357
|
}
|
|
8226
8358
|
console.log(`worker #${out.worker.id} (${out.worker.name}) \u2014 ${out.outcome}`);
|
|
8227
8359
|
console.log(` agentKind: ${out.worker.agentKind}`);
|
|
8360
|
+
console.log(` baseBranch: ${baseBranch}`);
|
|
8228
8361
|
console.log(` hostname: ${out.worker.hostname}`);
|
|
8229
8362
|
console.log(` machineId: ${out.worker.machineId}`);
|
|
8230
8363
|
console.log(` machineId file: ${machineIdPath()}`);
|
|
@@ -8333,13 +8466,22 @@ var worker = defineCommand({
|
|
|
8333
8466
|
console.log("(no worker registered for this project \u2014 run `baton worker register`)");
|
|
8334
8467
|
return;
|
|
8335
8468
|
}
|
|
8469
|
+
const baseBranch = resolveBaseBranch({ saved: cfg.baseBranch });
|
|
8336
8470
|
if (args.json) {
|
|
8337
|
-
console.log(
|
|
8471
|
+
console.log(
|
|
8472
|
+
toJson({
|
|
8473
|
+
...worker2,
|
|
8474
|
+
server: cfg.server,
|
|
8475
|
+
projectId: cfg.project,
|
|
8476
|
+
baseBranch
|
|
8477
|
+
})
|
|
8478
|
+
);
|
|
8338
8479
|
return;
|
|
8339
8480
|
}
|
|
8340
8481
|
console.log(`worker #${worker2.id} (${worker2.name})`);
|
|
8341
8482
|
console.log(` machineId: ${worker2.machineId}`);
|
|
8342
8483
|
console.log(` agentKind: ${worker2.agentKind ?? "claude-code"}`);
|
|
8484
|
+
console.log(` baseBranch: ${baseBranch}`);
|
|
8343
8485
|
console.log(` server: ${cfg?.server}`);
|
|
8344
8486
|
}
|
|
8345
8487
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lesscap/baton-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -14,28 +14,27 @@
|
|
|
14
14
|
"dist",
|
|
15
15
|
"scripts"
|
|
16
16
|
],
|
|
17
|
-
"scripts": {
|
|
18
|
-
"baton": "tsx src/index.ts",
|
|
19
|
-
"build": "node build.mjs && chmod +x dist/baton.mjs",
|
|
20
|
-
"postinstall": "node scripts/postinstall.mjs",
|
|
21
|
-
"prepublishOnly": "pnpm build",
|
|
22
|
-
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
23
|
-
"test": "tsx --test 'src/**/*.test.ts'"
|
|
24
|
-
},
|
|
25
17
|
"devDependencies": {
|
|
26
|
-
"@baton/shared": "workspace:*",
|
|
27
18
|
"@types/node": "^25.9.1",
|
|
28
19
|
"@types/ws": "^8.18.1",
|
|
29
20
|
"citty": "^0.2.2",
|
|
30
21
|
"esbuild": "^0.28.0",
|
|
31
22
|
"eventsource": "^4.1.0",
|
|
32
23
|
"tsx": "^4.22.4",
|
|
33
|
-
"typescript": "^6.0.3"
|
|
24
|
+
"typescript": "^6.0.3",
|
|
25
|
+
"@baton/shared": "0.0.0"
|
|
34
26
|
},
|
|
35
27
|
"dependencies": {
|
|
36
28
|
"@anthropic-ai/claude-agent-sdk": "^0.3.209",
|
|
37
29
|
"@openai/codex-sdk": "^0.144.4",
|
|
38
30
|
"node-pty": "^1.1.0",
|
|
39
31
|
"ws": "^8.21.0"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"baton": "tsx src/index.ts",
|
|
35
|
+
"build": "node build.mjs && chmod +x dist/baton.mjs",
|
|
36
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
37
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
38
|
+
"test": "tsx --test 'src/**/*.test.ts'"
|
|
40
39
|
}
|
|
41
|
-
}
|
|
40
|
+
}
|