@lesscap/baton-cli 0.3.1 → 0.3.2

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/baton.mjs +123 -89
  2. package/package.json +1 -1
package/dist/baton.mjs CHANGED
@@ -949,28 +949,33 @@ function getBaseURL() {
949
949
  // src/client/channel.ts
950
950
  var channelClient = (baseUrl) => {
951
951
  const u = (p) => `${baseUrl}${p}`;
952
- const authed = (token) => ({ authorization: `Bearer ${token}` });
953
- const sendJson = (token) => ({ "content-type": "application/json", ...authed(token) });
952
+ const JSON_CT = { "content-type": "application/json" };
954
953
  const member = (channelId, name) => u(`/channels/${channelId}/members/${encodeURIComponent(name)}`);
955
954
  return {
956
- create: async (title, description) => {
957
- const res = await fetch(u("/channels"), {
955
+ create: async (workspaceId, title, description, token) => {
956
+ const res = await fetch(u(`/workspaces/${workspaceId}/channels`), {
958
957
  method: "POST",
959
- headers: { "content-type": "application/json" },
960
- body: JSON.stringify({ ...title ? { title } : {}, ...description ? { description } : {} })
958
+ headers: { ...JSON_CT, ...token ? { authorization: `Bearer ${token}` } : {} },
959
+ body: JSON.stringify({
960
+ ...title ? { title } : {},
961
+ ...description ? { description } : {}
962
+ })
961
963
  });
962
- if (!res.ok) throw new Error(`POST /channels \u2192 ${res.status}: ${await res.text()}`);
964
+ if (!res.ok)
965
+ throw new Error(
966
+ `POST /workspaces/${workspaceId}/channels \u2192 ${res.status}: ${await res.text()}`
967
+ );
963
968
  return await res.json();
964
969
  },
965
- manifest: async (channelId, token) => {
966
- const res = await fetch(u(`/channels/${channelId}`), { headers: authed(token) });
970
+ manifest: async (channelId) => {
971
+ const res = await fetch(u(`/channels/${channelId}`));
967
972
  if (!res.ok) throw new Error(`GET channel \u2192 ${res.status}: ${await res.text()}`);
968
973
  return await res.json();
969
974
  },
970
- update: async (channelId, token, patch2) => {
975
+ update: async (channelId, patch2) => {
971
976
  const res = await fetch(u(`/channels/${channelId}`), {
972
977
  method: "PATCH",
973
- headers: sendJson(token),
978
+ headers: JSON_CT,
974
979
  body: JSON.stringify(patch2)
975
980
  });
976
981
  if (!res.ok) throw new Error(`PATCH channel \u2192 ${res.status}: ${await res.text()}`);
@@ -981,79 +986,63 @@ var channelClient = (baseUrl) => {
981
986
  if (!res.ok) throw new Error(`GET /channels/help \u2192 ${res.status}: ${await res.text()}`);
982
987
  return await res.text();
983
988
  },
984
- destroy: async (channelId, token) => {
985
- const res = await fetch(u(`/channels/${channelId}`), {
986
- method: "DELETE",
987
- headers: authed(token)
988
- });
989
+ destroy: async (channelId) => {
990
+ const res = await fetch(u(`/channels/${channelId}`), { method: "DELETE" });
989
991
  if (!res.ok) throw new Error(`DELETE channel \u2192 ${res.status}: ${await res.text()}`);
990
992
  },
991
- join: async (channelId, token, name, kind) => {
993
+ join: async (channelId, name, kind) => {
992
994
  const res = await fetch(member(channelId, name), {
993
995
  method: "PUT",
994
- headers: sendJson(token),
996
+ headers: JSON_CT,
995
997
  body: JSON.stringify({ kind: kind ?? "agent" })
996
998
  });
997
999
  if (!res.ok) throw new Error(`join channel \u2192 ${res.status}: ${await res.text()}`);
998
1000
  return (await res.json()).members;
999
1001
  },
1000
- leave: async (channelId, token, name) => {
1001
- const res = await fetch(member(channelId, name), { method: "DELETE", headers: authed(token) });
1002
+ leave: async (channelId, name) => {
1003
+ const res = await fetch(member(channelId, name), { method: "DELETE" });
1002
1004
  if (!res.ok) throw new Error(`leave channel \u2192 ${res.status}: ${await res.text()}`);
1003
1005
  },
1004
- members: async (channelId, token) => {
1005
- const res = await fetch(u(`/channels/${channelId}/members`), { headers: authed(token) });
1006
+ members: async (channelId) => {
1007
+ const res = await fetch(u(`/channels/${channelId}/members`));
1006
1008
  if (!res.ok) throw new Error(`GET members \u2192 ${res.status}: ${await res.text()}`);
1007
1009
  return (await res.json()).members;
1008
1010
  },
1009
- send: async (channelId, token, msg) => {
1011
+ send: async (channelId, msg) => {
1010
1012
  const res = await fetch(u(`/channels/${channelId}/messages`), {
1011
1013
  method: "POST",
1012
- headers: sendJson(token),
1014
+ headers: JSON_CT,
1013
1015
  body: JSON.stringify(msg)
1014
1016
  });
1015
1017
  if (!res.ok) throw new Error(`POST channel message \u2192 ${res.status}: ${await res.text()}`);
1016
1018
  return await res.json();
1017
1019
  },
1018
- uploadAttachment: async (channelId, token, input) => {
1020
+ uploadAttachment: async (channelId, input) => {
1019
1021
  const res = await fetch(
1020
1022
  u(`/channels/${channelId}/attachments?filename=${encodeURIComponent(input.filename)}`),
1021
- {
1022
- method: "POST",
1023
- headers: { "content-type": input.contentType, ...authed(token) },
1024
- body: input.body
1025
- }
1023
+ { method: "POST", headers: { "content-type": input.contentType }, body: input.body }
1026
1024
  );
1027
1025
  if (!res.ok) throw new Error(`upload attachment \u2192 ${res.status}: ${await res.text()}`);
1028
1026
  return await res.json();
1029
1027
  },
1030
- downloadAttachment: async (channelId, token, attId) => {
1031
- const res = await fetch(u(`/channels/${channelId}/attachments/${attId}`), {
1032
- headers: authed(token)
1033
- });
1028
+ downloadAttachment: async (channelId, attId) => {
1029
+ const res = await fetch(u(`/channels/${channelId}/attachments/${attId}`));
1034
1030
  if (!res.ok) throw new Error(`download attachment \u2192 ${res.status}: ${await res.text()}`);
1035
1031
  return res;
1036
1032
  },
1037
- read: async (channelId, token, opts) => {
1033
+ read: async (channelId, opts) => {
1038
1034
  const params = new URLSearchParams({ since: String(opts?.since ?? 0) });
1039
1035
  if (opts?.for) params.set("for", opts.for);
1040
- const res = await fetch(u(`/channels/${channelId}/messages?${params}`), {
1041
- headers: authed(token)
1042
- });
1036
+ const res = await fetch(u(`/channels/${channelId}/messages?${params}`));
1043
1037
  if (!res.ok) throw new Error(`GET messages \u2192 ${res.status}: ${await res.text()}`);
1044
1038
  return (await res.json()).messages;
1045
1039
  },
1046
- listen: (channelId, token, opts) => {
1040
+ listen: (channelId, opts) => {
1047
1041
  let cursor = opts.since ?? 0;
1048
1042
  const params = new URLSearchParams({ since: String(cursor) });
1049
1043
  if (opts.for) params.set("for", opts.for);
1050
1044
  if (opts.as) params.set("as", opts.as);
1051
- const es = new EventSource(u(`/channels/${channelId}/stream?${params}`), {
1052
- fetch: (url, init2) => fetch(url, {
1053
- ...init2,
1054
- headers: { ...init2?.headers, ...authed(token) }
1055
- })
1056
- });
1045
+ const es = new EventSource(u(`/channels/${channelId}/stream?${params}`));
1057
1046
  es.onmessage = (e) => {
1058
1047
  try {
1059
1048
  const m = JSON.parse(e.data);
@@ -1145,7 +1134,7 @@ var fmtTask = (t) => `${t.code} [${t.status}] ${t.title}`;
1145
1134
  var fmtComment = (c) => `${c.workerId !== void 0 ? `worker#${c.workerId}` : "you"} ${c.body}`;
1146
1135
  var fmtSession = (s) => `${s.id} ${s.name} ${s.attached ? "[active]" : "[inactive]"} ${s.worktreePath ?? "(pending)"}`;
1147
1136
  var fmtWorker = (w) => {
1148
- const offline = w.alive === false ? " [offline]" : "";
1137
+ const offline = w.connected === false ? " [offline]" : "";
1149
1138
  return `W-${w.id} ${w.name} ${w.hostname}${offline}`;
1150
1139
  };
1151
1140
  var renderOne = (item, fmt, json) => json ? toJson(item) : fmt(item);
@@ -1428,6 +1417,11 @@ var clientFor = (args) => {
1428
1417
  const auth = resolveAuth(process.env, fileToken);
1429
1418
  return createClient(baseUrl, auth && auth !== "cookie" ? auth : void 0);
1430
1419
  };
1420
+ var resolveBearer = () => {
1421
+ const fileToken = loadProjectConfigOrNull(projectConfigPath())?.worker?.apiToken;
1422
+ const auth = resolveAuth(process.env, fileToken);
1423
+ return auth && auth !== "cookie" ? auth.bearer : void 0;
1424
+ };
1431
1425
  var resolveProjectId = (args) => {
1432
1426
  if (args.project !== void 0 && args.project !== "") return Number(args.project);
1433
1427
  const cfg = loadProjectConfigOrNull(projectConfigPath());
@@ -1453,11 +1447,10 @@ var closeCommand = defineCommand({
1453
1447
  meta: { name: "close", description: "delete a channel (removes the room and all its messages)" },
1454
1448
  args: {
1455
1449
  channel: { type: "positional", required: true, description: "channel id" },
1456
- token: { type: "string", required: true, description: "channel token" },
1457
1450
  ...common
1458
1451
  },
1459
1452
  run: async ({ args }) => {
1460
- await channelClient(resolveBaseUrl(args.url)).destroy(args.channel, args.token);
1453
+ await channelClient(resolveBaseUrl(args.url)).destroy(args.channel);
1461
1454
  console.log(args.json ? toJson({ ok: true, deleted: args.channel }) : `closed ${args.channel}`);
1462
1455
  }
1463
1456
  });
@@ -1469,42 +1462,58 @@ var readStdin = async () => {
1469
1462
  return Buffer.concat(chunks).toString("utf8");
1470
1463
  };
1471
1464
  var renderRoster = (members) => members.length ? members.map((m) => ` ${m.name} (${m.kind})`).join("\n") : " (nobody online)";
1472
- var webLink = (url, channelId, token) => {
1465
+ var webLink = (url, channelId) => {
1473
1466
  const origin = url.replace(/\/api\/?$/, "");
1474
- return origin === url ? null : `${origin}/channel/${channelId}#token=${token}`;
1467
+ return origin === url ? null : `${origin}/channel/${channelId}`;
1475
1468
  };
1476
- var inviteBlock = (url, channelId, token) => {
1477
- const web = webLink(url, channelId, token);
1469
+ var inviteBlock = (url, channelId) => {
1470
+ const web = webLink(url, channelId);
1478
1471
  return [
1479
1472
  "\u2500\u2500 Share this to invite anyone into the room \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1480
1473
  ...web ? [`Humans \u2014 open in a browser: ${web}`, ""] : [],
1481
1474
  "Agents \u2014 two curl steps:",
1482
- ` 1) see the room: curl -sS -H "authorization: Bearer ${token}" "${url}/channels/${channelId}"`,
1475
+ ` 1) see the room: curl -sS "${url}/channels/${channelId}"`,
1483
1476
  ` 2) read protocol: curl -sS "${url}/channels/help" (then join / listen / send, pick your own NAME)`,
1484
1477
  "",
1485
- `connection: url=${url} channel=${channelId} token=${token}`,
1478
+ `connection: url=${url} channel=${channelId}`,
1486
1479
  "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
1487
1480
  ].join("\n");
1488
1481
  };
1489
1482
 
1490
1483
  // src/commands/channel/create.ts
1491
1484
  var createCommand = defineCommand({
1492
- meta: { name: "create", description: "open a channel (chat room) and print an invite" },
1485
+ meta: {
1486
+ name: "create",
1487
+ description: "open a channel (chat room) in a workspace and print an invite"
1488
+ },
1493
1489
  args: {
1490
+ workspace: {
1491
+ type: "string",
1492
+ description: "owning workspace id (or the cwd .baton.json workspace)"
1493
+ },
1494
1494
  name: { type: "string", description: "room title (optional)" },
1495
- desc: { type: "string", description: "room self-description: purpose / topic / how to participate" },
1495
+ desc: {
1496
+ type: "string",
1497
+ description: "room self-description: purpose / topic / how to participate"
1498
+ },
1496
1499
  ...common
1497
1500
  },
1498
1501
  run: async ({ args }) => {
1499
1502
  const url = resolveBaseUrl(args.url);
1500
- const { channelId, token } = await channelClient(url).create(args.name, args.desc);
1503
+ const workspaceId = resolveWorkspaceId(args);
1504
+ const { channelId } = await channelClient(url).create(
1505
+ workspaceId,
1506
+ args.name,
1507
+ args.desc,
1508
+ resolveBearer()
1509
+ );
1501
1510
  if (args.json) {
1502
- console.log(toJson({ channelId, token, url, name: args.name, description: args.desc }));
1511
+ console.log(toJson({ channelId, url, workspaceId, name: args.name, description: args.desc }));
1503
1512
  return;
1504
1513
  }
1505
- console.log(`channel ${channelId} opened
1514
+ console.log(`channel ${channelId} opened in workspace ${workspaceId}
1506
1515
  `);
1507
- console.log(inviteBlock(url, channelId, token));
1516
+ console.log(inviteBlock(url, channelId));
1508
1517
  }
1509
1518
  });
1510
1519
 
@@ -1515,13 +1524,12 @@ var downloadCommand = defineCommand({
1515
1524
  args: {
1516
1525
  channel: { type: "positional", required: true, description: "channel id" },
1517
1526
  att: { type: "positional", required: true, description: "attachment id (from a message ref)" },
1518
- token: { type: "string", required: true, description: "channel token" },
1519
1527
  out: { type: "string", alias: "o", description: "output file path (default: ./<attId>)" },
1520
1528
  ...common
1521
1529
  },
1522
1530
  run: async ({ args }) => {
1523
1531
  const url = resolveBaseUrl(args.url);
1524
- const res = await channelClient(url).downloadAttachment(args.channel, args.token, args.att);
1532
+ const res = await channelClient(url).downloadAttachment(args.channel, args.att);
1525
1533
  const out = args.out ?? args.att;
1526
1534
  writeFileSync2(out, Buffer.from(await res.arrayBuffer()));
1527
1535
  console.log(`saved \u2192 ${out}`);
@@ -1533,12 +1541,11 @@ var aboutCommand = defineCommand({
1533
1541
  meta: { name: "about", description: "show a channel's manifest (description + online roster)" },
1534
1542
  args: {
1535
1543
  channel: { type: "positional", required: true, description: "channel id" },
1536
- token: { type: "string", required: true, description: "channel token" },
1537
1544
  ...common
1538
1545
  },
1539
1546
  run: async ({ args }) => {
1540
1547
  const url = resolveBaseUrl(args.url);
1541
- const m = await channelClient(url).manifest(args.channel, args.token);
1548
+ const m = await channelClient(url).manifest(args.channel);
1542
1549
  if (args.json) {
1543
1550
  console.log(toJson(m));
1544
1551
  return;
@@ -1553,7 +1560,7 @@ protocol: curl -sS "${url}${m.help}"`);
1553
1560
  }
1554
1561
  });
1555
1562
  var helpCommand = defineCommand({
1556
- meta: { name: "help", description: "print the channel protocol doc (no token needed)" },
1563
+ meta: { name: "help", description: "print the channel protocol doc" },
1557
1564
  args: { ...common },
1558
1565
  run: async ({ args }) => {
1559
1566
  console.log(await channelClient(resolveBaseUrl(args.url)).help());
@@ -1563,12 +1570,11 @@ var membersCommand = defineCommand({
1563
1570
  meta: { name: "members", description: "list the channel's online roster" },
1564
1571
  args: {
1565
1572
  channel: { type: "positional", required: true, description: "channel id" },
1566
- token: { type: "string", required: true, description: "channel token" },
1567
1573
  ...common
1568
1574
  },
1569
1575
  run: async ({ args }) => {
1570
1576
  const url = resolveBaseUrl(args.url);
1571
- const members = await channelClient(url).members(args.channel, args.token);
1577
+ const members = await channelClient(url).members(args.channel);
1572
1578
  console.log(args.json ? toJson(members) : renderRoster(members));
1573
1579
  }
1574
1580
  });
@@ -1576,7 +1582,6 @@ var updateCommand = defineCommand({
1576
1582
  meta: { name: "update", description: "update a channel's title / description (topic + rules)" },
1577
1583
  args: {
1578
1584
  channel: { type: "positional", required: true, description: "channel id" },
1579
- token: { type: "string", required: true, description: "channel token" },
1580
1585
  title: { type: "string", description: "new title" },
1581
1586
  desc: { type: "string", description: "new description (purpose / topic / rules)" },
1582
1587
  ...common
@@ -1585,7 +1590,7 @@ var updateCommand = defineCommand({
1585
1590
  if (args.title === void 0 && args.desc === void 0)
1586
1591
  throw new Error("nothing to update: pass --title and/or --desc");
1587
1592
  const url = resolveBaseUrl(args.url);
1588
- const ch = await channelClient(url).update(args.channel, args.token, {
1593
+ const ch = await channelClient(url).update(args.channel, {
1589
1594
  title: args.title,
1590
1595
  description: args.desc
1591
1596
  });
@@ -1672,7 +1677,7 @@ var runListen = async (o) => {
1672
1677
  file
1673
1678
  });
1674
1679
  };
1675
- const close = channelClient(o.url).listen(o.channel, o.token, {
1680
+ const close = channelClient(o.url).listen(o.channel, {
1676
1681
  since: o.since,
1677
1682
  for: o.for,
1678
1683
  as: o.from,
@@ -1699,8 +1704,10 @@ var listenCommand = defineCommand({
1699
1704
  },
1700
1705
  args: {
1701
1706
  channel: { type: "positional", required: true, description: "channel id" },
1702
- token: { type: "string", required: true, description: "channel token" },
1703
- from: { type: "string", description: "your name; your own messages skipped, presence kept fresh" },
1707
+ from: {
1708
+ type: "string",
1709
+ description: "your name; your own messages skipped, presence kept fresh"
1710
+ },
1704
1711
  since: { type: "string", description: "replay messages after this seq (default 0 = full)" },
1705
1712
  for: { type: "string", description: "only broadcasts + messages addressed to this name" },
1706
1713
  ...common
@@ -1709,7 +1716,6 @@ var listenCommand = defineCommand({
1709
1716
  await runListen({
1710
1717
  url: resolveBaseUrl(args.url),
1711
1718
  channel: args.channel,
1712
- token: args.token,
1713
1719
  from: args.from ?? "",
1714
1720
  since: args.since ? Number(args.since) : 0,
1715
1721
  for: args.for
@@ -1719,30 +1725,35 @@ var listenCommand = defineCommand({
1719
1725
 
1720
1726
  // src/commands/channel/join.ts
1721
1727
  var joinCommand = defineCommand({
1722
- meta: { name: "join", description: "join a channel (register presence); --listen to also stream" },
1728
+ meta: {
1729
+ name: "join",
1730
+ description: "join a channel (register presence); --listen to also stream"
1731
+ },
1723
1732
  args: {
1724
1733
  channel: { type: "positional", required: true, description: "channel id" },
1725
- token: { type: "string", required: true, description: "channel token" },
1726
1734
  from: { type: "string", required: true, description: "your participant name" },
1727
1735
  kind: { type: "string", description: "your kind: agent (default) | human" },
1728
- listen: { type: "boolean", description: "after joining, stream messages (background subscriber)" },
1736
+ listen: {
1737
+ type: "boolean",
1738
+ description: "after joining, stream messages (background subscriber)"
1739
+ },
1729
1740
  since: { type: "string", description: "with --listen: replay after this seq (default 0)" },
1730
1741
  ...common
1731
1742
  },
1732
1743
  run: async ({ args }) => {
1733
1744
  const url = resolveBaseUrl(args.url);
1734
1745
  const kind = args.kind === "human" ? "human" : "agent";
1735
- const members = await channelClient(url).join(args.channel, args.token, args.from, kind);
1746
+ const members = await channelClient(url).join(args.channel, args.from, kind);
1736
1747
  if (!args.listen) {
1737
1748
  if (args.json) console.log(toJson({ joined: args.from, members }));
1738
- else console.log(`joined as ${args.from} (${members.length} online)
1749
+ else
1750
+ console.log(`joined as ${args.from} (${members.length} online)
1739
1751
  ${renderRoster(members)}`);
1740
1752
  return;
1741
1753
  }
1742
1754
  await runListen({
1743
1755
  url,
1744
1756
  channel: args.channel,
1745
- token: args.token,
1746
1757
  from: args.from,
1747
1758
  since: args.since ? Number(args.since) : 0
1748
1759
  });
@@ -1760,7 +1771,6 @@ var readCommand = defineCommand({
1760
1771
  meta: { name: "read", description: "poll channel messages once (curl-style, since a cursor)" },
1761
1772
  args: {
1762
1773
  channel: { type: "positional", required: true, description: "channel id" },
1763
- token: { type: "string", required: true, description: "channel token" },
1764
1774
  since: { type: "string", description: "only messages after this seq (default 0)" },
1765
1775
  from: { type: "string", description: "skip messages from this name (no echo)" },
1766
1776
  for: { type: "string", description: "only broadcasts + messages addressed to this name" },
@@ -1768,7 +1778,7 @@ var readCommand = defineCommand({
1768
1778
  },
1769
1779
  run: async ({ args }) => {
1770
1780
  const url = resolveBaseUrl(args.url);
1771
- const msgs = await channelClient(url).read(args.channel, args.token, {
1781
+ const msgs = await channelClient(url).read(args.channel, {
1772
1782
  since: args.since ? Number(args.since) : 0,
1773
1783
  for: args.for
1774
1784
  });
@@ -1824,11 +1834,16 @@ var sendCommand = defineCommand({
1824
1834
  args: {
1825
1835
  channel: { type: "positional", required: true, description: "channel id" },
1826
1836
  text: { type: "positional", required: false, description: "message text (short one-liner)" },
1827
- token: { type: "string", required: true, description: "channel token" },
1828
1837
  from: { type: "string", description: "your participant name (default: peer)" },
1829
- to: { type: "string", description: "direct it at these names (comma-separated); omit = broadcast" },
1838
+ to: {
1839
+ type: "string",
1840
+ description: "direct it at these names (comma-separated); omit = broadcast"
1841
+ },
1830
1842
  file: { type: "string", description: "read message body from this file (for large content)" },
1831
- attach: { type: "string", description: "attach file(s) (comma-separated paths); uploaded + carried on the message" },
1843
+ attach: {
1844
+ type: "string",
1845
+ description: "attach file(s) (comma-separated paths); uploaded + carried on the message"
1846
+ },
1832
1847
  ...common
1833
1848
  },
1834
1849
  run: async ({ args }) => {
@@ -1840,7 +1855,7 @@ var sendCommand = defineCommand({
1840
1855
  const attachments = [];
1841
1856
  for (const path of attachPaths(args.attach)) {
1842
1857
  attachments.push(
1843
- await client.uploadAttachment(args.channel, args.token, {
1858
+ await client.uploadAttachment(args.channel, {
1844
1859
  filename: basename2(path),
1845
1860
  contentType: contentTypeForPath(path),
1846
1861
  body: await openAsBlob2(path, { type: contentTypeForPath(path) })
@@ -1848,8 +1863,10 @@ var sendCommand = defineCommand({
1848
1863
  );
1849
1864
  }
1850
1865
  if (!text && attachments.length === 0)
1851
- throw new Error("nothing to send: provide text, --file <path>, --attach <file>, or pipe stdin");
1852
- const msg = await client.send(args.channel, args.token, {
1866
+ throw new Error(
1867
+ "nothing to send: provide text, --file <path>, --attach <file>, or pipe stdin"
1868
+ );
1869
+ const msg = await client.send(args.channel, {
1853
1870
  from: args.from ?? "peer",
1854
1871
  text: text ?? "",
1855
1872
  to: splitCsv(args.to),
@@ -3361,6 +3378,17 @@ var removeWorktree = (repo, worktreePath) => {
3361
3378
  } catch {
3362
3379
  }
3363
3380
  };
3381
+ var restoreWorktree = (repo, worktreePath, sessionCode) => {
3382
+ spawnSync2("git", ["-C", repo, "worktree", "prune"], { stdio: "pipe" });
3383
+ mkdirSync3(dirname(worktreePath), { recursive: true });
3384
+ const branch = `baton/${sessionCode}`;
3385
+ const reuse = spawnSync2("git", ["-C", repo, "worktree", "add", worktreePath, branch], {
3386
+ stdio: "pipe",
3387
+ encoding: "utf8"
3388
+ });
3389
+ if (reuse.status === 0) return;
3390
+ createWorktree({ repo, worktreePath, sessionCode, base: repoHeadBranch(repo) });
3391
+ };
3364
3392
 
3365
3393
  // src/worker/watchdog.ts
3366
3394
  var stale = (okAt, now, thresholdMs) => now - okAt > thresholdMs;
@@ -3448,6 +3476,9 @@ var runWorkerDaemon = async (cfg, client, signal) => {
3448
3476
  });
3449
3477
  await client.sessions.materialize(sessionId, { agentSessionId, worktreePath }, cfg.apiToken);
3450
3478
  log(`materialized session #${sessionId} \u2192 ${worktreePath}`);
3479
+ } else if (!existsSync5(worktreePath)) {
3480
+ restoreWorktree(repo, worktreePath, session2.agentSessionId.slice(0, 8));
3481
+ log(`recreated session #${sessionId} worktree (was missing) \u2192 ${worktreePath}`);
3451
3482
  }
3452
3483
  ensureExcluded(repo, PROJECT_CONFIG_NAME);
3453
3484
  saveProjectConfig(join7(worktreePath, PROJECT_CONFIG_NAME), worktreeConfig(cfg));
@@ -3624,7 +3655,10 @@ var worker = defineCommand({
3624
3655
  "no baton server \u2014 pass --url <url> (e.g. https://baton.fmap.dev/api), set BATON_URL, or run `baton init`"
3625
3656
  );
3626
3657
  if (!args.json) console.log(`registering against ${server}`);
3627
- const c = createClient(server);
3658
+ const cfgPath = configPathFromArgs(args);
3659
+ const fileToken = loadProjectConfigOrNull(cfgPath)?.worker?.apiToken;
3660
+ const auth = resolveAuth(process.env, fileToken);
3661
+ const c = createClient(server, auth && auth !== "cookie" ? auth : void 0);
3628
3662
  const hostname = osHostname();
3629
3663
  const machineId = readOrCreateMachineId();
3630
3664
  const name = args.name ?? hostname;
@@ -3637,7 +3671,7 @@ var worker = defineCommand({
3637
3671
  hostname,
3638
3672
  machineId
3639
3673
  },
3640
- configPathFromArgs(args)
3674
+ cfgPath
3641
3675
  );
3642
3676
  if (args.json) {
3643
3677
  console.log(toJson({ ...out, configPath, machineIdPath: machineIdPath() }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lesscap/baton-cli",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "baton CLI — register/run a worker, manage projects/requirements/tasks/sessions",