@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.
- package/dist/baton.mjs +123 -89
- 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
|
|
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(
|
|
955
|
+
create: async (workspaceId, title, description, token) => {
|
|
956
|
+
const res = await fetch(u(`/workspaces/${workspaceId}/channels`), {
|
|
958
957
|
method: "POST",
|
|
959
|
-
headers: {
|
|
960
|
-
body: JSON.stringify({
|
|
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)
|
|
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
|
|
966
|
-
const res = await fetch(u(`/channels/${channelId}`)
|
|
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,
|
|
975
|
+
update: async (channelId, patch2) => {
|
|
971
976
|
const res = await fetch(u(`/channels/${channelId}`), {
|
|
972
977
|
method: "PATCH",
|
|
973
|
-
headers:
|
|
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
|
|
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,
|
|
993
|
+
join: async (channelId, name, kind) => {
|
|
992
994
|
const res = await fetch(member(channelId, name), {
|
|
993
995
|
method: "PUT",
|
|
994
|
-
headers:
|
|
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,
|
|
1001
|
-
const res = await fetch(member(channelId, name), { method: "DELETE"
|
|
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
|
|
1005
|
-
const res = await fetch(u(`/channels/${channelId}/members`)
|
|
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,
|
|
1011
|
+
send: async (channelId, msg) => {
|
|
1010
1012
|
const res = await fetch(u(`/channels/${channelId}/messages`), {
|
|
1011
1013
|
method: "POST",
|
|
1012
|
-
headers:
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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.
|
|
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
|
|
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
|
|
1465
|
+
var webLink = (url, channelId) => {
|
|
1473
1466
|
const origin = url.replace(/\/api\/?$/, "");
|
|
1474
|
-
return origin === url ? null : `${origin}/channel/${channelId}
|
|
1467
|
+
return origin === url ? null : `${origin}/channel/${channelId}`;
|
|
1475
1468
|
};
|
|
1476
|
-
var inviteBlock = (url, channelId
|
|
1477
|
-
const web = webLink(url, channelId
|
|
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
|
|
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}
|
|
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: {
|
|
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: {
|
|
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
|
|
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,
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
1703
|
-
|
|
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: {
|
|
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: {
|
|
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.
|
|
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
|
|
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,
|
|
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: {
|
|
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: {
|
|
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,
|
|
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(
|
|
1852
|
-
|
|
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
|
|
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
|
-
|
|
3674
|
+
cfgPath
|
|
3641
3675
|
);
|
|
3642
3676
|
if (args.json) {
|
|
3643
3677
|
console.log(toJson({ ...out, configPath, machineIdPath: machineIdPath() }));
|