@bondsmith/bondsmithd 0.1.0 → 0.1.1

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/index.js +46 -29
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10,6 +10,9 @@ import { fileURLToPath } from "url";
10
10
  // ../../packages/local-agent-protocol/src/session.ts
11
11
  var SESSION_STATUSES = ["running", "exited"];
12
12
 
13
+ // ../../packages/local-agent-protocol/src/messages.ts
14
+ var REPLAYED_COMMAND_MESSAGE = "command already used (replay rejected)";
15
+
13
16
  // ../../packages/local-agent-protocol/src/schemas.ts
14
17
  import { z as z2 } from "zod";
15
18
 
@@ -991,7 +994,7 @@ function verifyRejectionMessage(reason) {
991
994
  case "expired":
992
995
  return "command envelope expired";
993
996
  case "replayed":
994
- return "command already used (replay rejected)";
997
+ return REPLAYED_COMMAND_MESSAGE;
995
998
  case "wrong_daemon":
996
999
  return "command not addressed to this daemon";
997
1000
  case "wrong_connection":
@@ -1005,6 +1008,7 @@ var BRACKETED_PASTE_START = "\x1B[200~";
1005
1008
  var BRACKETED_PASTE_END = "\x1B[201~";
1006
1009
  function createRouterConnection(connectionAuth, send, markControllerId, ptyAttachments) {
1007
1010
  const outputListeners = /* @__PURE__ */ new Map();
1011
+ const attachLocks = /* @__PURE__ */ new Map();
1008
1012
  const conn = {
1009
1013
  subscriptions: /* @__PURE__ */ new Set(),
1010
1014
  send,
@@ -1013,7 +1017,18 @@ function createRouterConnection(connectionAuth, send, markControllerId, ptyAttac
1013
1017
  },
1014
1018
  getOutputListener: (sessionId) => outputListeners.get(sessionId),
1015
1019
  connectionAuth,
1016
- markControllerId
1020
+ markControllerId,
1021
+ withAttachLock: (sessionId, fn) => {
1022
+ const previous = attachLocks.get(sessionId) ?? Promise.resolve();
1023
+ const next = previous.catch(() => {
1024
+ }).then(fn);
1025
+ attachLocks.set(
1026
+ sessionId,
1027
+ next.catch(() => {
1028
+ })
1029
+ );
1030
+ return next;
1031
+ }
1017
1032
  };
1018
1033
  function releaseAllSubscriptions() {
1019
1034
  for (const sessionId of conn.subscriptions) {
@@ -1117,36 +1132,38 @@ async function dispatch(conn, message, {
1117
1132
  }
1118
1133
  const session = getSessionOrError(sessionManager, conn, message.sessionId);
1119
1134
  if (!session) return;
1120
- const previousListener = conn.getOutputListener(message.sessionId);
1121
- const onData = (data) => {
1122
- conn.send({ type: "terminal.output", sessionId: message.sessionId, data });
1123
- };
1124
- if (previousListener && await ptyAttachments.swapListener(
1125
- message.sessionId,
1126
- session.tmuxSessionName,
1127
- previousListener,
1128
- onData
1129
- )) {
1135
+ await conn.withAttachLock(message.sessionId, async () => {
1136
+ const previousListener = conn.getOutputListener(message.sessionId);
1137
+ const onData = (data) => {
1138
+ conn.send({ type: "terminal.output", sessionId: message.sessionId, data });
1139
+ };
1140
+ if (previousListener && await ptyAttachments.swapListener(
1141
+ message.sessionId,
1142
+ session.tmuxSessionName,
1143
+ previousListener,
1144
+ onData
1145
+ )) {
1146
+ conn.subscriptions.add(message.sessionId);
1147
+ conn.registerOutputListener(message.sessionId, onData);
1148
+ conn.send({ type: "session.attached", sessionId: message.sessionId });
1149
+ return;
1150
+ }
1151
+ if (previousListener) {
1152
+ ptyAttachments.release(message.sessionId, previousListener);
1153
+ }
1154
+ const attached = await ptyAttachments.ensureAttached(
1155
+ message.sessionId,
1156
+ session.tmuxSessionName,
1157
+ onData
1158
+ );
1159
+ if (!attached) {
1160
+ conn.send(error("failed to attach to session"));
1161
+ return;
1162
+ }
1130
1163
  conn.subscriptions.add(message.sessionId);
1131
1164
  conn.registerOutputListener(message.sessionId, onData);
1132
1165
  conn.send({ type: "session.attached", sessionId: message.sessionId });
1133
- return;
1134
- }
1135
- if (previousListener) {
1136
- ptyAttachments.release(message.sessionId, previousListener);
1137
- }
1138
- const attached = await ptyAttachments.ensureAttached(
1139
- message.sessionId,
1140
- session.tmuxSessionName,
1141
- onData
1142
- );
1143
- if (!attached) {
1144
- conn.send(error("failed to attach to session"));
1145
- return;
1146
- }
1147
- conn.subscriptions.add(message.sessionId);
1148
- conn.registerOutputListener(message.sessionId, onData);
1149
- conn.send({ type: "session.attached", sessionId: message.sessionId });
1166
+ });
1150
1167
  return;
1151
1168
  }
1152
1169
  case "session.input": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bondsmith/bondsmithd",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "description": "Local daemon for Bondsmith agent sessions — runs on the user's own machine, pairs with a Bondsmith account, and exposes tmux-backed terminal sessions to the Bondsmith web app over a signed WebSocket protocol.",
6
6
  "homepage": "https://bondsmith.dev",