@lesscap/baton-cli 0.9.7 → 0.9.8

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 +1055 -1684
  2. package/package.json +1 -1
package/dist/baton.mjs CHANGED
@@ -5852,11 +5852,6 @@ var clientFor = (args) => {
5852
5852
  const auth = resolveAuth(process.env, fileToken);
5853
5853
  return createClient(baseUrl, auth && auth !== "cookie" ? auth : void 0);
5854
5854
  };
5855
- var resolveBearer = () => {
5856
- const fileToken = loadProjectConfigOrNull(projectConfigPath())?.worker?.apiToken;
5857
- const auth = resolveAuth(process.env, fileToken);
5858
- return auth && auth !== "cookie" ? auth.bearer : void 0;
5859
- };
5860
5855
  var resolveProjectId = (args) => {
5861
5856
  if (args.project !== void 0 && args.project !== "") return Number(args.project);
5862
5857
  if (process.env.BATON_PROJECT_ID !== void 0)
@@ -5877,7 +5872,6 @@ var parseWorkerHandle = (handle) => {
5877
5872
  const m = handle.trim().match(/^(?:[wW]-)?(\d+)$/);
5878
5873
  return m ? Number(m[1]) : null;
5879
5874
  };
5880
- var splitCsv = (s) => s ? s.split(",").map((x) => x.trim()).filter(Boolean) : void 0;
5881
5875
  var parseDuration = (s) => {
5882
5876
  const m = s.trim().match(/^(\d+)\s*([smhd]?)$/);
5883
5877
  if (!m) throw new Error(`bad interval "${s}" \u2014 use e.g. 90s, 30m, 2h, 1d`);
@@ -5959,771 +5953,422 @@ var artifact = defineCommand({
5959
5953
  subCommands: { publish: publishCommand, ls: listCommand, rm: removeCommand }
5960
5954
  });
5961
5955
 
5962
- // ../../node_modules/.pnpm/eventsource-parser@3.1.0/node_modules/eventsource-parser/dist/index.js
5963
- var ParseError = class extends Error {
5964
- constructor(message, options) {
5965
- super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
5966
- }
5956
+ // src/commands/host.ts
5957
+ var findHost = (hosts, handle) => {
5958
+ const host2 = hosts.find((item) => item.name === handle || String(item.id) === handle);
5959
+ if (!host2) throw new Error(`host not found: ${handle}`);
5960
+ return host2;
5967
5961
  };
5968
- var LF = 10;
5969
- var CR = 13;
5970
- var SPACE = 32;
5971
- function noop(_arg) {
5972
- }
5973
- function createParser(config) {
5974
- if (typeof config == "function")
5975
- throw new TypeError(
5976
- "`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?"
5977
- );
5978
- const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config, pendingFragments = [];
5979
- let pendingFragmentsLength = 0, isFirstChunk = true, id, data = "", dataLines = 0, eventType, terminated = false;
5980
- function feed(chunk) {
5981
- if (terminated)
5982
- throw new Error(
5983
- "Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing."
5984
- );
5985
- if (isFirstChunk && (isFirstChunk = false, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {
5986
- const trailing2 = processLines(chunk);
5987
- trailing2 !== "" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();
5988
- return;
5989
- }
5990
- if (chunk.indexOf(`
5991
- `) === -1 && chunk.indexOf("\r") === -1) {
5992
- pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();
5993
- return;
5994
- }
5995
- pendingFragments.push(chunk);
5996
- const input = pendingFragments.join("");
5997
- pendingFragments.length = 0, pendingFragmentsLength = 0;
5998
- const trailing = processLines(input);
5999
- trailing !== "" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();
6000
- }
6001
- function checkBufferSize() {
6002
- maxBufferSize !== void 0 && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = true, pendingFragments.length = 0, pendingFragmentsLength = 0, id = void 0, data = "", dataLines = 0, eventType = void 0, onError(
6003
- new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {
6004
- type: "max-buffer-size-exceeded"
6005
- })
6006
- )));
6007
- }
6008
- function processLines(chunk) {
6009
- let searchIndex = 0;
6010
- if (chunk.indexOf("\r") === -1) {
6011
- let lfIndex = chunk.indexOf(`
6012
- `, searchIndex);
6013
- for (; lfIndex !== -1; ) {
6014
- if (searchIndex === lfIndex) {
6015
- dataLines > 0 && onEvent({ id, event: eventType, data }), id = void 0, data = "", dataLines = 0, eventType = void 0, searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
6016
- `, searchIndex);
6017
- continue;
6018
- }
6019
- const firstCharCode = chunk.charCodeAt(searchIndex);
6020
- if (isDataPrefix(chunk, searchIndex, firstCharCode)) {
6021
- const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5, value = chunk.slice(valueStart, lfIndex);
6022
- if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {
6023
- onEvent({ id, event: eventType, data: value }), id = void 0, data = "", eventType = void 0, searchIndex = lfIndex + 2, lfIndex = chunk.indexOf(`
6024
- `, searchIndex);
6025
- continue;
6026
- }
6027
- data = dataLines === 0 ? value : `${data}
6028
- ${value}`, dataLines++;
6029
- } else isEventPrefix(chunk, searchIndex, firstCharCode) ? eventType = chunk.slice(
6030
- chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,
6031
- lfIndex
6032
- ) || void 0 : parseLine(chunk, searchIndex, lfIndex);
6033
- searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
6034
- `, searchIndex);
5962
+ var adminHost = async (c, handle) => findHost(await c.hosts.list(), handle);
5963
+ var positiveId = (raw) => {
5964
+ const id = Number(raw);
5965
+ if (!Number.isSafeInteger(id) || id <= 0) throw new Error("workspace must be a positive id");
5966
+ return id;
5967
+ };
5968
+ var hostArg = {
5969
+ host: { type: "positional", required: true, description: "host name or id" }
5970
+ };
5971
+ var workspaceArg = {
5972
+ workspace: { type: "string", required: true, description: "workspace id" }
5973
+ };
5974
+ var host = defineCommand({
5975
+ meta: { name: "host", description: "manage machines and which workspaces can use them (admin)" },
5976
+ subCommands: {
5977
+ add: defineCommand({
5978
+ meta: { name: "add", description: "add a host; prints its Provisioner token once" },
5979
+ args: {
5980
+ name: { type: "positional", required: true, description: "host name" },
5981
+ workspace: { type: "string", description: "also assign it to this workspace id" },
5982
+ ...common
5983
+ },
5984
+ run: async ({ args }) => {
5985
+ const c = clientFor(args);
5986
+ const workspaceId = args.workspace === void 0 ? null : positiveId(args.workspace);
5987
+ const credential = await c.hosts.create(args.name);
5988
+ console.log(renderOne(credential, fmtHostCredential, Boolean(args.json)));
5989
+ if (workspaceId !== null) await c.hosts.assign(credential.host.id, workspaceId);
6035
5990
  }
6036
- return chunk.slice(searchIndex);
6037
- }
6038
- for (; searchIndex < chunk.length; ) {
6039
- const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
6040
- `, searchIndex);
6041
- let lineEnd = -1;
6042
- if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1)
6043
- break;
6044
- parseLine(chunk, searchIndex, lineEnd), searchIndex = lineEnd + 1, chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF && searchIndex++;
6045
- }
6046
- return chunk.slice(searchIndex);
6047
- }
6048
- function parseLine(chunk, start, end) {
6049
- if (start === end) {
6050
- dispatchEvent();
6051
- return;
6052
- }
6053
- const firstCharCode = chunk.charCodeAt(start);
6054
- if (isDataPrefix(chunk, start, firstCharCode)) {
6055
- const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5, value2 = chunk.slice(valueStart, end);
6056
- data = dataLines === 0 ? value2 : `${data}
6057
- ${value2}`, dataLines++;
6058
- return;
6059
- }
6060
- if (isEventPrefix(chunk, start, firstCharCode)) {
6061
- eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;
6062
- return;
6063
- }
6064
- if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
6065
- const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
6066
- id = value2.includes("\0") ? void 0 : value2;
6067
- return;
6068
- }
6069
- if (firstCharCode === 58) {
6070
- if (onComment) {
6071
- const line2 = chunk.slice(start, end);
6072
- onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));
5991
+ }),
5992
+ ls: defineCommand({
5993
+ meta: {
5994
+ name: "ls",
5995
+ description: "list hosts, whether they are online, and their workspaces"
5996
+ },
5997
+ args: { ...common },
5998
+ run: async ({ args }) => {
5999
+ console.log(renderList(await clientFor(args).hosts.list(), fmtHost, Boolean(args.json)));
6073
6000
  }
6074
- return;
6075
- }
6076
- const line = chunk.slice(start, end), fieldSeparatorIndex = line.indexOf(":");
6077
- if (fieldSeparatorIndex === -1) {
6078
- processField(line, "", line);
6079
- return;
6080
- }
6081
- const field = line.slice(0, fieldSeparatorIndex), offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
6082
- processField(field, value, line);
6083
- }
6084
- function processField(field, value, line) {
6085
- switch (field) {
6086
- case "event":
6087
- eventType = value || void 0;
6088
- break;
6089
- case "data":
6090
- data = dataLines === 0 ? value : `${data}
6091
- ${value}`, dataLines++;
6092
- break;
6093
- case "id":
6094
- id = value.includes("\0") ? void 0 : value;
6095
- break;
6096
- case "retry":
6097
- /^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
6098
- new ParseError(`Invalid \`retry\` value: "${value}"`, {
6099
- type: "invalid-retry",
6100
- value,
6101
- line
6102
- })
6103
- );
6104
- break;
6105
- default:
6106
- onError(
6107
- new ParseError(
6108
- `Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
6109
- { type: "unknown-field", field, value, line }
6110
- )
6111
- );
6112
- break;
6113
- }
6114
- }
6115
- function dispatchEvent() {
6116
- dataLines > 0 && onEvent({
6117
- id,
6118
- event: eventType,
6119
- data
6120
- }), id = void 0, data = "", dataLines = 0, eventType = void 0;
6121
- }
6122
- function reset(options = {}) {
6123
- if (options.consume && pendingFragments.length > 0) {
6124
- const incompleteLine = pendingFragments.join("");
6125
- parseLine(incompleteLine, 0, incompleteLine.length);
6126
- }
6127
- isFirstChunk = true, id = void 0, data = "", dataLines = 0, eventType = void 0, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = false;
6001
+ }),
6002
+ assign: defineCommand({
6003
+ meta: { name: "assign", description: "let a workspace create Workers on this host" },
6004
+ args: { ...hostArg, ...workspaceArg, ...common },
6005
+ run: async ({ args }) => {
6006
+ const c = clientFor(args);
6007
+ const { id, name } = await adminHost(c, args.host);
6008
+ await c.hosts.assign(id, positiveId(args.workspace));
6009
+ console.log(`assigned ${name} to workspace ${args.workspace}`);
6010
+ }
6011
+ }),
6012
+ unassign: defineCommand({
6013
+ meta: {
6014
+ name: "unassign",
6015
+ description: "stop new Workers on this host for a workspace; existing ones keep running"
6016
+ },
6017
+ args: { ...hostArg, ...workspaceArg, ...common },
6018
+ run: async ({ args }) => {
6019
+ const c = clientFor(args);
6020
+ const { id, name } = await adminHost(c, args.host);
6021
+ await c.hosts.unassign(id, positiveId(args.workspace));
6022
+ console.log(`unassigned ${name} from workspace ${args.workspace}`);
6023
+ }
6024
+ }),
6025
+ rotate: defineCommand({
6026
+ meta: { name: "rotate", description: "replace a host token; the old one stops working" },
6027
+ args: { ...hostArg, ...common },
6028
+ run: async ({ args }) => {
6029
+ const c = clientFor(args);
6030
+ const { id } = await adminHost(c, args.host);
6031
+ console.log(renderOne(await c.hosts.rotate(id), fmtHostCredential, Boolean(args.json)));
6032
+ }
6033
+ })
6128
6034
  }
6129
- return { feed, reset };
6130
- }
6131
- function isDataPrefix(chunk, i, firstCharCode) {
6132
- return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;
6133
- }
6134
- function isEventPrefix(chunk, i, firstCharCode) {
6135
- return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;
6136
- }
6035
+ });
6137
6036
 
6138
- // ../../node_modules/.pnpm/eventsource@4.1.0/node_modules/eventsource/dist/index.js
6139
- var ErrorEvent = class extends Event {
6140
- /**
6141
- * Constructs a new `ErrorEvent` instance. This is typically not called directly,
6142
- * but rather emitted by the `EventSource` object when an error occurs.
6143
- *
6144
- * @param type - The type of the event (should be "error")
6145
- * @param errorEventInitDict - Optional properties to include in the error event
6146
- */
6147
- constructor(type, errorEventInitDict) {
6148
- var _a, _b;
6149
- super(type), this.code = (_a = errorEventInitDict == null ? void 0 : errorEventInitDict.code) != null ? _a : void 0, this.message = (_b = errorEventInitDict == null ? void 0 : errorEventInitDict.message) != null ? _b : void 0;
6037
+ // src/commands/init.ts
6038
+ import { existsSync as existsSync2 } from "node:fs";
6039
+ import * as readline from "node:readline/promises";
6040
+ var promptPick = async (label, items) => {
6041
+ if (items.length === 0) throw new Error(`no ${label}s found on server`);
6042
+ console.log(`${label}s:`);
6043
+ items.forEach((it, i) => {
6044
+ console.log(` [${i + 1}] #${it.id} ${it.name}`);
6045
+ });
6046
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
6047
+ try {
6048
+ const ans = (await rl.question(`pick a ${label} (1-${items.length}): `)).trim();
6049
+ const idx = Number(ans) - 1;
6050
+ const picked = items[idx];
6051
+ if (!picked) throw new Error(`invalid choice: ${ans}`);
6052
+ return picked;
6053
+ } finally {
6054
+ rl.close();
6150
6055
  }
6151
- /**
6152
- * Node.js "hides" the `message` and `code` properties of the `ErrorEvent` instance,
6153
- * when it is `console.log`'ed. This makes it harder to debug errors. To ease debugging,
6154
- * we explicitly include the properties in the `inspect` method.
6155
- *
6156
- * This is automatically called by Node.js when you `console.log` an instance of this class.
6157
- *
6158
- * @param _depth - The current depth
6159
- * @param options - The options passed to `util.inspect`
6160
- * @param inspect - The inspect function to use (prevents having to import it from `util`)
6161
- * @returns A string representation of the error
6162
- */
6163
- [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")](_depth, options, inspect) {
6164
- return inspect(inspectableError(this), options);
6056
+ };
6057
+ var init = defineCommand({
6058
+ meta: {
6059
+ name: "init",
6060
+ description: `write ${PROJECT_CONFIG_NAME} in the current directory (local state, gitignored)`
6061
+ },
6062
+ args: {
6063
+ workspace: { type: "string", description: "workspace id (skip prompt)" },
6064
+ project: { type: "string", description: "project id (skip prompt)" },
6065
+ force: { type: "boolean", description: `overwrite existing ${PROJECT_CONFIG_NAME}` },
6066
+ ...common
6067
+ },
6068
+ run: async ({ args }) => {
6069
+ const server = resolveBaseUrl(args.url);
6070
+ const cfgPath = projectConfigPath();
6071
+ if (existsSync2(cfgPath) && !args.force)
6072
+ throw new Error(`${PROJECT_CONFIG_NAME} already exists. use --force to overwrite.`);
6073
+ const c = createClient(server);
6074
+ let project2;
6075
+ let workspace2;
6076
+ if (args.project) {
6077
+ project2 = await c.projects.get(Number(args.project));
6078
+ workspace2 = await c.workspaces.get(project2.workspaceId);
6079
+ } else {
6080
+ const wsList = await c.workspaces.list();
6081
+ workspace2 = args.workspace ? wsList.find((w) => w.id === Number(args.workspace)) ?? (() => {
6082
+ throw new Error(`workspace ${args.workspace} not found`);
6083
+ })() : await promptPick("workspace", wsList);
6084
+ const projects = await c.projects.listByWorkspace(workspace2.id);
6085
+ project2 = await promptPick("project", projects);
6086
+ }
6087
+ const cfg = {
6088
+ server,
6089
+ workspace: workspace2.id,
6090
+ project: project2.id,
6091
+ name: project2.name
6092
+ };
6093
+ saveProjectConfig(cfgPath, cfg);
6094
+ console.log(`wrote ${cfgPath}`);
6095
+ console.log(` server: ${server}`);
6096
+ console.log(` workspace: #${workspace2.id} ${workspace2.name}`);
6097
+ console.log(` project: #${project2.id} ${project2.name}`);
6165
6098
  }
6166
- /**
6167
- * Deno "hides" the `message` and `code` properties of the `ErrorEvent` instance,
6168
- * when it is `console.log`'ed. This makes it harder to debug errors. To ease debugging,
6169
- * we explicitly include the properties in the `inspect` method.
6170
- *
6171
- * This is automatically called by Deno when you `console.log` an instance of this class.
6172
- *
6173
- * @param inspect - The inspect function to use (prevents having to import it from `util`)
6174
- * @param options - The options passed to `Deno.inspect`
6175
- * @returns A string representation of the error
6176
- */
6177
- [/* @__PURE__ */ Symbol.for("Deno.customInspect")](inspect, options) {
6178
- return inspect(inspectableError(this), options);
6099
+ });
6100
+
6101
+ // src/commands/session/shared.ts
6102
+ var resolveSession = async (client, projectId, handle) => {
6103
+ const asInt = Number(handle);
6104
+ if (Number.isInteger(asInt) && asInt > 0) {
6105
+ const byId = await client.sessions.get(asInt).catch(() => null);
6106
+ if (byId) return { id: byId.id, name: byId.name };
6179
6107
  }
6108
+ const byName = await client.sessions.findByName(projectId, handle);
6109
+ if (byName) return { id: byName.id, name: byName.name };
6110
+ throw new Error(`session "${handle}" not found in project ${projectId}`);
6180
6111
  };
6181
- function syntaxError(message) {
6182
- const DomException = globalThis.DOMException;
6183
- return typeof DomException == "function" ? new DomException(message, "SyntaxError") : new SyntaxError(message);
6184
- }
6185
- function flattenError(err) {
6186
- return err instanceof Error ? "errors" in err && Array.isArray(err.errors) ? err.errors.map(flattenError).join(", ") : "cause" in err && err.cause instanceof Error ? `${err}: ${flattenError(err.cause)}` : err.message : `${err}`;
6187
- }
6188
- function inspectableError(err) {
6189
- return {
6190
- type: err.type,
6191
- message: err.message,
6192
- code: err.code,
6193
- defaultPrevented: err.defaultPrevented,
6194
- cancelable: err.cancelable,
6195
- timeStamp: err.timeStamp
6196
- };
6197
- }
6198
- var __typeError = (msg) => {
6199
- throw TypeError(msg);
6200
- };
6201
- var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
6202
- var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6203
- var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
6204
- var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
6205
- var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
6206
- var _readyState;
6207
- var _url;
6208
- var _redirectUrl;
6209
- var _withCredentials;
6210
- var _fetch;
6211
- var _reconnectInterval;
6212
- var _reconnectTimer;
6213
- var _lastEventId;
6214
- var _controller;
6215
- var _parser;
6216
- var _onError;
6217
- var _onMessage;
6218
- var _onOpen;
6219
- var _EventSource_instances;
6220
- var connect_fn;
6221
- var _onFetchResponse;
6222
- var _onFetchError;
6223
- var getRequestOptions_fn;
6224
- var _onEvent;
6225
- var _onRetryChange;
6226
- var failConnection_fn;
6227
- var scheduleReconnect_fn;
6228
- var _reconnect;
6229
- var EventSource = class extends EventTarget {
6230
- constructor(url, eventSourceInitDict) {
6231
- var _a, _b;
6232
- super(), __privateAdd(this, _EventSource_instances), this.CONNECTING = 0, this.OPEN = 1, this.CLOSED = 2, __privateAdd(this, _readyState), __privateAdd(this, _url), __privateAdd(this, _redirectUrl), __privateAdd(this, _withCredentials), __privateAdd(this, _fetch), __privateAdd(this, _reconnectInterval), __privateAdd(this, _reconnectTimer), __privateAdd(this, _lastEventId, null), __privateAdd(this, _controller), __privateAdd(this, _parser), __privateAdd(this, _onError, null), __privateAdd(this, _onMessage, null), __privateAdd(this, _onOpen, null), __privateAdd(this, _onFetchResponse, async (response) => {
6233
- var _a2;
6234
- __privateGet(this, _parser).reset();
6235
- const { body, redirected, status, headers } = response;
6236
- if (status === 204) {
6237
- __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, "Server sent HTTP 204, not reconnecting", 204), this.close();
6238
- return;
6239
- }
6240
- if (redirected ? __privateSet(this, _redirectUrl, new URL(response.url)) : __privateSet(this, _redirectUrl, void 0), status !== 200) {
6241
- __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, `Non-200 status code (${status})`, status);
6242
- return;
6243
- }
6244
- if (!(headers.get("content-type") || "").startsWith("text/event-stream")) {
6245
- __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, 'Invalid content type, expected "text/event-stream"', status);
6246
- return;
6247
- }
6248
- if (__privateGet(this, _readyState) === this.CLOSED)
6249
- return;
6250
- __privateSet(this, _readyState, this.OPEN);
6251
- const openEvent = new Event("open");
6252
- if ((_a2 = __privateGet(this, _onOpen)) == null || _a2.call(this, openEvent), this.dispatchEvent(openEvent), typeof body != "object" || !body || !("getReader" in body)) {
6253
- __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, "Invalid response body, expected a web ReadableStream", status), this.close();
6254
- return;
6255
- }
6256
- const decoder = new TextDecoder(), reader = body.getReader();
6257
- let open3 = true;
6258
- do {
6259
- const { done, value } = await reader.read();
6260
- value && __privateGet(this, _parser).feed(decoder.decode(value, { stream: !done })), done && (open3 = false, __privateGet(this, _parser).reset(), __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this));
6261
- } while (open3);
6262
- }), __privateAdd(this, _onFetchError, (err) => {
6263
- __privateSet(this, _controller, void 0), !(err.name === "AbortError" || err.type === "aborted") && __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this, flattenError(err));
6264
- }), __privateAdd(this, _onEvent, (event) => {
6265
- typeof event.id == "string" && __privateSet(this, _lastEventId, event.id);
6266
- const messageEvent = new MessageEvent(event.event || "message", {
6267
- data: event.data,
6268
- origin: __privateGet(this, _redirectUrl) ? __privateGet(this, _redirectUrl).origin : __privateGet(this, _url).origin,
6269
- lastEventId: event.id || ""
6270
- });
6271
- __privateGet(this, _onMessage) && (!event.event || event.event === "message") && __privateGet(this, _onMessage).call(this, messageEvent), this.dispatchEvent(messageEvent);
6272
- }), __privateAdd(this, _onRetryChange, (value) => {
6273
- __privateSet(this, _reconnectInterval, value);
6274
- }), __privateAdd(this, _reconnect, () => {
6275
- __privateSet(this, _reconnectTimer, void 0), __privateGet(this, _readyState) === this.CONNECTING && __privateMethod(this, _EventSource_instances, connect_fn).call(this);
6112
+
6113
+ // src/commands/loop/create.ts
6114
+ var loopCreateCommand = defineCommand({
6115
+ meta: { name: "create", description: "create a recurring scheduled message on a session" },
6116
+ args: {
6117
+ session: { type: "positional", required: true, description: "session int id or name" },
6118
+ message: {
6119
+ type: "string",
6120
+ required: true,
6121
+ description: "message sent to the session each beat"
6122
+ },
6123
+ every: {
6124
+ type: "string",
6125
+ required: true,
6126
+ description: "interval: 90s / 30m / 2h / 1d (min 30s)"
6127
+ },
6128
+ name: { type: "string", description: "optional label" },
6129
+ off: { type: "boolean", description: "create paused (disabled)" },
6130
+ project: { type: "string", description: "project id (overrides .baton.json)" },
6131
+ ...common
6132
+ },
6133
+ run: async ({ args }) => {
6134
+ const c = clientFor(args);
6135
+ const session2 = await resolveSession(c, resolveProjectId(args), args.session);
6136
+ const loop2 = await c.loops.create(session2.id, {
6137
+ message: args.message,
6138
+ intervalSec: parseDuration(args.every),
6139
+ name: args.name,
6140
+ enabled: !args.off
6276
6141
  });
6277
- try {
6278
- if (url instanceof URL)
6279
- __privateSet(this, _url, url);
6280
- else if (typeof url == "string")
6281
- __privateSet(this, _url, new URL(url, getBaseURL()));
6282
- else
6283
- throw new Error("Invalid URL");
6284
- } catch {
6285
- throw syntaxError("An invalid or illegal string was specified");
6286
- }
6287
- __privateSet(this, _parser, createParser({
6288
- onEvent: __privateGet(this, _onEvent),
6289
- onRetry: __privateGet(this, _onRetryChange)
6290
- })), __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _reconnectInterval, 3e3), __privateSet(this, _fetch, (_a = eventSourceInitDict == null ? void 0 : eventSourceInitDict.fetch) != null ? _a : globalThis.fetch), __privateSet(this, _withCredentials, (_b = eventSourceInitDict == null ? void 0 : eventSourceInitDict.withCredentials) != null ? _b : false), __privateMethod(this, _EventSource_instances, connect_fn).call(this);
6291
- }
6292
- /**
6293
- * Returns the state of this EventSource object's connection. It can have the values described below.
6294
- *
6295
- * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState)
6296
- *
6297
- * Note: typed as `number` instead of `0 | 1 | 2` for compatibility with the `EventSource` interface,
6298
- * defined in the TypeScript `dom` library.
6299
- *
6300
- * @public
6301
- */
6302
- get readyState() {
6303
- return __privateGet(this, _readyState);
6142
+ console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6304
6143
  }
6305
- /**
6306
- * Returns the URL providing the event stream.
6307
- *
6308
- * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url)
6309
- *
6310
- * @public
6311
- */
6312
- get url() {
6313
- return __privateGet(this, _url).href;
6314
- }
6315
- /**
6316
- * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to "include", and false otherwise.
6317
- *
6318
- * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials)
6319
- */
6320
- get withCredentials() {
6321
- return __privateGet(this, _withCredentials);
6322
- }
6323
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */
6324
- get onerror() {
6325
- return __privateGet(this, _onError);
6326
- }
6327
- set onerror(value) {
6328
- __privateSet(this, _onError, value);
6329
- }
6330
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */
6331
- get onmessage() {
6332
- return __privateGet(this, _onMessage);
6333
- }
6334
- set onmessage(value) {
6335
- __privateSet(this, _onMessage, value);
6336
- }
6337
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */
6338
- get onopen() {
6339
- return __privateGet(this, _onOpen);
6340
- }
6341
- set onopen(value) {
6342
- __privateSet(this, _onOpen, value);
6343
- }
6344
- addEventListener(type, listener, options) {
6345
- const listen = listener;
6346
- super.addEventListener(type, listen, options);
6347
- }
6348
- removeEventListener(type, listener, options) {
6349
- const listen = listener;
6350
- super.removeEventListener(type, listen, options);
6351
- }
6352
- /**
6353
- * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED.
6354
- *
6355
- * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close)
6356
- *
6357
- * @public
6358
- */
6359
- close() {
6360
- __privateGet(this, _reconnectTimer) && clearTimeout(__privateGet(this, _reconnectTimer)), __privateGet(this, _readyState) !== this.CLOSED && (__privateGet(this, _controller) && __privateGet(this, _controller).abort(), __privateSet(this, _readyState, this.CLOSED), __privateSet(this, _controller, void 0));
6361
- }
6362
- };
6363
- _readyState = /* @__PURE__ */ new WeakMap(), _url = /* @__PURE__ */ new WeakMap(), _redirectUrl = /* @__PURE__ */ new WeakMap(), _withCredentials = /* @__PURE__ */ new WeakMap(), _fetch = /* @__PURE__ */ new WeakMap(), _reconnectInterval = /* @__PURE__ */ new WeakMap(), _reconnectTimer = /* @__PURE__ */ new WeakMap(), _lastEventId = /* @__PURE__ */ new WeakMap(), _controller = /* @__PURE__ */ new WeakMap(), _parser = /* @__PURE__ */ new WeakMap(), _onError = /* @__PURE__ */ new WeakMap(), _onMessage = /* @__PURE__ */ new WeakMap(), _onOpen = /* @__PURE__ */ new WeakMap(), _EventSource_instances = /* @__PURE__ */ new WeakSet(), /**
6364
- * Connect to the given URL and start receiving events
6365
- *
6366
- * @internal
6367
- */
6368
- connect_fn = function() {
6369
- __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _controller, new AbortController()), __privateGet(this, _fetch)(__privateGet(this, _url), __privateMethod(this, _EventSource_instances, getRequestOptions_fn).call(this)).then(__privateGet(this, _onFetchResponse)).catch(__privateGet(this, _onFetchError));
6370
- }, _onFetchResponse = /* @__PURE__ */ new WeakMap(), _onFetchError = /* @__PURE__ */ new WeakMap(), /**
6371
- * Get request options for the `fetch()` request
6372
- *
6373
- * @returns The request options
6374
- * @internal
6375
- */
6376
- getRequestOptions_fn = function() {
6377
- var _a;
6378
- const init2 = {
6379
- // [spec] Let `corsAttributeState` be `Anonymous`…
6380
- // [spec] …will have their mode set to "cors"…
6381
- mode: "cors",
6382
- redirect: "follow",
6383
- headers: { Accept: "text/event-stream", ...__privateGet(this, _lastEventId) ? { "Last-Event-ID": __privateGet(this, _lastEventId) } : void 0 },
6384
- cache: "no-store",
6385
- signal: (_a = __privateGet(this, _controller)) == null ? void 0 : _a.signal
6386
- };
6387
- return "window" in globalThis && (init2.credentials = this.withCredentials ? "include" : "same-origin"), init2;
6388
- }, _onEvent = /* @__PURE__ */ new WeakMap(), _onRetryChange = /* @__PURE__ */ new WeakMap(), /**
6389
- * Handles the process referred to in the EventSource specification as "failing a connection".
6390
- *
6391
- * @param error - The error causing the connection to fail
6392
- * @param code - The HTTP status code, if available
6393
- * @internal
6394
- */
6395
- failConnection_fn = function(message, code) {
6396
- var _a;
6397
- __privateGet(this, _readyState) !== this.CLOSED && __privateSet(this, _readyState, this.CLOSED);
6398
- const errorEvent = new ErrorEvent("error", { code, message });
6399
- (_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent);
6400
- }, /**
6401
- * Schedules a reconnection attempt against the EventSource endpoint.
6402
- *
6403
- * @param message - The error causing the connection to fail
6404
- * @param code - The HTTP status code, if available
6405
- * @internal
6406
- */
6407
- scheduleReconnect_fn = function(message, code) {
6408
- var _a;
6409
- if (__privateGet(this, _readyState) === this.CLOSED)
6410
- return;
6411
- __privateSet(this, _readyState, this.CONNECTING);
6412
- const errorEvent = new ErrorEvent("error", { code, message });
6413
- (_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent), __privateSet(this, _reconnectTimer, setTimeout(__privateGet(this, _reconnect), __privateGet(this, _reconnectInterval)));
6414
- }, _reconnect = /* @__PURE__ */ new WeakMap(), /**
6415
- * ReadyState representing an EventSource currently trying to connect
6416
- *
6417
- * @public
6418
- */
6419
- EventSource.CONNECTING = 0, /**
6420
- * ReadyState representing an EventSource connection that is open (eg connected)
6421
- *
6422
- * @public
6423
- */
6424
- EventSource.OPEN = 1, /**
6425
- * ReadyState representing an EventSource connection that is closed (eg disconnected)
6426
- *
6427
- * @public
6428
- */
6429
- EventSource.CLOSED = 2;
6430
- Object.defineProperty(EventSource, /* @__PURE__ */ Symbol.for("eventsource.supports-fetch-override"), {
6431
- value: true,
6432
- writable: false,
6433
- configurable: false,
6434
- enumerable: false
6435
6144
  });
6436
- function getBaseURL() {
6437
- const doc = "document" in globalThis ? globalThis.document : void 0;
6438
- return doc && typeof doc == "object" && "baseURI" in doc && typeof doc.baseURI == "string" ? doc.baseURI : void 0;
6439
- }
6440
-
6441
- // src/client/channel.ts
6442
- var channelClient = (baseUrl) => {
6443
- const u = (p) => `${baseUrl}${p}`;
6444
- const JSON_CT = { "content-type": "application/json" };
6445
- const member = (channelId, name) => u(`/channels/${channelId}/members/${encodeURIComponent(name)}`);
6446
- return {
6447
- create: async (workspaceId, title, description, token) => {
6448
- const res = await fetch(u(`/workspaces/${workspaceId}/channels`), {
6449
- method: "POST",
6450
- headers: { ...JSON_CT, ...token ? { authorization: `Bearer ${token}` } : {} },
6451
- body: JSON.stringify({
6452
- ...title ? { title } : {},
6453
- ...description ? { description } : {}
6454
- })
6455
- });
6456
- if (!res.ok)
6457
- throw new Error(
6458
- `POST /workspaces/${workspaceId}/channels \u2192 ${res.status}: ${await res.text()}`
6459
- );
6460
- return await res.json();
6461
- },
6462
- manifest: async (channelId) => {
6463
- const res = await fetch(u(`/channels/${channelId}`));
6464
- if (!res.ok) throw new Error(`GET channel \u2192 ${res.status}: ${await res.text()}`);
6465
- return await res.json();
6466
- },
6467
- update: async (channelId, patch) => {
6468
- const res = await fetch(u(`/channels/${channelId}`), {
6469
- method: "PATCH",
6470
- headers: JSON_CT,
6471
- body: JSON.stringify(patch)
6472
- });
6473
- if (!res.ok) throw new Error(`PATCH channel \u2192 ${res.status}: ${await res.text()}`);
6474
- return await res.json();
6475
- },
6476
- help: async () => {
6477
- const res = await fetch(u("/channels/help"));
6478
- if (!res.ok) throw new Error(`GET /channels/help \u2192 ${res.status}: ${await res.text()}`);
6479
- return await res.text();
6480
- },
6481
- destroy: async (channelId) => {
6482
- const res = await fetch(u(`/channels/${channelId}`), { method: "DELETE" });
6483
- if (!res.ok) throw new Error(`DELETE channel \u2192 ${res.status}: ${await res.text()}`);
6484
- },
6485
- join: async (channelId, name, kind) => {
6486
- const res = await fetch(member(channelId, name), {
6487
- method: "PUT",
6488
- headers: JSON_CT,
6489
- body: JSON.stringify({ kind: kind ?? "agent" })
6490
- });
6491
- if (!res.ok) throw new Error(`join channel \u2192 ${res.status}: ${await res.text()}`);
6492
- return (await res.json()).members;
6493
- },
6494
- leave: async (channelId, name) => {
6495
- const res = await fetch(member(channelId, name), { method: "DELETE" });
6496
- if (!res.ok) throw new Error(`leave channel \u2192 ${res.status}: ${await res.text()}`);
6497
- },
6498
- members: async (channelId) => {
6499
- const res = await fetch(u(`/channels/${channelId}/members`));
6500
- if (!res.ok) throw new Error(`GET members \u2192 ${res.status}: ${await res.text()}`);
6501
- return (await res.json()).members;
6502
- },
6503
- send: async (channelId, msg) => {
6504
- const res = await fetch(u(`/channels/${channelId}/messages`), {
6505
- method: "POST",
6506
- headers: JSON_CT,
6507
- body: JSON.stringify(msg)
6508
- });
6509
- if (!res.ok) throw new Error(`POST channel message \u2192 ${res.status}: ${await res.text()}`);
6510
- return await res.json();
6511
- },
6512
- uploadAttachment: async (channelId, input) => {
6513
- const res = await fetch(
6514
- u(`/channels/${channelId}/attachments?filename=${encodeURIComponent(input.filename)}`),
6515
- { method: "POST", headers: { "content-type": input.contentType }, body: input.body }
6516
- );
6517
- if (!res.ok) throw new Error(`upload attachment \u2192 ${res.status}: ${await res.text()}`);
6518
- return await res.json();
6519
- },
6520
- downloadAttachment: async (channelId, attId) => {
6521
- const res = await fetch(u(`/channels/${channelId}/attachments/${attId}`));
6522
- if (!res.ok) throw new Error(`download attachment \u2192 ${res.status}: ${await res.text()}`);
6523
- return res;
6524
- },
6525
- read: async (channelId, opts) => {
6526
- const params = new URLSearchParams({ since: String(opts?.since ?? 0) });
6527
- if (opts?.for) params.set("for", opts.for);
6528
- const res = await fetch(u(`/channels/${channelId}/messages?${params}`));
6529
- if (!res.ok) throw new Error(`GET messages \u2192 ${res.status}: ${await res.text()}`);
6530
- return (await res.json()).messages;
6531
- },
6532
- listen: (channelId, opts) => {
6533
- let cursor = opts.since ?? 0;
6534
- const params = new URLSearchParams({ since: String(cursor) });
6535
- if (opts.for) params.set("for", opts.for);
6536
- if (opts.as) params.set("as", opts.as);
6537
- const es = new EventSource(u(`/channels/${channelId}/stream?${params}`));
6538
- es.onmessage = (e) => {
6539
- try {
6540
- const m = JSON.parse(e.data);
6541
- if (m.seq <= cursor) return;
6542
- cursor = m.seq;
6543
- opts.onMessage(m);
6544
- } catch (err) {
6545
- opts.onError?.(err);
6546
- }
6547
- };
6548
- es.onerror = (err) => opts.onError?.(err);
6549
- return () => es.close();
6550
- }
6551
- };
6552
- };
6553
6145
 
6554
- // src/commands/channel/close.ts
6555
- var closeCommand = defineCommand({
6556
- meta: { name: "close", description: "delete a channel (removes the room and all its messages)" },
6146
+ // src/commands/loop/disable.ts
6147
+ var loopDisableCommand = defineCommand({
6148
+ meta: { name: "disable", description: "disable (pause) a loop" },
6557
6149
  args: {
6558
- channel: { type: "positional", required: true, description: "channel id" },
6150
+ loop: { type: "positional", required: true, description: "loop int id" },
6559
6151
  ...common
6560
6152
  },
6561
6153
  run: async ({ args }) => {
6562
- await channelClient(resolveBaseUrl(args.url)).destroy(args.channel);
6563
- console.log(args.json ? toJson({ ok: true, deleted: args.channel }) : `closed ${args.channel}`);
6154
+ const c = clientFor(args);
6155
+ const loop2 = await c.loops.update(Number(args.loop), { enabled: false });
6156
+ console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6564
6157
  }
6565
6158
  });
6566
6159
 
6567
- // src/commands/channel/shared.ts
6568
- var readStdin = async () => {
6569
- const chunks = [];
6570
- for await (const c of process.stdin) chunks.push(c);
6571
- return Buffer.concat(chunks).toString("utf8");
6572
- };
6573
- var renderRoster = (members) => members.length ? members.map((m) => ` ${m.name} (${m.kind})`).join("\n") : " (nobody online)";
6574
- var webLink = (url, channelId) => {
6575
- const origin = url.replace(/\/api\/?$/, "");
6576
- return origin === url ? null : `${origin}/channel/${channelId}`;
6577
- };
6578
- var inviteBlock = (url, channelId) => {
6579
- const web = webLink(url, channelId);
6580
- return [
6581
- "\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",
6582
- ...web ? [`Humans \u2014 open in a browser: ${web}`, ""] : [],
6583
- "Agents \u2014 two curl steps:",
6584
- ` 1) see the room: curl -sS "${url}/channels/${channelId}"`,
6585
- ` 2) read protocol: curl -sS "${url}/channels/help" (then join / listen / send, pick your own NAME)`,
6586
- "",
6587
- `connection: url=${url} channel=${channelId}`,
6588
- "\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"
6589
- ].join("\n");
6590
- };
6591
-
6592
- // src/commands/channel/create.ts
6593
- var createCommand = defineCommand({
6594
- meta: {
6595
- name: "create",
6596
- description: "open a channel (chat room) in a workspace and print an invite"
6597
- },
6160
+ // src/commands/loop/enable.ts
6161
+ var loopEnableCommand = defineCommand({
6162
+ meta: { name: "enable", description: "enable (resume) a loop" },
6598
6163
  args: {
6599
- workspace: {
6600
- type: "string",
6601
- description: "owning workspace id (or the cwd .baton.json workspace)"
6602
- },
6603
- name: { type: "string", description: "room title (optional)" },
6604
- desc: {
6605
- type: "string",
6606
- description: "room self-description: purpose / topic / how to participate"
6607
- },
6164
+ loop: { type: "positional", required: true, description: "loop int id" },
6608
6165
  ...common
6609
6166
  },
6610
6167
  run: async ({ args }) => {
6611
- const url = resolveBaseUrl(args.url);
6612
- const workspaceId = resolveWorkspaceId(args);
6613
- const { channelId } = await channelClient(url).create(
6614
- workspaceId,
6615
- args.name,
6616
- args.desc,
6617
- resolveBearer()
6618
- );
6619
- if (args.json) {
6620
- console.log(toJson({ channelId, url, workspaceId, name: args.name, description: args.desc }));
6621
- return;
6622
- }
6623
- console.log(`channel ${channelId} opened in workspace ${workspaceId}
6624
- `);
6625
- console.log(inviteBlock(url, channelId));
6168
+ const c = clientFor(args);
6169
+ const loop2 = await c.loops.update(Number(args.loop), { enabled: true });
6170
+ console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6626
6171
  }
6627
6172
  });
6628
6173
 
6629
- // src/commands/channel/download.ts
6630
- import { writeFileSync as writeFileSync2 } from "node:fs";
6631
- var downloadCommand = defineCommand({
6632
- meta: { name: "download", description: "download a channel attachment to a file" },
6174
+ // src/commands/loop/get.ts
6175
+ var loopGetCommand = defineCommand({
6176
+ meta: { name: "get", description: "show one loop" },
6633
6177
  args: {
6634
- channel: { type: "positional", required: true, description: "channel id" },
6635
- att: { type: "positional", required: true, description: "attachment id (from a message ref)" },
6636
- out: { type: "string", alias: "o", description: "output file path (default: ./<attId>)" },
6178
+ loop: { type: "positional", required: true, description: "loop int id" },
6637
6179
  ...common
6638
6180
  },
6639
6181
  run: async ({ args }) => {
6640
- const url = resolveBaseUrl(args.url);
6641
- const res = await channelClient(url).downloadAttachment(args.channel, args.att);
6642
- const out = args.out ?? args.att;
6643
- writeFileSync2(out, Buffer.from(await res.arrayBuffer()));
6644
- console.log(`saved \u2192 ${out}`);
6182
+ const c = clientFor(args);
6183
+ const loop2 = await c.loops.get(Number(args.loop));
6184
+ console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6645
6185
  }
6646
6186
  });
6647
6187
 
6648
- // src/commands/channel/info.ts
6649
- var aboutCommand = defineCommand({
6650
- meta: { name: "about", description: "show a channel's manifest (description + online roster)" },
6188
+ // src/commands/loop/ls.ts
6189
+ var loopLsCommand = defineCommand({
6190
+ meta: { name: "ls", description: "list a session's loops" },
6651
6191
  args: {
6652
- channel: { type: "positional", required: true, description: "channel id" },
6192
+ session: { type: "positional", required: true, description: "session int id or name" },
6193
+ project: { type: "string", description: "project id (overrides .baton.json)" },
6653
6194
  ...common
6654
6195
  },
6655
6196
  run: async ({ args }) => {
6656
- const url = resolveBaseUrl(args.url);
6657
- const m = await channelClient(url).manifest(args.channel);
6658
- if (args.json) {
6659
- console.log(toJson(m));
6660
- return;
6661
- }
6662
- console.log(`${m.title ?? "(untitled)"} [${m.id}]`);
6663
- if (m.description) console.log(m.description);
6664
- console.log(`
6665
- online (${m.members.length}):
6666
- ${renderRoster(m.members)}`);
6667
- console.log(`
6668
- protocol: curl -sS "${url}${m.help}"`);
6669
- }
6670
- });
6671
- var helpCommand = defineCommand({
6672
- meta: { name: "help", description: "print the channel protocol doc" },
6673
- args: { ...common },
6674
- run: async ({ args }) => {
6675
- console.log(await channelClient(resolveBaseUrl(args.url)).help());
6197
+ const c = clientFor(args);
6198
+ const session2 = await resolveSession(c, resolveProjectId(args), args.session);
6199
+ const loops = await c.loops.listBySession(session2.id);
6200
+ console.log(renderList(loops, fmtLoop, Boolean(args.json)));
6676
6201
  }
6677
6202
  });
6678
- var membersCommand = defineCommand({
6679
- meta: { name: "members", description: "list the channel's online roster" },
6203
+
6204
+ // src/commands/loop/rm.ts
6205
+ var loopRmCommand = defineCommand({
6206
+ meta: { name: "rm", description: "delete a loop" },
6680
6207
  args: {
6681
- channel: { type: "positional", required: true, description: "channel id" },
6208
+ loop: { type: "positional", required: true, description: "loop int id" },
6682
6209
  ...common
6683
6210
  },
6684
6211
  run: async ({ args }) => {
6685
- const url = resolveBaseUrl(args.url);
6686
- const members = await channelClient(url).members(args.channel);
6687
- console.log(args.json ? toJson(members) : renderRoster(members));
6212
+ const c = clientFor(args);
6213
+ const id = Number(args.loop);
6214
+ await c.loops.remove(id);
6215
+ console.log(removed("loop", id, Boolean(args.json)));
6688
6216
  }
6689
6217
  });
6690
- var updateCommand = defineCommand({
6691
- meta: { name: "update", description: "update a channel's title / description (topic + rules)" },
6218
+
6219
+ // src/commands/loop/set.ts
6220
+ var loopSetCommand = defineCommand({
6221
+ meta: { name: "set", description: "update a loop (message / interval / name)" },
6692
6222
  args: {
6693
- channel: { type: "positional", required: true, description: "channel id" },
6694
- title: { type: "string", description: "new title" },
6695
- desc: { type: "string", description: "new description (purpose / topic / rules)" },
6223
+ loop: { type: "positional", required: true, description: "loop int id" },
6224
+ message: { type: "string", description: "new message" },
6225
+ every: { type: "string", description: "new interval: 90s / 30m / 2h / 1d" },
6226
+ name: { type: "string", description: "new label" },
6696
6227
  ...common
6697
6228
  },
6698
6229
  run: async ({ args }) => {
6699
- if (args.title === void 0 && args.desc === void 0)
6700
- throw new Error("nothing to update: pass --title and/or --desc");
6701
- const url = resolveBaseUrl(args.url);
6702
- const ch = await channelClient(url).update(args.channel, {
6703
- title: args.title,
6704
- description: args.desc
6705
- });
6706
- console.log(args.json ? toJson(ch) : `updated ${ch.id}${ch.title ? ` \u2014 ${ch.title}` : ""}`);
6230
+ const c = clientFor(args);
6231
+ const patch = {};
6232
+ if (args.message !== void 0) patch.message = args.message;
6233
+ if (args.every !== void 0 && args.every !== "") patch.intervalSec = parseDuration(args.every);
6234
+ if (args.name !== void 0) patch.name = args.name;
6235
+ const loop2 = await c.loops.update(Number(args.loop), patch);
6236
+ console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6707
6237
  }
6708
6238
  });
6709
6239
 
6710
- // src/commands/channel/listen.ts
6711
- import { mkdirSync, writeFileSync as writeFileSync3 } from "node:fs";
6712
- import { tmpdir } from "node:os";
6713
- import { join as join2 } from "node:path";
6714
-
6715
- // ../shared/src/domain/attachment.ts
6716
- var labelAttachments = (attachments) => {
6717
- const counters = { image: 0, file: 0 };
6718
- return attachments.map((att) => {
6719
- const kind = att.contentType.startsWith("image/") ? "image" : "file";
6720
- counters[kind] += 1;
6721
- return `${kind}-${counters[kind]}`;
6722
- });
6240
+ // src/commands/loop.ts
6241
+ var loop = defineCommand({
6242
+ meta: { name: "loop", description: "recurring scheduled messages that wake a session" },
6243
+ subCommands: {
6244
+ create: loopCreateCommand,
6245
+ ls: loopLsCommand,
6246
+ get: loopGetCommand,
6247
+ set: loopSetCommand,
6248
+ enable: loopEnableCommand,
6249
+ disable: loopDisableCommand,
6250
+ rm: loopRmCommand
6251
+ }
6252
+ });
6253
+
6254
+ // src/commands/notify.ts
6255
+ var describe = (result) => {
6256
+ if (result.status === "sent") return `notified ${result.devices} device(s)`;
6257
+ if (result.status === "throttled")
6258
+ return `throttled: this session can notify again in ${result.retryAfterSec}s`;
6259
+ return "nobody to notify: no human has messaged this session, or they have not enabled notifications";
6723
6260
  };
6261
+ var notify = defineCommand({
6262
+ meta: {
6263
+ name: "notify",
6264
+ description: "Push a phone notification to the person working with this session. Use it only when a human needs to act now: something is wrong, a decision is needed, or the work is blocked. Do not use it for routine progress, and not to announce that a turn finished (that is already pushed). At most one notification per session every 10 minutes; text is cut at 200 characters."
6265
+ },
6266
+ args: {
6267
+ text: { type: "positional", required: true, description: "one short line for the lock screen" },
6268
+ ...common
6269
+ },
6270
+ run: async ({ args }) => {
6271
+ const raw = process.env.BATON_SESSION_ID;
6272
+ if (raw === void 0)
6273
+ throw new Error("baton notify only works inside a baton session (BATON_SESSION_ID is unset)");
6274
+ const sessionId = Number(raw);
6275
+ if (!Number.isSafeInteger(sessionId) || sessionId <= 0)
6276
+ throw new Error("invalid BATON_SESSION_ID");
6277
+ const result = await clientFor(args).sessions.notify(sessionId, args.text);
6278
+ console.log(args.json ? toJson(result) : describe(result));
6279
+ }
6280
+ });
6281
+
6282
+ // src/commands/project.ts
6283
+ var createProject = (c, input, json) => c.projects.create(input).then((p) => renderOne(p, fmtProject, json));
6284
+ var listProjects = (c, workspaceId, json) => c.projects.listByWorkspace(workspaceId).then((ps) => renderList(ps, fmtProject, json));
6285
+ var getProject = (c, id, json) => c.projects.get(id).then((p) => renderOne(p, fmtProject, json));
6286
+ var updateProject = (c, id, patch, json) => c.projects.update(id, patch).then((p) => renderOne(p, fmtProject, json));
6287
+ var removeProject = (c, id, json) => c.projects.remove(id).then(() => removed("project", id, json));
6288
+ var project = defineCommand({
6289
+ meta: { name: "project", description: "manage projects" },
6290
+ subCommands: {
6291
+ create: defineCommand({
6292
+ meta: { name: "create", description: "create a project" },
6293
+ args: {
6294
+ name: { type: "positional", required: true },
6295
+ workspace: { type: "string", description: "workspace id (overrides .baton.json)" },
6296
+ desc: { type: "string", description: "description" },
6297
+ ...common
6298
+ },
6299
+ run: async ({ args }) => {
6300
+ const out = await createProject(
6301
+ clientFor(args),
6302
+ { workspaceId: resolveWorkspaceId(args), name: args.name, description: args.desc },
6303
+ Boolean(args.json)
6304
+ );
6305
+ console.log(out);
6306
+ }
6307
+ }),
6308
+ ls: defineCommand({
6309
+ meta: { name: "ls", description: "list projects in a workspace" },
6310
+ args: {
6311
+ workspace: { type: "string", description: "workspace id (overrides .baton.json)" },
6312
+ ...common
6313
+ },
6314
+ run: async ({ args }) => {
6315
+ console.log(
6316
+ await listProjects(clientFor(args), resolveWorkspaceId(args), Boolean(args.json))
6317
+ );
6318
+ }
6319
+ }),
6320
+ get: defineCommand({
6321
+ meta: { name: "get", description: "get a project" },
6322
+ args: { id: { type: "positional", required: true }, ...common },
6323
+ run: async ({ args }) => {
6324
+ console.log(await getProject(clientFor(args), Number(args.id), Boolean(args.json)));
6325
+ }
6326
+ }),
6327
+ update: defineCommand({
6328
+ meta: { name: "update", description: "rename a project / set its description" },
6329
+ args: {
6330
+ id: { type: "positional", required: true },
6331
+ name: { type: "string", description: "new name" },
6332
+ desc: { type: "string", description: "new description" },
6333
+ ...common
6334
+ },
6335
+ run: async ({ args }) => {
6336
+ const patch = {
6337
+ ...args.name !== void 0 ? { name: args.name } : {},
6338
+ ...args.desc !== void 0 ? { description: args.desc } : {}
6339
+ };
6340
+ if (Object.keys(patch).length === 0) throw new Error("pass --name and/or --desc");
6341
+ console.log(
6342
+ await updateProject(clientFor(args), Number(args.id), patch, Boolean(args.json))
6343
+ );
6344
+ }
6345
+ }),
6346
+ rm: defineCommand({
6347
+ meta: { name: "rm", description: "delete a project" },
6348
+ args: { id: { type: "positional", required: true }, ...common },
6349
+ run: async ({ args }) => {
6350
+ console.log(await removeProject(clientFor(args), Number(args.id), Boolean(args.json)));
6351
+ }
6352
+ })
6353
+ }
6354
+ });
6724
6355
 
6725
- // ../shared/src/domain/channel.ts
6726
- var isMessageFor = (msg, name) => !msg.to || msg.to.length === 0 || msg.to.includes(name);
6356
+ // src/provisioner/config.ts
6357
+ import { execFile } from "node:child_process";
6358
+ import { constants } from "node:fs";
6359
+ import { access, readFile, stat as stat2 } from "node:fs/promises";
6360
+ import { isAbsolute } from "node:path";
6361
+ import { promisify } from "node:util";
6362
+
6363
+ // ../shared/src/domain/attachment.ts
6364
+ var labelAttachments = (attachments) => {
6365
+ const counters = { image: 0, file: 0 };
6366
+ return attachments.map((att) => {
6367
+ const kind = att.contentType.startsWith("image/") ? "image" : "file";
6368
+ counters[kind] += 1;
6369
+ return `${kind}-${counters[kind]}`;
6370
+ });
6371
+ };
6727
6372
 
6728
6373
  // ../shared/src/domain/file.ts
6729
6374
  var FILE_TEXT_MAX_BYTES = 5 * 1024 * 1024;
@@ -6840,747 +6485,578 @@ var WORKER_TEMPLATES = [
6840
6485
  var workerTemplateAgentKinds = (id) => id === "mini-local" ? AGENT_KINDS : WORKER_TEMPLATES.find((template) => template.id === id)?.agentKinds ?? [];
6841
6486
  var isWorkerTemplateId = (value) => typeof value === "string" && workerTemplateAgentKinds(value).length > 0;
6842
6487
 
6843
- // src/commands/channel/listen.ts
6844
- var PREVIEW = 280;
6845
- var describe = (e) => {
6846
- const ev = e;
6847
- return ev?.code ? `${ev.code}${ev.message ? ` ${ev.message}` : ""}` : String(e);
6488
+ // src/provisioner/config.ts
6489
+ var forbiddenEnv = /* @__PURE__ */ new Set([
6490
+ "BATON_PROVISIONER_TOKEN",
6491
+ "BATON_TOKEN",
6492
+ "BATON_WORKER_TOKEN",
6493
+ "BATON_USER",
6494
+ "BATON_PASS",
6495
+ "HOME",
6496
+ "XDG_DATA_HOME"
6497
+ ]);
6498
+ var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
6499
+ var absolutePath = (value, field) => {
6500
+ if (typeof value !== "string" || !isAbsolute(value) || value.includes("\0"))
6501
+ throw new Error(`${field} must be an absolute path`);
6502
+ return value;
6848
6503
  };
6849
- var runListen = async (o) => {
6850
- const emit = (x) => {
6851
- process.stdout.write(`${JSON.stringify(x)}
6852
- `);
6853
- };
6854
- const spillDir = join2(tmpdir(), "channel", o.channel);
6855
- mkdirSync(spillDir, { recursive: true });
6856
- const surface = (m) => {
6857
- const text = typeof m.text === "string" ? m.text : "";
6858
- const file = join2(spillDir, `${m.seq}.txt`);
6859
- writeFileSync3(file, text);
6860
- const truncated = text.length > PREVIEW;
6861
- emit({
6862
- type: "channel.message",
6863
- seq: m.seq,
6864
- from: m.from,
6865
- senderKind: m.senderKind,
6866
- to: m.to,
6867
- mine: o.from ? isMessageFor(m, o.from) : void 0,
6868
- ts: m.ts,
6869
- chars: text.length,
6870
- truncated,
6871
- preview: truncated ? `${text.slice(0, PREVIEW)} \u2026` : text,
6872
- // BASE-prefixed urls so the subscribing agent can fetch directly.
6873
- attachments: m.attachments?.length ? m.attachments.map((a) => ({
6874
- filename: a.filename,
6875
- contentType: a.contentType,
6876
- size: a.size,
6877
- url: `${o.url}${a.url}`
6878
- })) : void 0,
6879
- file
6880
- });
6881
- };
6882
- const close = channelClient(o.url).listen(o.channel, {
6883
- since: o.since,
6884
- for: o.for,
6885
- as: o.from,
6886
- // Skip our own messages (they echo back over the same channel).
6887
- onMessage: (m) => {
6888
- if (m.from !== o.from) surface(m);
6889
- },
6890
- onError: (e) => emit({ type: "channel.error", error: describe(e) })
6891
- });
6892
- emit({ type: "channel.listening", channel: o.channel, from: o.from, url: o.url });
6893
- await new Promise((resolve4) => {
6894
- const stop = () => {
6895
- close();
6896
- resolve4();
6897
- };
6898
- process.on("SIGINT", stop);
6899
- process.on("SIGTERM", stop);
6900
- });
6504
+ var environment = (value) => {
6505
+ if (!object(value)) throw new Error("workerEnv must be an object");
6506
+ return Object.fromEntries(
6507
+ Object.entries(value).map(([key, entry]) => {
6508
+ if (forbiddenEnv.has(key) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) || typeof entry !== "string" || entry.includes("\0"))
6509
+ throw new Error(`invalid worker environment key: ${key}`);
6510
+ return [key, entry];
6511
+ })
6512
+ );
6901
6513
  };
6902
- var listenCommand = defineCommand({
6903
- meta: {
6904
- name: "listen",
6905
- description: "stream a channel: one JSON line per message (for a background subscriber)"
6906
- },
6907
- args: {
6908
- channel: { type: "positional", required: true, description: "channel id" },
6909
- from: {
6910
- type: "string",
6911
- description: "your name; your own messages skipped, presence kept fresh"
6912
- },
6913
- since: { type: "string", description: "replay messages after this seq (default 0 = full)" },
6914
- for: { type: "string", description: "only broadcasts + messages addressed to this name" },
6915
- ...common
6916
- },
6917
- run: async ({ args }) => {
6918
- await runListen({
6919
- url: resolveBaseUrl(args.url),
6920
- channel: args.channel,
6921
- from: args.from ?? "",
6922
- since: args.since ? Number(args.since) : 0,
6923
- for: args.for
6924
- });
6925
- }
6514
+ var templates = (value) => {
6515
+ if (!object(value)) throw new Error("templates must be an object");
6516
+ return Object.fromEntries(
6517
+ WORKER_TEMPLATES.filter((template) => value[template.id] !== void 0).map((template) => {
6518
+ const raw = value[template.id];
6519
+ if (!object(raw)) throw new Error(`template ${template.id} must be an object`);
6520
+ const workerEnv2 = environment(raw.workerEnv);
6521
+ if (template.id === "mini-codex") {
6522
+ if (raw.pi !== void 0) throw new Error("mini-codex does not support pi");
6523
+ return [template.id, { workerEnv: workerEnv2 }];
6524
+ }
6525
+ const provider = template.id === "mini-glm-codeplan" ? "zai-coding-cn" : "qwen-token-plan-cn";
6526
+ const apiKey = template.id === "mini-glm-codeplan" ? "ZAI_CODING_CN_API_KEY" : "QWEN_TOKEN_PLAN_CN_API_KEY";
6527
+ if (!workerEnv2.ANTHROPIC_BASE_URL || !(workerEnv2.ANTHROPIC_AUTH_TOKEN || workerEnv2.ANTHROPIC_API_KEY) || !workerEnv2[apiKey])
6528
+ throw new Error(`template ${template.id} requires Claude and Pi credentials`);
6529
+ if (!object(raw.pi) || raw.pi.provider !== provider || typeof raw.pi.model !== "string" || !raw.pi.model.trim())
6530
+ throw new Error(`template ${template.id} requires its Pi provider and model`);
6531
+ return [template.id, { workerEnv: workerEnv2, pi: { provider, model: raw.pi.model.trim() } }];
6532
+ })
6533
+ );
6534
+ };
6535
+ var commandEnvironment = (config) => ({
6536
+ ...process.env,
6537
+ PATH: config.workerEnv.PATH ?? process.env.PATH ?? "/usr/bin:/bin"
6926
6538
  });
6927
-
6928
- // src/commands/channel/join.ts
6929
- var joinCommand = defineCommand({
6930
- meta: {
6931
- name: "join",
6932
- description: "join a channel (register presence); --listen to also stream"
6933
- },
6934
- args: {
6935
- channel: { type: "positional", required: true, description: "channel id" },
6936
- from: { type: "string", required: true, description: "your participant name" },
6937
- kind: { type: "string", description: "your kind: agent (default) | human" },
6938
- listen: {
6939
- type: "boolean",
6940
- description: "after joining, stream messages (background subscriber)"
6941
- },
6942
- since: { type: "string", description: "with --listen: replay after this seq (default 0)" },
6943
- ...common
6944
- },
6945
- run: async ({ args }) => {
6946
- const url = resolveBaseUrl(args.url);
6947
- const kind = args.kind === "human" ? "human" : "agent";
6948
- const members = await channelClient(url).join(args.channel, args.from, kind);
6949
- if (!args.listen) {
6950
- if (args.json) console.log(toJson({ joined: args.from, members }));
6951
- else
6952
- console.log(`joined as ${args.from} (${members.length} online)
6953
- ${renderRoster(members)}`);
6954
- return;
6955
- }
6956
- await runListen({
6957
- url,
6958
- channel: args.channel,
6959
- from: args.from,
6960
- since: args.since ? Number(args.since) : 0
6961
- });
6539
+ var loadProvisionerConfig = async (path) => {
6540
+ if (process.platform !== "darwin") throw new Error("provisioner requires macOS");
6541
+ absolutePath(path, "config");
6542
+ if ((await stat2(path)).mode & 63) throw new Error("config must be private: chmod 600 <config>");
6543
+ const text = await readFile(path, "utf8");
6544
+ let raw;
6545
+ try {
6546
+ raw = JSON.parse(text);
6547
+ } catch (error) {
6548
+ if (!(error instanceof SyntaxError)) throw error;
6549
+ throw new Error("invalid provisioner JSON config");
6962
6550
  }
6963
- });
6551
+ if (!object(raw) || typeof raw.server !== "string" || typeof raw.token !== "string")
6552
+ throw new Error("server and token required");
6553
+ let server;
6554
+ try {
6555
+ server = new URL(raw.server);
6556
+ } catch (error) {
6557
+ if (!(error instanceof TypeError)) throw error;
6558
+ throw new Error("server must be a valid HTTP(S) URL");
6559
+ }
6560
+ if (!["http:", "https:"].includes(server.protocol) || server.username || server.password || server.search || server.hash)
6561
+ throw new Error("server must be an HTTP(S) URL without credentials, query, or fragment");
6562
+ if (!raw.token.trim() || /[\r\n]/.test(raw.token))
6563
+ throw new Error("valid provisioner token required");
6564
+ const config = {
6565
+ server: server.toString().replace(/\/$/, ""),
6566
+ token: raw.token.trim(),
6567
+ projectsRoot: absolutePath(raw.projectsRoot, "projectsRoot"),
6568
+ cliPath: absolutePath(raw.cliPath, "cliPath"),
6569
+ nodePath: absolutePath(raw.nodePath ?? process.execPath, "nodePath"),
6570
+ workerEnv: environment(raw.workerEnv),
6571
+ templates: templates(raw.templates)
6572
+ };
6573
+ await access(config.cliPath, constants.R_OK);
6574
+ await access(config.nodePath ?? process.execPath, constants.X_OK);
6575
+ const env = commandEnvironment(config);
6576
+ await promisify(execFile)("git", ["--version"], { env, timeout: 1e4 });
6577
+ await promisify(execFile)("launchctl", ["help"], { env, timeout: 1e4 });
6578
+ await promisify(execFile)("/usr/bin/plutil", ["-help"], { timeout: 1e4 });
6579
+ return config;
6580
+ };
6964
6581
 
6965
- // src/commands/channel/read.ts
6966
- var fmtMessage = (m, base) => {
6967
- const head = `${m.seq} ${m.from} ${m.text}`;
6968
- const atts = (m.attachments ?? []).map((a) => `
6969
- \u{1F4CE} ${a.filename} \u2192 ${base}${a.url}`).join("");
6970
- return head + atts;
6582
+ // ../../node_modules/.pnpm/eventsource-parser@3.1.0/node_modules/eventsource-parser/dist/index.js
6583
+ var ParseError = class extends Error {
6584
+ constructor(message, options) {
6585
+ super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
6586
+ }
6971
6587
  };
6972
- var readCommand = defineCommand({
6973
- meta: { name: "read", description: "poll channel messages once (curl-style, since a cursor)" },
6974
- args: {
6975
- channel: { type: "positional", required: true, description: "channel id" },
6976
- since: { type: "string", description: "only messages after this seq (default 0)" },
6977
- from: { type: "string", description: "skip messages from this name (no echo)" },
6978
- for: { type: "string", description: "only broadcasts + messages addressed to this name" },
6979
- ...common
6980
- },
6981
- run: async ({ args }) => {
6982
- const url = resolveBaseUrl(args.url);
6983
- const msgs = await channelClient(url).read(args.channel, {
6984
- since: args.since ? Number(args.since) : 0,
6985
- for: args.for
6986
- });
6987
- const out = args.from ? msgs.filter((m) => m.from !== args.from) : msgs;
6988
- if (args.json) console.log(toJson(out));
6989
- else console.log(out.length ? out.map((m) => fmtMessage(m, url)).join("\n") : "(none)");
6990
- }
6991
- });
6992
-
6993
- // src/commands/channel/send.ts
6994
- import { openAsBlob as openAsBlob3, readFileSync as readFileSync2 } from "node:fs";
6995
- import { basename as basename3 } from "node:path";
6996
-
6997
- // src/commands/attach.ts
6998
- import { openAsBlob as openAsBlob2 } from "node:fs";
6999
- import { basename as basename2 } from "node:path";
7000
- var attachPaths = (raw) => (raw === void 0 ? [] : Array.isArray(raw) ? raw : [raw]).flatMap((p) => p.split(",")).map((p) => p.trim()).filter(Boolean);
7001
- var uploadAttachments = async (client, sessionId, paths) => {
7002
- const out = [];
7003
- for (const path of paths) {
7004
- const body = await openAsBlob2(path, { type: contentTypeForPath(path) });
7005
- out.push(
7006
- await client.sessions.uploadAttachment(sessionId, {
7007
- filename: basename2(path),
7008
- contentType: contentTypeForPath(path),
7009
- body
7010
- })
6588
+ var LF = 10;
6589
+ var CR = 13;
6590
+ var SPACE = 32;
6591
+ function noop(_arg) {
6592
+ }
6593
+ function createParser(config) {
6594
+ if (typeof config == "function")
6595
+ throw new TypeError(
6596
+ "`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?"
7011
6597
  );
7012
- }
7013
- return out;
7014
- };
7015
-
7016
- // src/commands/channel/send.ts
7017
- var sendCommand = defineCommand({
7018
- meta: { name: "send", description: "post one message to a channel" },
7019
- args: {
7020
- channel: { type: "positional", required: true, description: "channel id" },
7021
- text: { type: "positional", required: false, description: "message text (short one-liner)" },
7022
- from: { type: "string", description: "your participant name (default: peer)" },
7023
- to: {
7024
- type: "string",
7025
- description: "direct it at these names (comma-separated); omit = broadcast"
7026
- },
7027
- file: { type: "string", description: "read message body from this file (for large content)" },
7028
- attach: {
7029
- type: "string",
7030
- description: "attach file(s) (comma-separated paths); uploaded + carried on the message"
7031
- },
7032
- ...common
7033
- },
7034
- run: async ({ args }) => {
7035
- const url = resolveBaseUrl(args.url);
7036
- const client = channelClient(url);
7037
- let text = args.text;
7038
- if (text === void 0 && args.file) text = readFileSync2(args.file, "utf8");
7039
- if (text === void 0 && !process.stdin.isTTY) text = await readStdin();
7040
- const attachments = [];
7041
- for (const path of attachPaths(args.attach)) {
7042
- attachments.push(
7043
- await client.uploadAttachment(args.channel, {
7044
- filename: basename3(path),
7045
- contentType: contentTypeForPath(path),
7046
- body: await openAsBlob3(path, { type: contentTypeForPath(path) })
7047
- })
7048
- );
7049
- }
7050
- if (!text && attachments.length === 0)
6598
+ const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config, pendingFragments = [];
6599
+ let pendingFragmentsLength = 0, isFirstChunk = true, id, data = "", dataLines = 0, eventType, terminated = false;
6600
+ function feed(chunk) {
6601
+ if (terminated)
7051
6602
  throw new Error(
7052
- "nothing to send: provide text, --file <path>, --attach <file>, or pipe stdin"
6603
+ "Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing."
7053
6604
  );
7054
- const msg = await client.send(args.channel, {
7055
- from: args.from ?? "peer",
7056
- text: text ?? "",
7057
- to: splitCsv(args.to),
7058
- attachments: attachments.length ? attachments : void 0
7059
- });
7060
- if (args.json) console.log(toJson(msg));
7061
- else console.log(`sent (seq ${msg.seq})`);
6605
+ if (isFirstChunk && (isFirstChunk = false, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {
6606
+ const trailing2 = processLines(chunk);
6607
+ trailing2 !== "" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();
6608
+ return;
6609
+ }
6610
+ if (chunk.indexOf(`
6611
+ `) === -1 && chunk.indexOf("\r") === -1) {
6612
+ pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();
6613
+ return;
6614
+ }
6615
+ pendingFragments.push(chunk);
6616
+ const input = pendingFragments.join("");
6617
+ pendingFragments.length = 0, pendingFragmentsLength = 0;
6618
+ const trailing = processLines(input);
6619
+ trailing !== "" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();
7062
6620
  }
7063
- });
7064
-
7065
- // src/commands/channel.ts
7066
- var channel = defineCommand({
7067
- meta: {
7068
- name: "channel",
7069
- description: "multi-agent chat room: create / about / update / help / join / members / send / read / listen / download / close"
7070
- },
7071
- subCommands: {
7072
- create: createCommand,
7073
- about: aboutCommand,
7074
- update: updateCommand,
7075
- help: helpCommand,
7076
- join: joinCommand,
7077
- members: membersCommand,
7078
- send: sendCommand,
7079
- read: readCommand,
7080
- listen: listenCommand,
7081
- download: downloadCommand,
7082
- close: closeCommand
6621
+ function checkBufferSize() {
6622
+ maxBufferSize !== void 0 && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = true, pendingFragments.length = 0, pendingFragmentsLength = 0, id = void 0, data = "", dataLines = 0, eventType = void 0, onError(
6623
+ new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {
6624
+ type: "max-buffer-size-exceeded"
6625
+ })
6626
+ )));
7083
6627
  }
7084
- });
7085
-
7086
- // src/commands/host.ts
7087
- var findHost = (hosts, handle) => {
7088
- const host2 = hosts.find((item) => item.name === handle || String(item.id) === handle);
7089
- if (!host2) throw new Error(`host not found: ${handle}`);
7090
- return host2;
7091
- };
7092
- var adminHost = async (c, handle) => findHost(await c.hosts.list(), handle);
7093
- var positiveId = (raw) => {
7094
- const id = Number(raw);
7095
- if (!Number.isSafeInteger(id) || id <= 0) throw new Error("workspace must be a positive id");
7096
- return id;
7097
- };
7098
- var hostArg = {
7099
- host: { type: "positional", required: true, description: "host name or id" }
7100
- };
7101
- var workspaceArg = {
7102
- workspace: { type: "string", required: true, description: "workspace id" }
7103
- };
7104
- var host = defineCommand({
7105
- meta: { name: "host", description: "manage machines and which workspaces can use them (admin)" },
7106
- subCommands: {
7107
- add: defineCommand({
7108
- meta: { name: "add", description: "add a host; prints its Provisioner token once" },
7109
- args: {
7110
- name: { type: "positional", required: true, description: "host name" },
7111
- workspace: { type: "string", description: "also assign it to this workspace id" },
7112
- ...common
7113
- },
7114
- run: async ({ args }) => {
7115
- const c = clientFor(args);
7116
- const workspaceId = args.workspace === void 0 ? null : positiveId(args.workspace);
7117
- const credential = await c.hosts.create(args.name);
7118
- console.log(renderOne(credential, fmtHostCredential, Boolean(args.json)));
7119
- if (workspaceId !== null) await c.hosts.assign(credential.host.id, workspaceId);
7120
- }
7121
- }),
7122
- ls: defineCommand({
7123
- meta: {
7124
- name: "ls",
7125
- description: "list hosts, whether they are online, and their workspaces"
7126
- },
7127
- args: { ...common },
7128
- run: async ({ args }) => {
7129
- console.log(renderList(await clientFor(args).hosts.list(), fmtHost, Boolean(args.json)));
7130
- }
7131
- }),
7132
- assign: defineCommand({
7133
- meta: { name: "assign", description: "let a workspace create Workers on this host" },
7134
- args: { ...hostArg, ...workspaceArg, ...common },
7135
- run: async ({ args }) => {
7136
- const c = clientFor(args);
7137
- const { id, name } = await adminHost(c, args.host);
7138
- await c.hosts.assign(id, positiveId(args.workspace));
7139
- console.log(`assigned ${name} to workspace ${args.workspace}`);
7140
- }
7141
- }),
7142
- unassign: defineCommand({
7143
- meta: {
7144
- name: "unassign",
7145
- description: "stop new Workers on this host for a workspace; existing ones keep running"
7146
- },
7147
- args: { ...hostArg, ...workspaceArg, ...common },
7148
- run: async ({ args }) => {
7149
- const c = clientFor(args);
7150
- const { id, name } = await adminHost(c, args.host);
7151
- await c.hosts.unassign(id, positiveId(args.workspace));
7152
- console.log(`unassigned ${name} from workspace ${args.workspace}`);
7153
- }
7154
- }),
7155
- rotate: defineCommand({
7156
- meta: { name: "rotate", description: "replace a host token; the old one stops working" },
7157
- args: { ...hostArg, ...common },
7158
- run: async ({ args }) => {
7159
- const c = clientFor(args);
7160
- const { id } = await adminHost(c, args.host);
7161
- console.log(renderOne(await c.hosts.rotate(id), fmtHostCredential, Boolean(args.json)));
6628
+ function processLines(chunk) {
6629
+ let searchIndex = 0;
6630
+ if (chunk.indexOf("\r") === -1) {
6631
+ let lfIndex = chunk.indexOf(`
6632
+ `, searchIndex);
6633
+ for (; lfIndex !== -1; ) {
6634
+ if (searchIndex === lfIndex) {
6635
+ dataLines > 0 && onEvent({ id, event: eventType, data }), id = void 0, data = "", dataLines = 0, eventType = void 0, searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
6636
+ `, searchIndex);
6637
+ continue;
6638
+ }
6639
+ const firstCharCode = chunk.charCodeAt(searchIndex);
6640
+ if (isDataPrefix(chunk, searchIndex, firstCharCode)) {
6641
+ const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5, value = chunk.slice(valueStart, lfIndex);
6642
+ if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {
6643
+ onEvent({ id, event: eventType, data: value }), id = void 0, data = "", eventType = void 0, searchIndex = lfIndex + 2, lfIndex = chunk.indexOf(`
6644
+ `, searchIndex);
6645
+ continue;
6646
+ }
6647
+ data = dataLines === 0 ? value : `${data}
6648
+ ${value}`, dataLines++;
6649
+ } else isEventPrefix(chunk, searchIndex, firstCharCode) ? eventType = chunk.slice(
6650
+ chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,
6651
+ lfIndex
6652
+ ) || void 0 : parseLine(chunk, searchIndex, lfIndex);
6653
+ searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
6654
+ `, searchIndex);
7162
6655
  }
7163
- })
7164
- }
7165
- });
7166
-
7167
- // src/commands/init.ts
7168
- import { existsSync as existsSync2 } from "node:fs";
7169
- import * as readline from "node:readline/promises";
7170
- var promptPick = async (label, items) => {
7171
- if (items.length === 0) throw new Error(`no ${label}s found on server`);
7172
- console.log(`${label}s:`);
7173
- items.forEach((it, i) => {
7174
- console.log(` [${i + 1}] #${it.id} ${it.name}`);
7175
- });
7176
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
7177
- try {
7178
- const ans = (await rl.question(`pick a ${label} (1-${items.length}): `)).trim();
7179
- const idx = Number(ans) - 1;
7180
- const picked = items[idx];
7181
- if (!picked) throw new Error(`invalid choice: ${ans}`);
7182
- return picked;
7183
- } finally {
7184
- rl.close();
6656
+ return chunk.slice(searchIndex);
6657
+ }
6658
+ for (; searchIndex < chunk.length; ) {
6659
+ const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
6660
+ `, searchIndex);
6661
+ let lineEnd = -1;
6662
+ if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1)
6663
+ break;
6664
+ parseLine(chunk, searchIndex, lineEnd), searchIndex = lineEnd + 1, chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF && searchIndex++;
6665
+ }
6666
+ return chunk.slice(searchIndex);
7185
6667
  }
7186
- };
7187
- var init = defineCommand({
7188
- meta: {
7189
- name: "init",
7190
- description: `write ${PROJECT_CONFIG_NAME} in the current directory (local state, gitignored)`
7191
- },
7192
- args: {
7193
- workspace: { type: "string", description: "workspace id (skip prompt)" },
7194
- project: { type: "string", description: "project id (skip prompt)" },
7195
- force: { type: "boolean", description: `overwrite existing ${PROJECT_CONFIG_NAME}` },
7196
- ...common
7197
- },
7198
- run: async ({ args }) => {
7199
- const server = resolveBaseUrl(args.url);
7200
- const cfgPath = projectConfigPath();
7201
- if (existsSync2(cfgPath) && !args.force)
7202
- throw new Error(`${PROJECT_CONFIG_NAME} already exists. use --force to overwrite.`);
7203
- const c = createClient(server);
7204
- let project2;
7205
- let workspace2;
7206
- if (args.project) {
7207
- project2 = await c.projects.get(Number(args.project));
7208
- workspace2 = await c.workspaces.get(project2.workspaceId);
7209
- } else {
7210
- const wsList = await c.workspaces.list();
7211
- workspace2 = args.workspace ? wsList.find((w) => w.id === Number(args.workspace)) ?? (() => {
7212
- throw new Error(`workspace ${args.workspace} not found`);
7213
- })() : await promptPick("workspace", wsList);
7214
- const projects = await c.projects.listByWorkspace(workspace2.id);
7215
- project2 = await promptPick("project", projects);
6668
+ function parseLine(chunk, start, end) {
6669
+ if (start === end) {
6670
+ dispatchEvent();
6671
+ return;
7216
6672
  }
7217
- const cfg = {
7218
- server,
7219
- workspace: workspace2.id,
7220
- project: project2.id,
7221
- name: project2.name
7222
- };
7223
- saveProjectConfig(cfgPath, cfg);
7224
- console.log(`wrote ${cfgPath}`);
7225
- console.log(` server: ${server}`);
7226
- console.log(` workspace: #${workspace2.id} ${workspace2.name}`);
7227
- console.log(` project: #${project2.id} ${project2.name}`);
7228
- }
7229
- });
7230
-
7231
- // src/commands/session/shared.ts
7232
- var resolveSession = async (client, projectId, handle) => {
7233
- const asInt = Number(handle);
7234
- if (Number.isInteger(asInt) && asInt > 0) {
7235
- const byId = await client.sessions.get(asInt).catch(() => null);
7236
- if (byId) return { id: byId.id, name: byId.name };
6673
+ const firstCharCode = chunk.charCodeAt(start);
6674
+ if (isDataPrefix(chunk, start, firstCharCode)) {
6675
+ const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5, value2 = chunk.slice(valueStart, end);
6676
+ data = dataLines === 0 ? value2 : `${data}
6677
+ ${value2}`, dataLines++;
6678
+ return;
6679
+ }
6680
+ if (isEventPrefix(chunk, start, firstCharCode)) {
6681
+ eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;
6682
+ return;
6683
+ }
6684
+ if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
6685
+ const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
6686
+ id = value2.includes("\0") ? void 0 : value2;
6687
+ return;
6688
+ }
6689
+ if (firstCharCode === 58) {
6690
+ if (onComment) {
6691
+ const line2 = chunk.slice(start, end);
6692
+ onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));
6693
+ }
6694
+ return;
6695
+ }
6696
+ const line = chunk.slice(start, end), fieldSeparatorIndex = line.indexOf(":");
6697
+ if (fieldSeparatorIndex === -1) {
6698
+ processField(line, "", line);
6699
+ return;
6700
+ }
6701
+ const field = line.slice(0, fieldSeparatorIndex), offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
6702
+ processField(field, value, line);
7237
6703
  }
7238
- const byName = await client.sessions.findByName(projectId, handle);
7239
- if (byName) return { id: byName.id, name: byName.name };
7240
- throw new Error(`session "${handle}" not found in project ${projectId}`);
7241
- };
7242
-
7243
- // src/commands/loop/create.ts
7244
- var loopCreateCommand = defineCommand({
7245
- meta: { name: "create", description: "create a recurring scheduled message on a session" },
7246
- args: {
7247
- session: { type: "positional", required: true, description: "session int id or name" },
7248
- message: {
7249
- type: "string",
7250
- required: true,
7251
- description: "message sent to the session each beat"
7252
- },
7253
- every: {
7254
- type: "string",
7255
- required: true,
7256
- description: "interval: 90s / 30m / 2h / 1d (min 30s)"
7257
- },
7258
- name: { type: "string", description: "optional label" },
7259
- off: { type: "boolean", description: "create paused (disabled)" },
7260
- project: { type: "string", description: "project id (overrides .baton.json)" },
7261
- ...common
7262
- },
7263
- run: async ({ args }) => {
7264
- const c = clientFor(args);
7265
- const session2 = await resolveSession(c, resolveProjectId(args), args.session);
7266
- const loop2 = await c.loops.create(session2.id, {
7267
- message: args.message,
7268
- intervalSec: parseDuration(args.every),
7269
- name: args.name,
7270
- enabled: !args.off
7271
- });
7272
- console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6704
+ function processField(field, value, line) {
6705
+ switch (field) {
6706
+ case "event":
6707
+ eventType = value || void 0;
6708
+ break;
6709
+ case "data":
6710
+ data = dataLines === 0 ? value : `${data}
6711
+ ${value}`, dataLines++;
6712
+ break;
6713
+ case "id":
6714
+ id = value.includes("\0") ? void 0 : value;
6715
+ break;
6716
+ case "retry":
6717
+ /^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
6718
+ new ParseError(`Invalid \`retry\` value: "${value}"`, {
6719
+ type: "invalid-retry",
6720
+ value,
6721
+ line
6722
+ })
6723
+ );
6724
+ break;
6725
+ default:
6726
+ onError(
6727
+ new ParseError(
6728
+ `Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
6729
+ { type: "unknown-field", field, value, line }
6730
+ )
6731
+ );
6732
+ break;
6733
+ }
7273
6734
  }
7274
- });
7275
-
7276
- // src/commands/loop/disable.ts
7277
- var loopDisableCommand = defineCommand({
7278
- meta: { name: "disable", description: "disable (pause) a loop" },
7279
- args: {
7280
- loop: { type: "positional", required: true, description: "loop int id" },
7281
- ...common
7282
- },
7283
- run: async ({ args }) => {
7284
- const c = clientFor(args);
7285
- const loop2 = await c.loops.update(Number(args.loop), { enabled: false });
7286
- console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6735
+ function dispatchEvent() {
6736
+ dataLines > 0 && onEvent({
6737
+ id,
6738
+ event: eventType,
6739
+ data
6740
+ }), id = void 0, data = "", dataLines = 0, eventType = void 0;
7287
6741
  }
7288
- });
7289
-
7290
- // src/commands/loop/enable.ts
7291
- var loopEnableCommand = defineCommand({
7292
- meta: { name: "enable", description: "enable (resume) a loop" },
7293
- args: {
7294
- loop: { type: "positional", required: true, description: "loop int id" },
7295
- ...common
7296
- },
7297
- run: async ({ args }) => {
7298
- const c = clientFor(args);
7299
- const loop2 = await c.loops.update(Number(args.loop), { enabled: true });
7300
- console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6742
+ function reset(options = {}) {
6743
+ if (options.consume && pendingFragments.length > 0) {
6744
+ const incompleteLine = pendingFragments.join("");
6745
+ parseLine(incompleteLine, 0, incompleteLine.length);
6746
+ }
6747
+ isFirstChunk = true, id = void 0, data = "", dataLines = 0, eventType = void 0, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = false;
7301
6748
  }
7302
- });
6749
+ return { feed, reset };
6750
+ }
6751
+ function isDataPrefix(chunk, i, firstCharCode) {
6752
+ return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;
6753
+ }
6754
+ function isEventPrefix(chunk, i, firstCharCode) {
6755
+ return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;
6756
+ }
7303
6757
 
7304
- // src/commands/loop/get.ts
7305
- var loopGetCommand = defineCommand({
7306
- meta: { name: "get", description: "show one loop" },
7307
- args: {
7308
- loop: { type: "positional", required: true, description: "loop int id" },
7309
- ...common
7310
- },
7311
- run: async ({ args }) => {
7312
- const c = clientFor(args);
7313
- const loop2 = await c.loops.get(Number(args.loop));
7314
- console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
6758
+ // ../../node_modules/.pnpm/eventsource@4.1.0/node_modules/eventsource/dist/index.js
6759
+ var ErrorEvent = class extends Event {
6760
+ /**
6761
+ * Constructs a new `ErrorEvent` instance. This is typically not called directly,
6762
+ * but rather emitted by the `EventSource` object when an error occurs.
6763
+ *
6764
+ * @param type - The type of the event (should be "error")
6765
+ * @param errorEventInitDict - Optional properties to include in the error event
6766
+ */
6767
+ constructor(type, errorEventInitDict) {
6768
+ var _a, _b;
6769
+ super(type), this.code = (_a = errorEventInitDict == null ? void 0 : errorEventInitDict.code) != null ? _a : void 0, this.message = (_b = errorEventInitDict == null ? void 0 : errorEventInitDict.message) != null ? _b : void 0;
7315
6770
  }
7316
- });
7317
-
7318
- // src/commands/loop/ls.ts
7319
- var loopLsCommand = defineCommand({
7320
- meta: { name: "ls", description: "list a session's loops" },
7321
- args: {
7322
- session: { type: "positional", required: true, description: "session int id or name" },
7323
- project: { type: "string", description: "project id (overrides .baton.json)" },
7324
- ...common
7325
- },
7326
- run: async ({ args }) => {
7327
- const c = clientFor(args);
7328
- const session2 = await resolveSession(c, resolveProjectId(args), args.session);
7329
- const loops = await c.loops.listBySession(session2.id);
7330
- console.log(renderList(loops, fmtLoop, Boolean(args.json)));
6771
+ /**
6772
+ * Node.js "hides" the `message` and `code` properties of the `ErrorEvent` instance,
6773
+ * when it is `console.log`'ed. This makes it harder to debug errors. To ease debugging,
6774
+ * we explicitly include the properties in the `inspect` method.
6775
+ *
6776
+ * This is automatically called by Node.js when you `console.log` an instance of this class.
6777
+ *
6778
+ * @param _depth - The current depth
6779
+ * @param options - The options passed to `util.inspect`
6780
+ * @param inspect - The inspect function to use (prevents having to import it from `util`)
6781
+ * @returns A string representation of the error
6782
+ */
6783
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")](_depth, options, inspect) {
6784
+ return inspect(inspectableError(this), options);
7331
6785
  }
7332
- });
7333
-
7334
- // src/commands/loop/rm.ts
7335
- var loopRmCommand = defineCommand({
7336
- meta: { name: "rm", description: "delete a loop" },
7337
- args: {
7338
- loop: { type: "positional", required: true, description: "loop int id" },
7339
- ...common
7340
- },
7341
- run: async ({ args }) => {
7342
- const c = clientFor(args);
7343
- const id = Number(args.loop);
7344
- await c.loops.remove(id);
7345
- console.log(removed("loop", id, Boolean(args.json)));
6786
+ /**
6787
+ * Deno "hides" the `message` and `code` properties of the `ErrorEvent` instance,
6788
+ * when it is `console.log`'ed. This makes it harder to debug errors. To ease debugging,
6789
+ * we explicitly include the properties in the `inspect` method.
6790
+ *
6791
+ * This is automatically called by Deno when you `console.log` an instance of this class.
6792
+ *
6793
+ * @param inspect - The inspect function to use (prevents having to import it from `util`)
6794
+ * @param options - The options passed to `Deno.inspect`
6795
+ * @returns A string representation of the error
6796
+ */
6797
+ [/* @__PURE__ */ Symbol.for("Deno.customInspect")](inspect, options) {
6798
+ return inspect(inspectableError(this), options);
7346
6799
  }
7347
- });
7348
-
7349
- // src/commands/loop/set.ts
7350
- var loopSetCommand = defineCommand({
7351
- meta: { name: "set", description: "update a loop (message / interval / name)" },
7352
- args: {
7353
- loop: { type: "positional", required: true, description: "loop int id" },
7354
- message: { type: "string", description: "new message" },
7355
- every: { type: "string", description: "new interval: 90s / 30m / 2h / 1d" },
7356
- name: { type: "string", description: "new label" },
7357
- ...common
7358
- },
7359
- run: async ({ args }) => {
7360
- const c = clientFor(args);
7361
- const patch = {};
7362
- if (args.message !== void 0) patch.message = args.message;
7363
- if (args.every !== void 0 && args.every !== "") patch.intervalSec = parseDuration(args.every);
7364
- if (args.name !== void 0) patch.name = args.name;
7365
- const loop2 = await c.loops.update(Number(args.loop), patch);
7366
- console.log(renderOne(loop2, fmtLoop, Boolean(args.json)));
7367
- }
7368
- });
7369
-
7370
- // src/commands/loop.ts
7371
- var loop = defineCommand({
7372
- meta: { name: "loop", description: "recurring scheduled messages that wake a session" },
7373
- subCommands: {
7374
- create: loopCreateCommand,
7375
- ls: loopLsCommand,
7376
- get: loopGetCommand,
7377
- set: loopSetCommand,
7378
- enable: loopEnableCommand,
7379
- disable: loopDisableCommand,
7380
- rm: loopRmCommand
7381
- }
7382
- });
7383
-
7384
- // src/commands/notify.ts
7385
- var describe2 = (result) => {
7386
- if (result.status === "sent") return `notified ${result.devices} device(s)`;
7387
- if (result.status === "throttled")
7388
- return `throttled: this session can notify again in ${result.retryAfterSec}s`;
7389
- return "nobody to notify: no human has messaged this session, or they have not enabled notifications";
7390
6800
  };
7391
- var notify = defineCommand({
7392
- meta: {
7393
- name: "notify",
7394
- description: "Push a phone notification to the person working with this session. Use it only when a human needs to act now: something is wrong, a decision is needed, or the work is blocked. Do not use it for routine progress, and not to announce that a turn finished (that is already pushed). At most one notification per session every 10 minutes; text is cut at 200 characters."
7395
- },
7396
- args: {
7397
- text: { type: "positional", required: true, description: "one short line for the lock screen" },
7398
- ...common
7399
- },
7400
- run: async ({ args }) => {
7401
- const raw = process.env.BATON_SESSION_ID;
7402
- if (raw === void 0)
7403
- throw new Error("baton notify only works inside a baton session (BATON_SESSION_ID is unset)");
7404
- const sessionId = Number(raw);
7405
- if (!Number.isSafeInteger(sessionId) || sessionId <= 0)
7406
- throw new Error("invalid BATON_SESSION_ID");
7407
- const result = await clientFor(args).sessions.notify(sessionId, args.text);
7408
- console.log(args.json ? toJson(result) : describe2(result));
7409
- }
7410
- });
7411
-
7412
- // src/commands/project.ts
7413
- var createProject = (c, input, json) => c.projects.create(input).then((p) => renderOne(p, fmtProject, json));
7414
- var listProjects = (c, workspaceId, json) => c.projects.listByWorkspace(workspaceId).then((ps) => renderList(ps, fmtProject, json));
7415
- var getProject = (c, id, json) => c.projects.get(id).then((p) => renderOne(p, fmtProject, json));
7416
- var updateProject = (c, id, patch, json) => c.projects.update(id, patch).then((p) => renderOne(p, fmtProject, json));
7417
- var removeProject = (c, id, json) => c.projects.remove(id).then(() => removed("project", id, json));
7418
- var project = defineCommand({
7419
- meta: { name: "project", description: "manage projects" },
7420
- subCommands: {
7421
- create: defineCommand({
7422
- meta: { name: "create", description: "create a project" },
7423
- args: {
7424
- name: { type: "positional", required: true },
7425
- workspace: { type: "string", description: "workspace id (overrides .baton.json)" },
7426
- desc: { type: "string", description: "description" },
7427
- ...common
7428
- },
7429
- run: async ({ args }) => {
7430
- const out = await createProject(
7431
- clientFor(args),
7432
- { workspaceId: resolveWorkspaceId(args), name: args.name, description: args.desc },
7433
- Boolean(args.json)
7434
- );
7435
- console.log(out);
7436
- }
7437
- }),
7438
- ls: defineCommand({
7439
- meta: { name: "ls", description: "list projects in a workspace" },
7440
- args: {
7441
- workspace: { type: "string", description: "workspace id (overrides .baton.json)" },
7442
- ...common
7443
- },
7444
- run: async ({ args }) => {
7445
- console.log(
7446
- await listProjects(clientFor(args), resolveWorkspaceId(args), Boolean(args.json))
7447
- );
6801
+ function syntaxError(message) {
6802
+ const DomException = globalThis.DOMException;
6803
+ return typeof DomException == "function" ? new DomException(message, "SyntaxError") : new SyntaxError(message);
6804
+ }
6805
+ function flattenError(err) {
6806
+ return err instanceof Error ? "errors" in err && Array.isArray(err.errors) ? err.errors.map(flattenError).join(", ") : "cause" in err && err.cause instanceof Error ? `${err}: ${flattenError(err.cause)}` : err.message : `${err}`;
6807
+ }
6808
+ function inspectableError(err) {
6809
+ return {
6810
+ type: err.type,
6811
+ message: err.message,
6812
+ code: err.code,
6813
+ defaultPrevented: err.defaultPrevented,
6814
+ cancelable: err.cancelable,
6815
+ timeStamp: err.timeStamp
6816
+ };
6817
+ }
6818
+ var __typeError = (msg) => {
6819
+ throw TypeError(msg);
6820
+ };
6821
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
6822
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6823
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
6824
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
6825
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
6826
+ var _readyState;
6827
+ var _url;
6828
+ var _redirectUrl;
6829
+ var _withCredentials;
6830
+ var _fetch;
6831
+ var _reconnectInterval;
6832
+ var _reconnectTimer;
6833
+ var _lastEventId;
6834
+ var _controller;
6835
+ var _parser;
6836
+ var _onError;
6837
+ var _onMessage;
6838
+ var _onOpen;
6839
+ var _EventSource_instances;
6840
+ var connect_fn;
6841
+ var _onFetchResponse;
6842
+ var _onFetchError;
6843
+ var getRequestOptions_fn;
6844
+ var _onEvent;
6845
+ var _onRetryChange;
6846
+ var failConnection_fn;
6847
+ var scheduleReconnect_fn;
6848
+ var _reconnect;
6849
+ var EventSource = class extends EventTarget {
6850
+ constructor(url, eventSourceInitDict) {
6851
+ var _a, _b;
6852
+ super(), __privateAdd(this, _EventSource_instances), this.CONNECTING = 0, this.OPEN = 1, this.CLOSED = 2, __privateAdd(this, _readyState), __privateAdd(this, _url), __privateAdd(this, _redirectUrl), __privateAdd(this, _withCredentials), __privateAdd(this, _fetch), __privateAdd(this, _reconnectInterval), __privateAdd(this, _reconnectTimer), __privateAdd(this, _lastEventId, null), __privateAdd(this, _controller), __privateAdd(this, _parser), __privateAdd(this, _onError, null), __privateAdd(this, _onMessage, null), __privateAdd(this, _onOpen, null), __privateAdd(this, _onFetchResponse, async (response) => {
6853
+ var _a2;
6854
+ __privateGet(this, _parser).reset();
6855
+ const { body, redirected, status, headers } = response;
6856
+ if (status === 204) {
6857
+ __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, "Server sent HTTP 204, not reconnecting", 204), this.close();
6858
+ return;
7448
6859
  }
7449
- }),
7450
- get: defineCommand({
7451
- meta: { name: "get", description: "get a project" },
7452
- args: { id: { type: "positional", required: true }, ...common },
7453
- run: async ({ args }) => {
7454
- console.log(await getProject(clientFor(args), Number(args.id), Boolean(args.json)));
6860
+ if (redirected ? __privateSet(this, _redirectUrl, new URL(response.url)) : __privateSet(this, _redirectUrl, void 0), status !== 200) {
6861
+ __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, `Non-200 status code (${status})`, status);
6862
+ return;
7455
6863
  }
7456
- }),
7457
- update: defineCommand({
7458
- meta: { name: "update", description: "rename a project / set its description" },
7459
- args: {
7460
- id: { type: "positional", required: true },
7461
- name: { type: "string", description: "new name" },
7462
- desc: { type: "string", description: "new description" },
7463
- ...common
7464
- },
7465
- run: async ({ args }) => {
7466
- const patch = {
7467
- ...args.name !== void 0 ? { name: args.name } : {},
7468
- ...args.desc !== void 0 ? { description: args.desc } : {}
7469
- };
7470
- if (Object.keys(patch).length === 0) throw new Error("pass --name and/or --desc");
7471
- console.log(
7472
- await updateProject(clientFor(args), Number(args.id), patch, Boolean(args.json))
7473
- );
6864
+ if (!(headers.get("content-type") || "").startsWith("text/event-stream")) {
6865
+ __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, 'Invalid content type, expected "text/event-stream"', status);
6866
+ return;
7474
6867
  }
7475
- }),
7476
- rm: defineCommand({
7477
- meta: { name: "rm", description: "delete a project" },
7478
- args: { id: { type: "positional", required: true }, ...common },
7479
- run: async ({ args }) => {
7480
- console.log(await removeProject(clientFor(args), Number(args.id), Boolean(args.json)));
6868
+ if (__privateGet(this, _readyState) === this.CLOSED)
6869
+ return;
6870
+ __privateSet(this, _readyState, this.OPEN);
6871
+ const openEvent = new Event("open");
6872
+ if ((_a2 = __privateGet(this, _onOpen)) == null || _a2.call(this, openEvent), this.dispatchEvent(openEvent), typeof body != "object" || !body || !("getReader" in body)) {
6873
+ __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, "Invalid response body, expected a web ReadableStream", status), this.close();
6874
+ return;
7481
6875
  }
7482
- })
6876
+ const decoder = new TextDecoder(), reader = body.getReader();
6877
+ let open3 = true;
6878
+ do {
6879
+ const { done, value } = await reader.read();
6880
+ value && __privateGet(this, _parser).feed(decoder.decode(value, { stream: !done })), done && (open3 = false, __privateGet(this, _parser).reset(), __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this));
6881
+ } while (open3);
6882
+ }), __privateAdd(this, _onFetchError, (err) => {
6883
+ __privateSet(this, _controller, void 0), !(err.name === "AbortError" || err.type === "aborted") && __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this, flattenError(err));
6884
+ }), __privateAdd(this, _onEvent, (event) => {
6885
+ typeof event.id == "string" && __privateSet(this, _lastEventId, event.id);
6886
+ const messageEvent = new MessageEvent(event.event || "message", {
6887
+ data: event.data,
6888
+ origin: __privateGet(this, _redirectUrl) ? __privateGet(this, _redirectUrl).origin : __privateGet(this, _url).origin,
6889
+ lastEventId: event.id || ""
6890
+ });
6891
+ __privateGet(this, _onMessage) && (!event.event || event.event === "message") && __privateGet(this, _onMessage).call(this, messageEvent), this.dispatchEvent(messageEvent);
6892
+ }), __privateAdd(this, _onRetryChange, (value) => {
6893
+ __privateSet(this, _reconnectInterval, value);
6894
+ }), __privateAdd(this, _reconnect, () => {
6895
+ __privateSet(this, _reconnectTimer, void 0), __privateGet(this, _readyState) === this.CONNECTING && __privateMethod(this, _EventSource_instances, connect_fn).call(this);
6896
+ });
6897
+ try {
6898
+ if (url instanceof URL)
6899
+ __privateSet(this, _url, url);
6900
+ else if (typeof url == "string")
6901
+ __privateSet(this, _url, new URL(url, getBaseURL()));
6902
+ else
6903
+ throw new Error("Invalid URL");
6904
+ } catch {
6905
+ throw syntaxError("An invalid or illegal string was specified");
6906
+ }
6907
+ __privateSet(this, _parser, createParser({
6908
+ onEvent: __privateGet(this, _onEvent),
6909
+ onRetry: __privateGet(this, _onRetryChange)
6910
+ })), __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _reconnectInterval, 3e3), __privateSet(this, _fetch, (_a = eventSourceInitDict == null ? void 0 : eventSourceInitDict.fetch) != null ? _a : globalThis.fetch), __privateSet(this, _withCredentials, (_b = eventSourceInitDict == null ? void 0 : eventSourceInitDict.withCredentials) != null ? _b : false), __privateMethod(this, _EventSource_instances, connect_fn).call(this);
7483
6911
  }
7484
- });
7485
-
7486
- // src/provisioner/config.ts
7487
- import { execFile } from "node:child_process";
7488
- import { constants } from "node:fs";
7489
- import { access, readFile, stat as stat2 } from "node:fs/promises";
7490
- import { isAbsolute } from "node:path";
7491
- import { promisify } from "node:util";
7492
- var forbiddenEnv = /* @__PURE__ */ new Set([
7493
- "BATON_PROVISIONER_TOKEN",
7494
- "BATON_TOKEN",
7495
- "BATON_WORKER_TOKEN",
7496
- "BATON_USER",
7497
- "BATON_PASS",
7498
- "HOME",
7499
- "XDG_DATA_HOME"
7500
- ]);
7501
- var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7502
- var absolutePath = (value, field) => {
7503
- if (typeof value !== "string" || !isAbsolute(value) || value.includes("\0"))
7504
- throw new Error(`${field} must be an absolute path`);
7505
- return value;
7506
- };
7507
- var environment = (value) => {
7508
- if (!object(value)) throw new Error("workerEnv must be an object");
7509
- return Object.fromEntries(
7510
- Object.entries(value).map(([key, entry]) => {
7511
- if (forbiddenEnv.has(key) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) || typeof entry !== "string" || entry.includes("\0"))
7512
- throw new Error(`invalid worker environment key: ${key}`);
7513
- return [key, entry];
7514
- })
7515
- );
7516
- };
7517
- var templates = (value) => {
7518
- if (!object(value)) throw new Error("templates must be an object");
7519
- return Object.fromEntries(
7520
- WORKER_TEMPLATES.filter((template) => value[template.id] !== void 0).map((template) => {
7521
- const raw = value[template.id];
7522
- if (!object(raw)) throw new Error(`template ${template.id} must be an object`);
7523
- const workerEnv2 = environment(raw.workerEnv);
7524
- if (template.id === "mini-codex") {
7525
- if (raw.pi !== void 0) throw new Error("mini-codex does not support pi");
7526
- return [template.id, { workerEnv: workerEnv2 }];
7527
- }
7528
- const provider = template.id === "mini-glm-codeplan" ? "zai-coding-cn" : "qwen-token-plan-cn";
7529
- const apiKey = template.id === "mini-glm-codeplan" ? "ZAI_CODING_CN_API_KEY" : "QWEN_TOKEN_PLAN_CN_API_KEY";
7530
- if (!workerEnv2.ANTHROPIC_BASE_URL || !(workerEnv2.ANTHROPIC_AUTH_TOKEN || workerEnv2.ANTHROPIC_API_KEY) || !workerEnv2[apiKey])
7531
- throw new Error(`template ${template.id} requires Claude and Pi credentials`);
7532
- if (!object(raw.pi) || raw.pi.provider !== provider || typeof raw.pi.model !== "string" || !raw.pi.model.trim())
7533
- throw new Error(`template ${template.id} requires its Pi provider and model`);
7534
- return [template.id, { workerEnv: workerEnv2, pi: { provider, model: raw.pi.model.trim() } }];
7535
- })
7536
- );
7537
- };
7538
- var commandEnvironment = (config) => ({
7539
- ...process.env,
7540
- PATH: config.workerEnv.PATH ?? process.env.PATH ?? "/usr/bin:/bin"
7541
- });
7542
- var loadProvisionerConfig = async (path) => {
7543
- if (process.platform !== "darwin") throw new Error("provisioner requires macOS");
7544
- absolutePath(path, "config");
7545
- if ((await stat2(path)).mode & 63) throw new Error("config must be private: chmod 600 <config>");
7546
- const text = await readFile(path, "utf8");
7547
- let raw;
7548
- try {
7549
- raw = JSON.parse(text);
7550
- } catch (error) {
7551
- if (!(error instanceof SyntaxError)) throw error;
7552
- throw new Error("invalid provisioner JSON config");
6912
+ /**
6913
+ * Returns the state of this EventSource object's connection. It can have the values described below.
6914
+ *
6915
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState)
6916
+ *
6917
+ * Note: typed as `number` instead of `0 | 1 | 2` for compatibility with the `EventSource` interface,
6918
+ * defined in the TypeScript `dom` library.
6919
+ *
6920
+ * @public
6921
+ */
6922
+ get readyState() {
6923
+ return __privateGet(this, _readyState);
6924
+ }
6925
+ /**
6926
+ * Returns the URL providing the event stream.
6927
+ *
6928
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url)
6929
+ *
6930
+ * @public
6931
+ */
6932
+ get url() {
6933
+ return __privateGet(this, _url).href;
6934
+ }
6935
+ /**
6936
+ * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to "include", and false otherwise.
6937
+ *
6938
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials)
6939
+ */
6940
+ get withCredentials() {
6941
+ return __privateGet(this, _withCredentials);
6942
+ }
6943
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */
6944
+ get onerror() {
6945
+ return __privateGet(this, _onError);
7553
6946
  }
7554
- if (!object(raw) || typeof raw.server !== "string" || typeof raw.token !== "string")
7555
- throw new Error("server and token required");
7556
- let server;
7557
- try {
7558
- server = new URL(raw.server);
7559
- } catch (error) {
7560
- if (!(error instanceof TypeError)) throw error;
7561
- throw new Error("server must be a valid HTTP(S) URL");
6947
+ set onerror(value) {
6948
+ __privateSet(this, _onError, value);
6949
+ }
6950
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */
6951
+ get onmessage() {
6952
+ return __privateGet(this, _onMessage);
6953
+ }
6954
+ set onmessage(value) {
6955
+ __privateSet(this, _onMessage, value);
6956
+ }
6957
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */
6958
+ get onopen() {
6959
+ return __privateGet(this, _onOpen);
6960
+ }
6961
+ set onopen(value) {
6962
+ __privateSet(this, _onOpen, value);
6963
+ }
6964
+ addEventListener(type, listener, options) {
6965
+ const listen = listener;
6966
+ super.addEventListener(type, listen, options);
6967
+ }
6968
+ removeEventListener(type, listener, options) {
6969
+ const listen = listener;
6970
+ super.removeEventListener(type, listen, options);
6971
+ }
6972
+ /**
6973
+ * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED.
6974
+ *
6975
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close)
6976
+ *
6977
+ * @public
6978
+ */
6979
+ close() {
6980
+ __privateGet(this, _reconnectTimer) && clearTimeout(__privateGet(this, _reconnectTimer)), __privateGet(this, _readyState) !== this.CLOSED && (__privateGet(this, _controller) && __privateGet(this, _controller).abort(), __privateSet(this, _readyState, this.CLOSED), __privateSet(this, _controller, void 0));
7562
6981
  }
7563
- if (!["http:", "https:"].includes(server.protocol) || server.username || server.password || server.search || server.hash)
7564
- throw new Error("server must be an HTTP(S) URL without credentials, query, or fragment");
7565
- if (!raw.token.trim() || /[\r\n]/.test(raw.token))
7566
- throw new Error("valid provisioner token required");
7567
- const config = {
7568
- server: server.toString().replace(/\/$/, ""),
7569
- token: raw.token.trim(),
7570
- projectsRoot: absolutePath(raw.projectsRoot, "projectsRoot"),
7571
- cliPath: absolutePath(raw.cliPath, "cliPath"),
7572
- nodePath: absolutePath(raw.nodePath ?? process.execPath, "nodePath"),
7573
- workerEnv: environment(raw.workerEnv),
7574
- templates: templates(raw.templates)
7575
- };
7576
- await access(config.cliPath, constants.R_OK);
7577
- await access(config.nodePath ?? process.execPath, constants.X_OK);
7578
- const env = commandEnvironment(config);
7579
- await promisify(execFile)("git", ["--version"], { env, timeout: 1e4 });
7580
- await promisify(execFile)("launchctl", ["help"], { env, timeout: 1e4 });
7581
- await promisify(execFile)("/usr/bin/plutil", ["-help"], { timeout: 1e4 });
7582
- return config;
7583
6982
  };
6983
+ _readyState = /* @__PURE__ */ new WeakMap(), _url = /* @__PURE__ */ new WeakMap(), _redirectUrl = /* @__PURE__ */ new WeakMap(), _withCredentials = /* @__PURE__ */ new WeakMap(), _fetch = /* @__PURE__ */ new WeakMap(), _reconnectInterval = /* @__PURE__ */ new WeakMap(), _reconnectTimer = /* @__PURE__ */ new WeakMap(), _lastEventId = /* @__PURE__ */ new WeakMap(), _controller = /* @__PURE__ */ new WeakMap(), _parser = /* @__PURE__ */ new WeakMap(), _onError = /* @__PURE__ */ new WeakMap(), _onMessage = /* @__PURE__ */ new WeakMap(), _onOpen = /* @__PURE__ */ new WeakMap(), _EventSource_instances = /* @__PURE__ */ new WeakSet(), /**
6984
+ * Connect to the given URL and start receiving events
6985
+ *
6986
+ * @internal
6987
+ */
6988
+ connect_fn = function() {
6989
+ __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _controller, new AbortController()), __privateGet(this, _fetch)(__privateGet(this, _url), __privateMethod(this, _EventSource_instances, getRequestOptions_fn).call(this)).then(__privateGet(this, _onFetchResponse)).catch(__privateGet(this, _onFetchError));
6990
+ }, _onFetchResponse = /* @__PURE__ */ new WeakMap(), _onFetchError = /* @__PURE__ */ new WeakMap(), /**
6991
+ * Get request options for the `fetch()` request
6992
+ *
6993
+ * @returns The request options
6994
+ * @internal
6995
+ */
6996
+ getRequestOptions_fn = function() {
6997
+ var _a;
6998
+ const init2 = {
6999
+ // [spec] Let `corsAttributeState` be `Anonymous`…
7000
+ // [spec] …will have their mode set to "cors"…
7001
+ mode: "cors",
7002
+ redirect: "follow",
7003
+ headers: { Accept: "text/event-stream", ...__privateGet(this, _lastEventId) ? { "Last-Event-ID": __privateGet(this, _lastEventId) } : void 0 },
7004
+ cache: "no-store",
7005
+ signal: (_a = __privateGet(this, _controller)) == null ? void 0 : _a.signal
7006
+ };
7007
+ return "window" in globalThis && (init2.credentials = this.withCredentials ? "include" : "same-origin"), init2;
7008
+ }, _onEvent = /* @__PURE__ */ new WeakMap(), _onRetryChange = /* @__PURE__ */ new WeakMap(), /**
7009
+ * Handles the process referred to in the EventSource specification as "failing a connection".
7010
+ *
7011
+ * @param error - The error causing the connection to fail
7012
+ * @param code - The HTTP status code, if available
7013
+ * @internal
7014
+ */
7015
+ failConnection_fn = function(message, code) {
7016
+ var _a;
7017
+ __privateGet(this, _readyState) !== this.CLOSED && __privateSet(this, _readyState, this.CLOSED);
7018
+ const errorEvent = new ErrorEvent("error", { code, message });
7019
+ (_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent);
7020
+ }, /**
7021
+ * Schedules a reconnection attempt against the EventSource endpoint.
7022
+ *
7023
+ * @param message - The error causing the connection to fail
7024
+ * @param code - The HTTP status code, if available
7025
+ * @internal
7026
+ */
7027
+ scheduleReconnect_fn = function(message, code) {
7028
+ var _a;
7029
+ if (__privateGet(this, _readyState) === this.CLOSED)
7030
+ return;
7031
+ __privateSet(this, _readyState, this.CONNECTING);
7032
+ const errorEvent = new ErrorEvent("error", { code, message });
7033
+ (_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent), __privateSet(this, _reconnectTimer, setTimeout(__privateGet(this, _reconnect), __privateGet(this, _reconnectInterval)));
7034
+ }, _reconnect = /* @__PURE__ */ new WeakMap(), /**
7035
+ * ReadyState representing an EventSource currently trying to connect
7036
+ *
7037
+ * @public
7038
+ */
7039
+ EventSource.CONNECTING = 0, /**
7040
+ * ReadyState representing an EventSource connection that is open (eg connected)
7041
+ *
7042
+ * @public
7043
+ */
7044
+ EventSource.OPEN = 1, /**
7045
+ * ReadyState representing an EventSource connection that is closed (eg disconnected)
7046
+ *
7047
+ * @public
7048
+ */
7049
+ EventSource.CLOSED = 2;
7050
+ Object.defineProperty(EventSource, /* @__PURE__ */ Symbol.for("eventsource.supports-fetch-override"), {
7051
+ value: true,
7052
+ writable: false,
7053
+ configurable: false,
7054
+ enumerable: false
7055
+ });
7056
+ function getBaseURL() {
7057
+ const doc = "document" in globalThis ? globalThis.document : void 0;
7058
+ return doc && typeof doc == "object" && "baseURI" in doc && typeof doc.baseURI == "string" ? doc.baseURI : void 0;
7059
+ }
7584
7060
 
7585
7061
  // src/provisioner/client.ts
7586
7062
  var createProvisionerClient = (config) => {
@@ -7613,7 +7089,7 @@ var createProvisionerClient = (config) => {
7613
7089
  import { execFile as execFile3 } from "node:child_process";
7614
7090
  import { lstat, mkdir as mkdir3, realpath, writeFile as writeFile2 } from "node:fs/promises";
7615
7091
  import { hostname } from "node:os";
7616
- import { dirname as dirname2, join as join5 } from "node:path";
7092
+ import { dirname as dirname2, join as join4 } from "node:path";
7617
7093
  import { setTimeout as setTimeout2 } from "node:timers/promises";
7618
7094
  import { promisify as promisify3 } from "node:util";
7619
7095
 
@@ -7621,18 +7097,18 @@ import { promisify as promisify3 } from "node:util";
7621
7097
  import { execFile as execFile2 } from "node:child_process";
7622
7098
  import { mkdir, open } from "node:fs/promises";
7623
7099
  import { homedir } from "node:os";
7624
- import { dirname, join as join3 } from "node:path";
7100
+ import { dirname, join as join2 } from "node:path";
7625
7101
  import { promisify as promisify2 } from "node:util";
7626
- var workerStatePath = (config, creation) => join3(
7102
+ var workerStatePath = (config, creation) => join2(
7627
7103
  config.projectsRoot,
7628
7104
  ".worker-state",
7629
7105
  `project-${creation.projectId}`,
7630
7106
  `creation-${creation.id}`
7631
7107
  );
7632
- var workerLogPath = (creation) => join3(homedir(), "Library/Logs", `baton-worker-creation-${creation.id}.log`);
7108
+ var workerLogPath = (creation) => join2(homedir(), "Library/Logs", `baton-worker-creation-${creation.id}.log`);
7633
7109
  var startAgent = async (config, agent, signal) => {
7634
7110
  signal?.throwIfAborted();
7635
- const plist = join3(homedir(), "Library/LaunchAgents", `${agent.label}.plist`);
7111
+ const plist = join2(homedir(), "Library/LaunchAgents", `${agent.label}.plist`);
7636
7112
  await mkdir(dirname(plist), { recursive: true });
7637
7113
  await mkdir(dirname(agent.log), { recursive: true });
7638
7114
  const domain = `gui/${process.getuid?.()}`;
@@ -7718,12 +7194,12 @@ var installProvisionerAgent = async (config, configPath) => startAgent(config, {
7718
7194
  ],
7719
7195
  cwd: dirname(configPath),
7720
7196
  env: { HOME: homedir(), PATH: config.workerEnv.PATH ?? process.env.PATH ?? "/usr/bin:/bin" },
7721
- log: join3(homedir(), "Library/Logs/baton-provisioner.log")
7197
+ log: join2(homedir(), "Library/Logs/baton-provisioner.log")
7722
7198
  });
7723
7199
 
7724
7200
  // src/provisioner/worker-template.ts
7725
7201
  import { mkdir as mkdir2, writeFile } from "node:fs/promises";
7726
- import { join as join4 } from "node:path";
7202
+ import { join as join3 } from "node:path";
7727
7203
  var requireWorkerTemplate = (config, creation) => {
7728
7204
  if (!workerTemplateAgentKinds(creation.templateId).includes(creation.agentKind))
7729
7205
  throw new Error("agentKind is not supported by this template");
@@ -7737,10 +7213,10 @@ var workerTemplateEnvironment = async (config, creation, state) => {
7737
7213
  if (!profile) return config.workerEnv;
7738
7214
  if (creation.agentKind !== "pi") return profile.workerEnv;
7739
7215
  if (!profile.pi) throw new Error("Pi provider and model are not configured");
7740
- const agentDir = join4(state, "pi");
7216
+ const agentDir = join3(state, "pi");
7741
7217
  await mkdir2(agentDir, { mode: 448 });
7742
7218
  await writeFile(
7743
- join4(agentDir, "settings.json"),
7219
+ join3(agentDir, "settings.json"),
7744
7220
  JSON.stringify({
7745
7221
  defaultProvider: profile.pi.provider,
7746
7222
  defaultModel: profile.pi.model
@@ -7778,13 +7254,13 @@ var initRepository = async (config, creation, repoPath, progress, signal) => {
7778
7254
  signal.throwIfAborted();
7779
7255
  await mkdir3(config.projectsRoot, { recursive: true, mode: 448 });
7780
7256
  const root = await realpath(config.projectsRoot);
7781
- await directory(join5(root, `project-${creation.projectId}`));
7782
- await mkdir3(join5(root, `project-${creation.projectId}`, `creation-${creation.id}`), {
7257
+ await directory(join4(root, `project-${creation.projectId}`));
7258
+ await mkdir3(join4(root, `project-${creation.projectId}`, `creation-${creation.id}`), {
7783
7259
  mode: 448
7784
7260
  });
7785
7261
  await progress("git");
7786
7262
  await gitIn(config, repoPath, ["init", "-b", "main"], signal);
7787
- await writeFile2(join5(repoPath, ".git/info/exclude"), "\n.baton.json\n", { flag: "a" });
7263
+ await writeFile2(join4(repoPath, ".git/info/exclude"), "\n.baton.json\n", { flag: "a" });
7788
7264
  await gitIn(
7789
7265
  config,
7790
7266
  repoPath,
@@ -7805,7 +7281,7 @@ var adoptRepository = async (config, creation, repoPath, progress, signal) => {
7805
7281
  await progress("directory");
7806
7282
  signal.throwIfAborted();
7807
7283
  const real = await realpath(repoPath);
7808
- const project2 = join5(await realpath(config.projectsRoot), `project-${creation.projectId}`);
7284
+ const project2 = join4(await realpath(config.projectsRoot), `project-${creation.projectId}`);
7809
7285
  if (dirname2(real) !== project2 || !(await lstat(real)).isDirectory())
7810
7286
  throw new Error(`unsafe directory: ${repoPath}`);
7811
7287
  await progress("git");
@@ -7822,18 +7298,18 @@ var adoptRepository = async (config, creation, repoPath, progress, signal) => {
7822
7298
  var provisionWorker = async (config, creation, client, signal) => {
7823
7299
  requireWorkerTemplate(config, creation);
7824
7300
  const shared = creation.repoPath !== null;
7825
- const repoPath = creation.repoPath ?? join5(config.projectsRoot, `project-${creation.projectId}`, `creation-${creation.id}`);
7301
+ const repoPath = creation.repoPath ?? join4(config.projectsRoot, `project-${creation.projectId}`, `creation-${creation.id}`);
7826
7302
  const progress = (step) => client.report(creation.id, { status: "running", step, repoPath }, signal);
7827
7303
  await (shared ? adoptRepository : initRepository)(config, creation, repoPath, progress, signal);
7828
7304
  await progress("register");
7829
7305
  const result = await client.register(creation.id, hostname(), signal);
7830
7306
  const state = workerStatePath(config, creation);
7831
7307
  const root = await realpath(config.projectsRoot);
7832
- await directory(join5(root, ".worker-state"));
7833
- await directory(join5(root, ".worker-state", `project-${creation.projectId}`));
7308
+ await directory(join4(root, ".worker-state"));
7309
+ await directory(join4(root, ".worker-state", `project-${creation.projectId}`));
7834
7310
  await mkdir3(state, { mode: 448 });
7835
- await mkdir3(join5(state, "baton"), { mode: 448 });
7836
- await writeFile2(join5(state, "baton/machine-id"), `${creation.machineId}
7311
+ await mkdir3(join4(state, "baton"), { mode: 448 });
7312
+ await writeFile2(join4(state, "baton/machine-id"), `${creation.machineId}
7837
7313
  `, {
7838
7314
  flag: "wx",
7839
7315
  mode: 384
@@ -7847,7 +7323,7 @@ var provisionWorker = async (config, creation, client, signal) => {
7847
7323
  machineId: result.worker.machineId,
7848
7324
  apiToken: result.apiToken
7849
7325
  });
7850
- const configPath = shared ? join5(state, "worker.json") : join5(repoPath, ".baton.json");
7326
+ const configPath = shared ? join4(state, "worker.json") : join4(repoPath, ".baton.json");
7851
7327
  await writeFile2(configPath, `${JSON.stringify(cfg, null, 2)}
7852
7328
  `, { flag: "wx", mode: 384 });
7853
7329
  await progress("start");
@@ -8027,183 +7503,6 @@ var provisioner = defineCommand({
8027
7503
  }
8028
7504
  });
8029
7505
 
8030
- // src/commands/relay.ts
8031
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
8032
- import { tmpdir as tmpdir2 } from "node:os";
8033
- import { join as join6 } from "node:path";
8034
-
8035
- // src/client/relay.ts
8036
- var relayClient = (baseUrl) => {
8037
- const u = (p) => `${baseUrl}${p}`;
8038
- const authed = (token) => ({ authorization: `Bearer ${token}` });
8039
- return {
8040
- create: async () => {
8041
- const res = await fetch(u("/relay/channels"), { method: "POST" });
8042
- if (!res.ok) throw new Error(`POST /relay/channels \u2192 ${res.status}: ${await res.text()}`);
8043
- return await res.json();
8044
- },
8045
- send: async (channelId, token, msg) => {
8046
- const res = await fetch(u(`/relay/channels/${channelId}/messages`), {
8047
- method: "POST",
8048
- headers: { "content-type": "application/json", ...authed(token) },
8049
- body: JSON.stringify(msg)
8050
- });
8051
- if (!res.ok) throw new Error(`POST relay message \u2192 ${res.status}: ${await res.text()}`);
8052
- return await res.json();
8053
- },
8054
- listen: (channelId, token, opts) => {
8055
- let cursor = opts.since ?? 0;
8056
- const es = new EventSource(u(`/relay/channels/${channelId}/stream?since=${cursor}`), {
8057
- fetch: (url, init2) => fetch(url, {
8058
- ...init2,
8059
- headers: { ...init2?.headers, ...authed(token) }
8060
- })
8061
- });
8062
- es.onmessage = (e) => {
8063
- try {
8064
- const m = JSON.parse(e.data);
8065
- if (m.seq <= cursor) return;
8066
- cursor = m.seq;
8067
- opts.onMessage(m);
8068
- } catch (err) {
8069
- opts.onError?.(err);
8070
- }
8071
- };
8072
- es.onerror = (err) => opts.onError?.(err);
8073
- return () => es.close();
8074
- }
8075
- };
8076
- };
8077
-
8078
- // src/commands/relay.ts
8079
- var PREVIEW2 = 280;
8080
- var readStdin2 = async () => {
8081
- const chunks = [];
8082
- for await (const c of process.stdin) chunks.push(c);
8083
- return Buffer.concat(chunks).toString("utf8");
8084
- };
8085
- var inviteBlock2 = (url, channelId, token) => [
8086
- "\u2500\u2500 Copy everything below to your peer \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",
8087
- "You're invited to a live Claude\u2194Claude channel. Use the `hotline` skill in",
8088
- "join mode, or run these two commands directly (pick your own NAME):",
8089
- "",
8090
- ` baton relay listen ${channelId} --token ${token} --from NAME --url ${url}`,
8091
- ` baton relay send ${channelId} --token ${token} --from NAME --text "..." --url ${url}`,
8092
- "",
8093
- `connection: url=${url} channel=${channelId} token=${token}`,
8094
- "\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"
8095
- ].join("\n");
8096
- var hostCommand = defineCommand({
8097
- meta: { name: "host", description: "open a relay channel and print an invite for a peer" },
8098
- args: {
8099
- name: { type: "string", description: "your participant name (default: host)" },
8100
- ...common
8101
- },
8102
- run: async ({ args }) => {
8103
- const url = resolveBaseUrl(args.url);
8104
- const { channelId, token } = await relayClient(url).create();
8105
- if (args.json) {
8106
- console.log(toJson({ channelId, token, url, host: args.name ?? "host" }));
8107
- return;
8108
- }
8109
- console.log(`channel ${channelId} opened
8110
- `);
8111
- console.log(inviteBlock2(url, channelId, token));
8112
- }
8113
- });
8114
- var sendCommand2 = defineCommand({
8115
- meta: { name: "send", description: "post one message to a relay channel" },
8116
- args: {
8117
- channel: { type: "positional", required: true, description: "channel id" },
8118
- text: { type: "positional", required: false, description: "message text (short one-liner)" },
8119
- token: { type: "string", required: true, description: "channel token" },
8120
- from: { type: "string", description: "your participant name (default: peer)" },
8121
- file: { type: "string", description: "read message body from this file (for large content)" },
8122
- ...common
8123
- },
8124
- run: async ({ args }) => {
8125
- const url = resolveBaseUrl(args.url);
8126
- let text = args.text;
8127
- if (text === void 0 && args.file) text = readFileSync3(args.file, "utf8");
8128
- if (text === void 0 && !process.stdin.isTTY) text = await readStdin2();
8129
- if (text === void 0 || text === "")
8130
- throw new Error("nothing to send: provide text, --file <path>, or pipe stdin");
8131
- const msg = await relayClient(url).send(args.channel, args.token, {
8132
- from: args.from ?? "peer",
8133
- text
8134
- });
8135
- if (args.json) console.log(toJson(msg));
8136
- else console.log(`sent (seq ${msg.seq})`);
8137
- }
8138
- });
8139
- var listenCommand2 = defineCommand({
8140
- meta: {
8141
- name: "listen",
8142
- description: "stream a channel: one JSON line per peer message (for a background subscriber)"
8143
- },
8144
- args: {
8145
- channel: { type: "positional", required: true, description: "channel id" },
8146
- token: { type: "string", required: true, description: "channel token" },
8147
- from: {
8148
- type: "string",
8149
- description: "your name; messages from this name are skipped (no echo)"
8150
- },
8151
- since: { type: "string", description: "replay messages after this seq (default 0 = full)" },
8152
- ...common
8153
- },
8154
- run: async ({ args }) => {
8155
- const url = resolveBaseUrl(args.url);
8156
- const me = args.from ?? "";
8157
- const emit = (o) => {
8158
- process.stdout.write(`${JSON.stringify(o)}
8159
- `);
8160
- };
8161
- const spillDir = join6(tmpdir2(), "hotline", args.channel);
8162
- mkdirSync2(spillDir, { recursive: true });
8163
- const surface = (m) => {
8164
- const text = typeof m.text === "string" ? m.text : "";
8165
- const file = join6(spillDir, `${m.seq}.txt`);
8166
- writeFileSync4(file, text);
8167
- const truncated = text.length > PREVIEW2;
8168
- emit({
8169
- type: "relay.message",
8170
- seq: m.seq,
8171
- from: m.from,
8172
- ts: m.ts,
8173
- chars: text.length,
8174
- truncated,
8175
- preview: truncated ? `${text.slice(0, PREVIEW2)} \u2026` : text,
8176
- file
8177
- });
8178
- };
8179
- const describe3 = (e) => {
8180
- const ev = e;
8181
- return ev?.code ? `${ev.code}${ev.message ? ` ${ev.message}` : ""}` : String(e);
8182
- };
8183
- const close = relayClient(url).listen(args.channel, args.token, {
8184
- since: args.since ? Number(args.since) : 0,
8185
- // Skip our own messages (they come back over the same channel).
8186
- onMessage: (m) => {
8187
- if (m.from !== me) surface(m);
8188
- },
8189
- onError: (e) => emit({ type: "relay.error", error: describe3(e) })
8190
- });
8191
- emit({ type: "relay.listening", channel: args.channel, from: me, url });
8192
- await new Promise((resolve4) => {
8193
- const stop = () => {
8194
- close();
8195
- resolve4();
8196
- };
8197
- process.on("SIGINT", stop);
8198
- process.on("SIGTERM", stop);
8199
- });
8200
- }
8201
- });
8202
- var relay = defineCommand({
8203
- meta: { name: "relay", description: "Claude\u2194Claude live channel: host / listen / send" },
8204
- subCommands: { host: hostCommand, listen: listenCommand2, send: sendCommand2 }
8205
- });
8206
-
8207
7506
  // src/commands/service.ts
8208
7507
  import { resolve as resolve2 } from "node:path";
8209
7508
 
@@ -8523,14 +7822,14 @@ import { randomUUID } from "node:crypto";
8523
7822
  import { query } from "@anthropic-ai/claude-agent-sdk";
8524
7823
 
8525
7824
  // src/session/runner/transcript.ts
8526
- import { readdirSync, readFileSync as readFileSync4, statSync } from "node:fs";
7825
+ import { readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
8527
7826
  import { homedir as homedir2 } from "node:os";
8528
- import { join as join7 } from "node:path";
7827
+ import { join as join5 } from "node:path";
8529
7828
  var findTranscriptPath = (agentSessionId) => {
8530
- const root = join7(homedir2(), ".claude", "projects");
7829
+ const root = join5(homedir2(), ".claude", "projects");
8531
7830
  try {
8532
7831
  for (const dir of readdirSync(root)) {
8533
- const candidate = join7(root, dir, `${agentSessionId}.jsonl`);
7832
+ const candidate = join5(root, dir, `${agentSessionId}.jsonl`);
8534
7833
  try {
8535
7834
  if (statSync(candidate).isFile()) return candidate;
8536
7835
  } catch {
@@ -8601,7 +7900,7 @@ var readFirstExchange = (agentSessionId) => {
8601
7900
  const path = findTranscriptPath(agentSessionId);
8602
7901
  if (!path) return null;
8603
7902
  try {
8604
- return parseFirstExchange(readFileSync4(path, "utf8"));
7903
+ return parseFirstExchange(readFileSync2(path, "utf8"));
8605
7904
  } catch {
8606
7905
  return null;
8607
7906
  }
@@ -8609,12 +7908,12 @@ var readFirstExchange = (agentSessionId) => {
8609
7908
 
8610
7909
  // src/session/runner/turn.ts
8611
7910
  import { mkdir as mkdir5, writeFile as writeFile3 } from "node:fs/promises";
8612
- import { join as join12 } from "node:path";
7911
+ import { join as join10 } from "node:path";
8613
7912
 
8614
7913
  // src/session/runner/attachments.ts
8615
7914
  import { createWriteStream } from "node:fs";
8616
7915
  import { mkdir as mkdir4 } from "node:fs/promises";
8617
- import { join as join8 } from "node:path";
7916
+ import { join as join6 } from "node:path";
8618
7917
  import { Readable } from "node:stream";
8619
7918
  import { pipeline } from "node:stream/promises";
8620
7919
  var dedupe = (name, taken) => {
@@ -8644,8 +7943,8 @@ var materializeAttachments = async (opts) => {
8644
7943
  const name = dedupe(att.filename, taken);
8645
7944
  const res = await fetchImpl(`${opts.serverBase}${att.url}`);
8646
7945
  if (!res.ok || !res.body) throw new Error(`attachment ${att.id} \u2192 ${res.status}`);
8647
- await pipeline(Readable.fromWeb(res.body), createWriteStream(join8(dir, name)));
8648
- paths.push(join8(dir, name));
7946
+ await pipeline(Readable.fromWeb(res.body), createWriteStream(join6(dir, name)));
7947
+ paths.push(join6(dir, name));
8649
7948
  }
8650
7949
  return paths;
8651
7950
  };
@@ -8661,7 +7960,7 @@ ${text}` : header;
8661
7960
  };
8662
7961
 
8663
7962
  // src/session/runner/codex.ts
8664
- import { join as join9 } from "node:path";
7963
+ import { join as join7 } from "node:path";
8665
7964
  import { Codex } from "@openai/codex-sdk";
8666
7965
 
8667
7966
  // src/session/runner/sdk-env.ts
@@ -8823,7 +8122,7 @@ async function* startCodexEvents(config, input, worker2, opts) {
8823
8122
  const env = compactEnv(buildSdkEnv(opts.envOverlay));
8824
8123
  const codex = new Codex(env ? { env } : void 0);
8825
8124
  const resuming = !config.agentSessionId.startsWith("pending:");
8826
- const addDirs = additionalDirs([join9(config.runtimeDir, "attachments")]);
8125
+ const addDirs = additionalDirs([join7(config.runtimeDir, "attachments")]);
8827
8126
  const sandboxMode = codexSandboxMode(opts.planMode ?? false);
8828
8127
  const approvalPolicy = codexApprovalPolicy();
8829
8128
  const networkAccessEnabled = codexNetworkAccess();
@@ -8864,7 +8163,7 @@ async function* startCodexEvents(config, input, worker2, opts) {
8864
8163
  // src/session/runner/pi.ts
8865
8164
  import { readFile as readFile2 } from "node:fs/promises";
8866
8165
  import { homedir as homedir3 } from "node:os";
8867
- import { join as join10 } from "node:path";
8166
+ import { join as join8 } from "node:path";
8868
8167
  var newPiTurnState = (startedAt = Date.now()) => ({
8869
8168
  startedAt,
8870
8169
  usage: {},
@@ -9009,7 +8308,7 @@ async function* startPiEvents(config, input, opts) {
9009
8308
  agentDir,
9010
8309
  // pi discovers .pi/ and .agents/ skills natively; Baton workers deploy theirs
9011
8310
  // under .claude/skills (project and user level).
9012
- additionalSkillPaths: [join10(config.cwd, ".claude/skills"), join10(homedir3(), ".claude/skills")]
8311
+ additionalSkillPaths: [join8(config.cwd, ".claude/skills"), join8(homedir3(), ".claude/skills")]
9013
8312
  });
9014
8313
  await resourceLoader.reload();
9015
8314
  const existing = pi.SessionManager.findById(config.cwd, config.agentSessionId);
@@ -9084,11 +8383,11 @@ async function* startPiEvents(config, input, opts) {
9084
8383
  }
9085
8384
 
9086
8385
  // src/session/runner/query.ts
9087
- import { join as join11 } from "node:path";
8386
+ import { join as join9 } from "node:path";
9088
8387
  var startQuery = (config, text, resuming, queryFn, abortController, log, { envOverlay, planMode = false, model, effort } = {}) => {
9089
8388
  const env = buildSdkEnv(envOverlay);
9090
8389
  const exe = claudeExecutable();
9091
- const addDirs = additionalDirs([join11(config.runtimeDir, "attachments")]);
8390
+ const addDirs = additionalDirs([join9(config.runtimeDir, "attachments")]);
9092
8391
  const level = claudeEffort(effort);
9093
8392
  if (addDirs) log(`[add-dir] ${addDirs.join(", ")}`);
9094
8393
  if (planMode) log("[plan] read-only planning turn");
@@ -9339,7 +8638,7 @@ var UnsettledExecution = class extends Error {
9339
8638
  var materializeImages = async (config, execution) => {
9340
8639
  const images = execution.message.payload.images ?? [];
9341
8640
  if (!images.length) return [];
9342
- const directory2 = join12(
8641
+ const directory2 = join10(
9343
8642
  config.runtimeDir,
9344
8643
  "attachments",
9345
8644
  `message-${execution.message.id}`,
@@ -9350,7 +8649,7 @@ var materializeImages = async (config, execution) => {
9350
8649
  images.map(async (image, index) => {
9351
8650
  const match = /^data:image\/(png|jpeg|webp|gif);base64,([A-Za-z0-9+/=\r\n]+)$/.exec(image);
9352
8651
  if (!match) throw new Error("unsupported pasted image");
9353
- const path = join12(directory2, `pasted-${index + 1}.${match[1]}`);
8652
+ const path = join10(directory2, `pasted-${index + 1}.${match[1]}`);
9354
8653
  await writeFile3(path, Buffer.from(match[2] ?? "", "base64"));
9355
8654
  return path;
9356
8655
  })
@@ -9359,7 +8658,7 @@ var materializeImages = async (config, execution) => {
9359
8658
  var buildPrompt = async (config, execution, fetchImpl) => {
9360
8659
  const { text: rawText, attachments = [] } = execution.message.payload;
9361
8660
  const paths = attachments.length ? await materializeAttachments({
9362
- directory: join12(
8661
+ directory: join10(
9363
8662
  config.runtimeDir,
9364
8663
  "attachments",
9365
8664
  `message-${execution.message.id}`,
@@ -9616,15 +8915,15 @@ var runDaemon = async (config, deps, signal) => {
9616
8915
  // src/worker/runtime-paths.ts
9617
8916
  import { createHash } from "node:crypto";
9618
8917
  import { homedir as homedir4 } from "node:os";
9619
- import { join as join13 } from "node:path";
9620
- var workerRuntimeDir = (owner, env = process.env) => join13(
9621
- env.XDG_DATA_HOME ?? join13(env.HOME ?? homedir4(), ".local/share"),
8918
+ import { join as join11 } from "node:path";
8919
+ var workerRuntimeDir = (owner, env = process.env) => join11(
8920
+ env.XDG_DATA_HOME ?? join11(env.HOME ?? homedir4(), ".local/share"),
9622
8921
  "baton",
9623
8922
  "runtime",
9624
8923
  createHash("sha256").update(owner.server).digest("hex"),
9625
8924
  `worker-${owner.workerId}`
9626
8925
  );
9627
- var sessionRuntimeDir = (owner, sessionId, env = process.env) => join13(workerRuntimeDir(owner, env), "sessions", String(sessionId));
8926
+ var sessionRuntimeDir = (owner, sessionId, env = process.env) => join11(workerRuntimeDir(owner, env), "sessions", String(sessionId));
9628
8927
 
9629
8928
  // src/commands/session/run.ts
9630
8929
  var bearerFetch = (token) => (input, init2) => fetch(input, {
@@ -9719,6 +9018,25 @@ var sessionRunCommand = defineCommand({
9719
9018
  }
9720
9019
  });
9721
9020
 
9021
+ // src/commands/attach.ts
9022
+ import { openAsBlob as openAsBlob2 } from "node:fs";
9023
+ import { basename as basename2 } from "node:path";
9024
+ var attachPaths = (raw) => (raw === void 0 ? [] : Array.isArray(raw) ? raw : [raw]).flatMap((p) => p.split(",")).map((p) => p.trim()).filter(Boolean);
9025
+ var uploadAttachments = async (client, sessionId, paths) => {
9026
+ const out = [];
9027
+ for (const path of paths) {
9028
+ const body = await openAsBlob2(path, { type: contentTypeForPath(path) });
9029
+ out.push(
9030
+ await client.sessions.uploadAttachment(sessionId, {
9031
+ filename: basename2(path),
9032
+ contentType: contentTypeForPath(path),
9033
+ body
9034
+ })
9035
+ );
9036
+ }
9037
+ return out;
9038
+ };
9039
+
9722
9040
  // src/commands/session/send.ts
9723
9041
  var sessionSendCommand = defineCommand({
9724
9042
  meta: { name: "send", description: "post a chat message into a session (no streaming reply)" },
@@ -9766,6 +9084,60 @@ var sessionStopCommand = defineCommand({
9766
9084
  }
9767
9085
  });
9768
9086
 
9087
+ // src/commands/session/tell.ts
9088
+ var TELL_LOOP_LIMIT = 10;
9089
+ var TELL_HEADER = /^\[Message from baton session #\d+ /gm;
9090
+ var renderTell = (from, body) => [
9091
+ `[Message from baton session #${from.id} ${JSON.stringify(from.name)}]`,
9092
+ `To reply, run: baton session tell ${from.id} "<your reply>"`,
9093
+ "Reply only if a response is actually needed.",
9094
+ "",
9095
+ body
9096
+ ].join("\n");
9097
+ var headerCount = (event) => {
9098
+ const text = event.payload?.text ?? "";
9099
+ return text.match(TELL_HEADER)?.length ?? 0;
9100
+ };
9101
+ var countConsecutiveTells = (events) => {
9102
+ const newestFirst = events.filter((event) => event.type === "user_message").map(headerCount).reverse();
9103
+ const end = newestFirst.indexOf(0);
9104
+ return (end < 0 ? newestFirst : newestFirst.slice(0, end)).reduce((sum, n) => sum + n, 0);
9105
+ };
9106
+ var sendTell = async (c, { fromId, toId, body }) => {
9107
+ if (body.trim() === "") throw new Error("nothing to send: message text is empty");
9108
+ if (toId === fromId) throw new Error("cannot tell your own session");
9109
+ const [from, events] = await Promise.all([c.sessions.get(fromId), c.sessions.listEvents(fromId)]);
9110
+ const received = countConsecutiveTells(events);
9111
+ if (received >= TELL_LOOP_LIMIT)
9112
+ throw new Error(
9113
+ `not sent: this session received ${received} agent messages in a row. Stop and ask a human; a message from them resets the count.`
9114
+ );
9115
+ return c.sessions.sendMessage(toId, renderTell(from, body));
9116
+ };
9117
+ var parseId = (raw) => raw !== void 0 && /^[1-9]\d*$/.test(raw) ? Number(raw) : null;
9118
+ var sessionTellCommand = defineCommand({
9119
+ meta: {
9120
+ name: "tell",
9121
+ description: "Send a message to another agent session in this workspace. It includes who sent it and how to reply, and wakes the target if needed. Delivery is fire-and-forget: end your turn, and any reply arrives as a new message. You cannot read another session; ask it instead."
9122
+ },
9123
+ args: {
9124
+ session: { type: "positional", required: true, description: "target session int id" },
9125
+ text: { type: "positional", required: true, description: "message text" },
9126
+ ...common
9127
+ },
9128
+ run: async ({ args }) => {
9129
+ const fromId = parseId(process.env.BATON_SESSION_ID);
9130
+ const bearer = process.env.BATON_WORKER_TOKEN;
9131
+ if (fromId === null || !bearer)
9132
+ throw new Error("baton session tell only works inside a baton session");
9133
+ const toId = parseId(args.session);
9134
+ if (toId === null) throw new Error(`invalid session "${args.session}": pass the session id`);
9135
+ const client = createClient(resolveBaseUrl(args.url), { bearer });
9136
+ const result = await sendTell(client, { fromId, toId, body: args.text });
9137
+ console.log(args.json ? toJson(result) : `told session #${toId} (input ${result.input.id})`);
9138
+ }
9139
+ });
9140
+
9769
9141
  // src/commands/session/terminal.ts
9770
9142
  var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
9771
9143
  var sessionTerminalCommand = defineCommand({
@@ -9814,6 +9186,7 @@ var session = defineCommand({
9814
9186
  rename: sessionRenameCommand,
9815
9187
  rm: sessionRmCommand,
9816
9188
  send: sessionSendCommand,
9189
+ tell: sessionTellCommand,
9817
9190
  ls: sessionLsCommand,
9818
9191
  get: sessionGetCommand,
9819
9192
  run: sessionRunCommand,
@@ -9827,12 +9200,12 @@ import { hostname as osHostname } from "node:os";
9827
9200
  // src/worker/bundled-skills.ts
9828
9201
  import { spawnSync as spawnSync2 } from "node:child_process";
9829
9202
  import { cpSync, existsSync as existsSync5 } from "node:fs";
9830
- import { dirname as dirname4, join as join14 } from "node:path";
9203
+ import { dirname as dirname4, join as join12 } from "node:path";
9831
9204
  import { fileURLToPath } from "node:url";
9832
9205
 
9833
9206
  // src/worker/git-exclude.ts
9834
9207
  import { spawnSync } from "node:child_process";
9835
- import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "node:fs";
9208
+ import { appendFileSync, existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3 } from "node:fs";
9836
9209
  import { dirname as dirname3, isAbsolute as isAbsolute3, resolve as resolve3 } from "node:path";
9837
9210
  var ensureExcluded = (cwd, pattern) => {
9838
9211
  const result = spawnSync("git", ["-C", cwd, "rev-parse", "--git-path", "info/exclude"], {
@@ -9846,9 +9219,9 @@ var ensureExcluded = (cwd, pattern) => {
9846
9219
  }
9847
9220
  const location = result.stdout.trim();
9848
9221
  const path = isAbsolute3(location) ? location : resolve3(cwd, location);
9849
- const current = existsSync4(path) ? readFileSync5(path, "utf8") : "";
9222
+ const current = existsSync4(path) ? readFileSync3(path, "utf8") : "";
9850
9223
  if (current.split("\n").includes(pattern)) return;
9851
- mkdirSync3(dirname3(path), { recursive: true });
9224
+ mkdirSync(dirname3(path), { recursive: true });
9852
9225
  appendFileSync(path, `${current && !current.endsWith("\n") ? "\n" : ""}${pattern}
9853
9226
  `);
9854
9227
  };
@@ -9859,8 +9232,8 @@ var TARGETS = [`.claude/skills/${SKILL_NAME}`, `.agents/skills/${SKILL_NAME}`];
9859
9232
  var bundledSkillPath = () => {
9860
9233
  const here = dirname4(fileURLToPath(import.meta.url));
9861
9234
  const candidates = [
9862
- join14(here, "..", "skills", SKILL_NAME),
9863
- join14(here, "..", "..", "skills", SKILL_NAME)
9235
+ join12(here, "..", "skills", SKILL_NAME),
9236
+ join12(here, "..", "..", "skills", SKILL_NAME)
9864
9237
  ];
9865
9238
  const found = candidates.find(existsSync5);
9866
9239
  if (!found) throw new Error(`bundled skill missing: ${SKILL_NAME}`);
@@ -9877,7 +9250,7 @@ var syncBundledArtifactSkill = (cwd) => {
9877
9250
  const source = bundledSkillPath();
9878
9251
  TARGETS.filter((target) => !isTracked(cwd, target)).forEach((target) => {
9879
9252
  ensureExcluded(cwd, `${target}/`);
9880
- cpSync(source, join14(cwd, target), { recursive: true, force: true });
9253
+ cpSync(source, join12(cwd, target), { recursive: true, force: true });
9881
9254
  });
9882
9255
  };
9883
9256
 
@@ -10148,7 +9521,7 @@ var openCommitDownload = async (repo, commit, path, signal) => {
10148
9521
  // src/worker/files/disk.ts
10149
9522
  import { constants as constants2 } from "node:fs";
10150
9523
  import { open as open2, readdir, realpath as realpath3, stat as stat3 } from "node:fs/promises";
10151
- import { isAbsolute as isAbsolute4, join as join15, relative, sep } from "node:path";
9524
+ import { isAbsolute as isAbsolute4, join as join13, relative, sep } from "node:path";
10152
9525
  import { Readable as Readable3 } from "node:stream";
10153
9526
  var isMissing = (error) => ["ENOENT", "ENOTDIR", "ELOOP"].includes(error.code ?? "");
10154
9527
  var childPath = (parent, name) => parent ? `${parent}/${name}` : name;
@@ -10161,7 +9534,7 @@ var resolveInside = async (root, path, allowRoot) => {
10161
9534
  const rel = checkedPath(path, allowRoot);
10162
9535
  let real;
10163
9536
  try {
10164
- real = await realpath3(join15(root, rel));
9537
+ real = await realpath3(join13(root, rel));
10165
9538
  } catch (error) {
10166
9539
  if (isMissing(error)) throw notFound(rel);
10167
9540
  throw error;
@@ -10191,7 +9564,7 @@ var toEntry = async (root, dir, dirent) => {
10191
9564
  if (dirent.isDirectory())
10192
9565
  return isExcludedDirectoryName(dirent.name) ? null : { name: dirent.name, path, kind: "directory" };
10193
9566
  if (dirent.isFile()) return { name: dirent.name, path, kind: "file" };
10194
- return dirent.isSymbolicLink() ? symlinkEntry(root, join15(dir.real, dirent.name), dirent.name, path) : null;
9567
+ return dirent.isSymbolicLink() ? symlinkEntry(root, join13(dir.real, dirent.name), dirent.name, path) : null;
10195
9568
  };
10196
9569
  var listDirectory = async (root, path) => {
10197
9570
  const dir = await resolveInside(root, path, true);
@@ -10214,7 +9587,7 @@ var searchFiles = async (root, query3, now = Date.now) => {
10214
9587
  const dir = pending[index] ?? "";
10215
9588
  let dirents;
10216
9589
  try {
10217
- dirents = await readdir(join15(root, dir), { withFileTypes: true });
9590
+ dirents = await readdir(join13(root, dir), { withFileTypes: true });
10218
9591
  } catch (error) {
10219
9592
  console.warn(`[files] search skipped unreadable directory ${dir || "."}`, error);
10220
9593
  continue;
@@ -10458,14 +9831,14 @@ var createLiveness = (deps) => {
10458
9831
  };
10459
9832
 
10460
9833
  // src/worker/service-supervisor.ts
10461
- import { mkdirSync as mkdirSync4 } from "node:fs";
9834
+ import { mkdirSync as mkdirSync2 } from "node:fs";
10462
9835
  import { stat as stat4 } from "node:fs/promises";
10463
- import { join as join17 } from "node:path";
9836
+ import { join as join15 } from "node:path";
10464
9837
 
10465
9838
  // src/worker/service-process.ts
10466
9839
  import { spawn as spawn3 } from "node:child_process";
10467
9840
  import { closeSync, existsSync as existsSync6, openSync } from "node:fs";
10468
- import { dirname as dirname5, join as join16 } from "node:path";
9841
+ import { dirname as dirname5, join as join14 } from "node:path";
10469
9842
  import { fileURLToPath as fileURLToPath2 } from "node:url";
10470
9843
  var START_TIMEOUT_MS = 1e4;
10471
9844
  var isWrapperMessage = (message) => {
@@ -10476,7 +9849,7 @@ var isWrapperMessage = (message) => {
10476
9849
  };
10477
9850
  var binPath = () => {
10478
9851
  const here = dirname5(fileURLToPath2(import.meta.url));
10479
- const devShim = join16(here, "..", "..", "bin", "baton.mjs");
9852
+ const devShim = join14(here, "..", "..", "bin", "baton.mjs");
10480
9853
  return existsSync6(devShim) ? devShim : fileURLToPath2(import.meta.url);
10481
9854
  };
10482
9855
  var startServiceProcess = (input) => {
@@ -10570,9 +9943,9 @@ var createServiceSupervisor = (deps) => {
10570
9943
  if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
10571
9944
  return report({ requestId, ok: false, error: `directory not found: ${cwd}`, status: 400 });
10572
9945
  }
10573
- const logDir = join17(workerRuntimeDir(cfg), "services");
10574
- const logPath = join17(logDir, `${name}.log`);
10575
- mkdirSync4(logDir, { recursive: true, mode: 448 });
9946
+ const logDir = join15(workerRuntimeDir(cfg), "services");
9947
+ const logPath = join15(logDir, `${name}.log`);
9948
+ mkdirSync2(logDir, { recursive: true, mode: 448 });
10576
9949
  if (entries.has(name))
10577
9950
  return report({
10578
9951
  requestId,
@@ -10637,7 +10010,7 @@ import { spawn as spawn4 } from "node:child_process";
10637
10010
  import { randomUUID as randomUUID2 } from "node:crypto";
10638
10011
  import { existsSync as existsSync7 } from "node:fs";
10639
10012
  import { mkdir as mkdir6, rm as rm3, stat as stat5 } from "node:fs/promises";
10640
- import { dirname as dirname6, join as join21 } from "node:path";
10013
+ import { dirname as dirname6, join as join19 } from "node:path";
10641
10014
  import { fileURLToPath as fileURLToPath3 } from "node:url";
10642
10015
  import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
10643
10016
 
@@ -10730,8 +10103,8 @@ var generateTitle = async (input) => {
10730
10103
 
10731
10104
  // src/session/runner/title-codex.ts
10732
10105
  import { mkdtemp, rm } from "node:fs/promises";
10733
- import { tmpdir as tmpdir3 } from "node:os";
10734
- import { join as join18 } from "node:path";
10106
+ import { tmpdir } from "node:os";
10107
+ import { join as join16 } from "node:path";
10735
10108
  import { Codex as Codex2 } from "@openai/codex-sdk";
10736
10109
  var generateTitleWithCodex = async (input) => {
10737
10110
  const { userText, assistantText } = input;
@@ -10740,7 +10113,7 @@ var generateTitleWithCodex = async (input) => {
10740
10113
  const timer = setTimeout(() => abort.abort(), TITLE_TIMEOUT_MS);
10741
10114
  let cwd = "";
10742
10115
  try {
10743
- cwd = await mkdtemp(join18(tmpdir3(), "baton-title-"));
10116
+ cwd = await mkdtemp(join16(tmpdir(), "baton-title-"));
10744
10117
  const client = input.client ?? new Codex2();
10745
10118
  const thread = client.startThread({
10746
10119
  workingDirectory: cwd,
@@ -10768,8 +10141,8 @@ var generateTitleWithCodex = async (input) => {
10768
10141
 
10769
10142
  // src/session/runner/title-pi.ts
10770
10143
  import { mkdtemp as mkdtemp2, rm as rm2 } from "node:fs/promises";
10771
- import { tmpdir as tmpdir4 } from "node:os";
10772
- import { join as join19 } from "node:path";
10144
+ import { tmpdir as tmpdir2 } from "node:os";
10145
+ import { join as join17 } from "node:path";
10773
10146
  var generateTitleWithPi = async (input) => {
10774
10147
  const { userText, assistantText } = input;
10775
10148
  if (`${userText}${assistantText}`.trim().length < 4) return { kind: "declined" };
@@ -10777,7 +10150,7 @@ var generateTitleWithPi = async (input) => {
10777
10150
  let timedOut = false;
10778
10151
  let timer;
10779
10152
  try {
10780
- cwd = await mkdtemp2(join19(tmpdir4(), "baton-title-"));
10153
+ cwd = await mkdtemp2(join17(tmpdir2(), "baton-title-"));
10781
10154
  const pi = await import("@earendil-works/pi-coding-agent");
10782
10155
  const agentDir = pi.getAgentDir();
10783
10156
  const resourceLoader = new pi.DefaultResourceLoader({
@@ -10829,11 +10202,11 @@ var generateTitleWithPi = async (input) => {
10829
10202
  // src/worker/session-process.ts
10830
10203
  import { execFile as execFile5 } from "node:child_process";
10831
10204
  import { readFile as readFile3, rename, writeFile as writeFile4 } from "node:fs/promises";
10832
- import { join as join20 } from "node:path";
10205
+ import { join as join18 } from "node:path";
10833
10206
  import { promisify as promisify5 } from "node:util";
10834
10207
  var run3 = promisify5(execFile5);
10835
10208
  var RUNNER_RECORD = ".baton-runner.json";
10836
- var recordPath = (cwd) => join20(cwd, RUNNER_RECORD);
10209
+ var recordPath = (cwd) => join18(cwd, RUNNER_RECORD);
10837
10210
  var codeOf = (error) => typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
10838
10211
  var readRunnerRecord = async (cwd) => {
10839
10212
  let raw;
@@ -10901,7 +10274,7 @@ var stopRecordedRunner = async (record, expected, graceMs = 5e3) => {
10901
10274
  // src/worker/session-supervisor.ts
10902
10275
  var binPath2 = () => {
10903
10276
  const here = dirname6(fileURLToPath3(import.meta.url));
10904
- const devShim = join21(here, "..", "..", "bin", "baton.mjs");
10277
+ const devShim = join19(here, "..", "..", "bin", "baton.mjs");
10905
10278
  return existsSync7(devShim) ? devShim : fileURLToPath3(import.meta.url);
10906
10279
  };
10907
10280
  var createSessionSupervisor = (deps) => {
@@ -11146,7 +10519,7 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
11146
10519
  import { existsSync as existsSync8, readdirSync as readdirSync2 } from "node:fs";
11147
10520
  import { createRequire as createModuleRequire } from "node:module";
11148
10521
  import { homedir as homedir5 } from "node:os";
11149
- import { dirname as dirname7, join as join22 } from "node:path";
10522
+ import { dirname as dirname7, join as join20 } from "node:path";
11150
10523
  import { fileURLToPath as fileURLToPath4 } from "node:url";
11151
10524
  var MAX_TERMINALS = 10;
11152
10525
  var claudePtyCommand = (agentSessionId, hasJsonl, env = process.env) => ({
@@ -11167,13 +10540,13 @@ var piPtyCommand = (agentSessionId, env = process.env) => {
11167
10540
  const override = env.BATON_PI_BIN?.trim();
11168
10541
  if (override) return { file: override, args };
11169
10542
  const entry = fileURLToPath4(import.meta.resolve("@earendil-works/pi-coding-agent"));
11170
- return { file: process.execPath, args: [join22(dirname7(entry), "bundle", "cli.js"), ...args] };
10543
+ return { file: process.execPath, args: [join20(dirname7(entry), "bundle", "cli.js"), ...args] };
11171
10544
  };
11172
10545
  var hasSessionJsonl = (agentSessionId) => {
11173
- const root = join22(homedir5(), ".claude", "projects");
10546
+ const root = join20(homedir5(), ".claude", "projects");
11174
10547
  if (!existsSync8(root)) return false;
11175
10548
  for (const dir of readdirSync2(root))
11176
- if (existsSync8(join22(root, dir, `${agentSessionId}.jsonl`))) return true;
10549
+ if (existsSync8(join20(root, dir, `${agentSessionId}.jsonl`))) return true;
11177
10550
  return false;
11178
10551
  };
11179
10552
  var ptyCommand = (kind, agentSessionId) => ({
@@ -11370,18 +10743,18 @@ var runWorkerDaemon = async (cfg, client, signal) => {
11370
10743
 
11371
10744
  // src/worker/machine-id.ts
11372
10745
  import { randomUUID as randomUUID3 } from "node:crypto";
11373
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
10746
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
11374
10747
  import { homedir as homedir6 } from "node:os";
11375
- import { dirname as dirname8, join as join23 } from "node:path";
11376
- var machineIdPath = (env = process.env) => join23(env.XDG_DATA_HOME ?? join23(env.HOME ?? homedir6(), ".local/share"), "baton", "machine-id");
10748
+ import { dirname as dirname8, join as join21 } from "node:path";
10749
+ var machineIdPath = (env = process.env) => join21(env.XDG_DATA_HOME ?? join21(env.HOME ?? homedir6(), ".local/share"), "baton", "machine-id");
11377
10750
  var readOrCreateMachineId = (path = machineIdPath()) => {
11378
10751
  if (existsSync9(path)) {
11379
- const s = readFileSync6(path, "utf8").trim();
10752
+ const s = readFileSync4(path, "utf8").trim();
11380
10753
  if (s) return s;
11381
10754
  }
11382
10755
  const id = randomUUID3();
11383
- mkdirSync5(dirname8(path), { recursive: true });
11384
- writeFileSync5(path, `${id}
10756
+ mkdirSync3(dirname8(path), { recursive: true });
10757
+ writeFileSync2(path, `${id}
11385
10758
  `, "utf8");
11386
10759
  return id;
11387
10760
  };
@@ -11765,8 +11138,6 @@ var main = defineCommand({
11765
11138
  artifact,
11766
11139
  loop,
11767
11140
  notify,
11768
- relay,
11769
- channel,
11770
11141
  workspace,
11771
11142
  project
11772
11143
  }