@hasna/bridge 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -143,8 +143,18 @@ bridge agents add aicopilot-main --kind aicopilot --profile aicopilot-main
143
143
 
144
144
  - `bridge_status`
145
145
  - `bridge_config`
146
+ - `bridge_session_list`
147
+ - `bridge_session_status`
148
+ - `bridge_session_create`
149
+ - `bridge_session_attach`
150
+ - `bridge_session_send`
151
+ - `bridge_session_route_message`
146
152
  - `bridge_route_message`
147
153
 
154
+ Use `bridge_session_route_message` for normal inbound channel behavior through
155
+ session bindings. `bridge_route_message` remains for compatibility with explicit
156
+ stateless routes.
157
+
148
158
  ## State
149
159
 
150
160
  Default config path:
@@ -234,6 +244,10 @@ If your Mac has more than one Messages account, add the account selector:
234
244
  bridge channels add-imessage imessage-main --allowed-handles +15555550100 --account you@example.com
235
245
  ```
236
246
 
247
+ For receive mode, configured accounts are also used as a filter when Messages
248
+ stores account metadata in `chat.db`. If an account is configured but an inbound
249
+ row has no matching account metadata, the row is skipped rather than routed.
250
+
237
251
  Enable local receive polling only when you are comfortable granting the daemon
238
252
  host access to Messages data:
239
253
 
package/dist/cli/index.js CHANGED
@@ -7137,17 +7137,57 @@ function imessageDateToIso(value) {
7137
7137
  function getIMessageDbPath(channel) {
7138
7138
  return channel.chatDbPath || defaultMessagesDbPath();
7139
7139
  }
7140
+ function tableColumns(db, table) {
7141
+ const rows = db.query(`pragma table_info(${table})`).all();
7142
+ return new Set(rows.map((row) => row.name).filter((name) => Boolean(name)));
7143
+ }
7144
+ function selectColumn(columns, table, column, alias) {
7145
+ return columns.has(column) ? `${table}.${column} as ${alias}` : `null as ${alias}`;
7146
+ }
7147
+ function normalizeIdentifier(value) {
7148
+ return value.trim().toLowerCase();
7149
+ }
7150
+ function valueMatchesConfigured(value, expected) {
7151
+ if (!value || !expected)
7152
+ return false;
7153
+ const normalizedValue = normalizeIdentifier(value);
7154
+ const normalizedExpected = normalizeIdentifier(expected);
7155
+ return normalizedValue === normalizedExpected || normalizedValue.endsWith(`:${normalizedExpected}`) || normalizedValue.endsWith(`;${normalizedExpected}`);
7156
+ }
7157
+ function rowMatchesAccount(channel, row) {
7158
+ if (!channel.account)
7159
+ return true;
7160
+ const rowCandidates = [row.account, row.accountGuid].filter(Boolean);
7161
+ const candidates = rowCandidates.length ? rowCandidates : [row.chatAccount].filter(Boolean);
7162
+ return candidates.some((value) => valueMatchesConfigured(value, channel.account));
7163
+ }
7164
+ function rowMatchesService(channel, row) {
7165
+ const expected = channel.serviceName || "iMessage";
7166
+ const candidates = row.service ? [row.service] : row.handleService ? [row.handleService] : row.chatService ? [row.chatService] : [];
7167
+ if (!candidates.length)
7168
+ return true;
7169
+ return candidates.some((value) => valueMatchesConfigured(value, expected));
7170
+ }
7140
7171
  function getIMessageMessages(channel, options = {}) {
7141
7172
  if ((channel.receiveMode || "disabled") !== "chat-db")
7142
7173
  return [];
7143
7174
  const db = new Database(getIMessageDbPath(channel), { readonly: true });
7144
7175
  try {
7176
+ const messageColumns = tableColumns(db, "message");
7177
+ const handleColumns = tableColumns(db, "handle");
7178
+ const chatColumns = tableColumns(db, "chat");
7145
7179
  const limit = options.limit || channel.pollLimit || 50;
7146
7180
  const scanLimit = Math.max(limit * 10, limit);
7147
7181
  const rows = db.query(`
7148
7182
  select
7149
7183
  message.ROWID as rowId,
7150
7184
  handle.id as handle,
7185
+ ${selectColumn(messageColumns, "message", "account", "account")},
7186
+ ${selectColumn(messageColumns, "message", "account_guid", "accountGuid")},
7187
+ ${selectColumn(messageColumns, "message", "service", "service")},
7188
+ ${selectColumn(handleColumns, "handle", "service", "handleService")},
7189
+ ${selectColumn(chatColumns, "chat", "account_login", "chatAccount")},
7190
+ ${selectColumn(chatColumns, "chat", "service_name", "chatService")},
7151
7191
  chat.guid as chatGuid,
7152
7192
  chat.display_name as displayName,
7153
7193
  message.text as text,
@@ -7162,8 +7202,14 @@ function getIMessageMessages(channel, options = {}) {
7162
7202
  order by message.ROWID asc
7163
7203
  limit ?
7164
7204
  `).all(options.afterRowId || 0, scanLimit);
7165
- return rows.filter((row) => row.handle && row.text && imessageHandleAllowed(channel, row.handle)).slice(0, limit).map((row) => {
7205
+ return rows.filter((row) => row.handle && row.text && imessageHandleAllowed(channel, row.handle) && rowMatchesAccount(channel, row) && rowMatchesService(channel, row)).slice(0, limit).map((row) => {
7166
7206
  const item = { rowId: row.rowId, handle: row.handle, text: row.text, date: row.date };
7207
+ if (row.account)
7208
+ item.account = row.account;
7209
+ if (row.accountGuid)
7210
+ item.accountGuid = row.accountGuid;
7211
+ if (row.service || row.handleService || row.chatService)
7212
+ item.service = row.service || row.handleService || row.chatService;
7167
7213
  if (row.chatGuid)
7168
7214
  item.chatGuid = row.chatGuid;
7169
7215
  if (row.displayName)
package/dist/index.js CHANGED
@@ -5106,17 +5106,57 @@ function imessageDateToIso(value) {
5106
5106
  function getIMessageDbPath(channel) {
5107
5107
  return channel.chatDbPath || defaultMessagesDbPath();
5108
5108
  }
5109
+ function tableColumns(db, table) {
5110
+ const rows = db.query(`pragma table_info(${table})`).all();
5111
+ return new Set(rows.map((row) => row.name).filter((name) => Boolean(name)));
5112
+ }
5113
+ function selectColumn(columns, table, column, alias) {
5114
+ return columns.has(column) ? `${table}.${column} as ${alias}` : `null as ${alias}`;
5115
+ }
5116
+ function normalizeIdentifier(value) {
5117
+ return value.trim().toLowerCase();
5118
+ }
5119
+ function valueMatchesConfigured(value, expected) {
5120
+ if (!value || !expected)
5121
+ return false;
5122
+ const normalizedValue = normalizeIdentifier(value);
5123
+ const normalizedExpected = normalizeIdentifier(expected);
5124
+ return normalizedValue === normalizedExpected || normalizedValue.endsWith(`:${normalizedExpected}`) || normalizedValue.endsWith(`;${normalizedExpected}`);
5125
+ }
5126
+ function rowMatchesAccount(channel, row) {
5127
+ if (!channel.account)
5128
+ return true;
5129
+ const rowCandidates = [row.account, row.accountGuid].filter(Boolean);
5130
+ const candidates = rowCandidates.length ? rowCandidates : [row.chatAccount].filter(Boolean);
5131
+ return candidates.some((value) => valueMatchesConfigured(value, channel.account));
5132
+ }
5133
+ function rowMatchesService(channel, row) {
5134
+ const expected = channel.serviceName || "iMessage";
5135
+ const candidates = row.service ? [row.service] : row.handleService ? [row.handleService] : row.chatService ? [row.chatService] : [];
5136
+ if (!candidates.length)
5137
+ return true;
5138
+ return candidates.some((value) => valueMatchesConfigured(value, expected));
5139
+ }
5109
5140
  function getIMessageMessages(channel, options = {}) {
5110
5141
  if ((channel.receiveMode || "disabled") !== "chat-db")
5111
5142
  return [];
5112
5143
  const db = new Database(getIMessageDbPath(channel), { readonly: true });
5113
5144
  try {
5145
+ const messageColumns = tableColumns(db, "message");
5146
+ const handleColumns = tableColumns(db, "handle");
5147
+ const chatColumns = tableColumns(db, "chat");
5114
5148
  const limit = options.limit || channel.pollLimit || 50;
5115
5149
  const scanLimit = Math.max(limit * 10, limit);
5116
5150
  const rows = db.query(`
5117
5151
  select
5118
5152
  message.ROWID as rowId,
5119
5153
  handle.id as handle,
5154
+ ${selectColumn(messageColumns, "message", "account", "account")},
5155
+ ${selectColumn(messageColumns, "message", "account_guid", "accountGuid")},
5156
+ ${selectColumn(messageColumns, "message", "service", "service")},
5157
+ ${selectColumn(handleColumns, "handle", "service", "handleService")},
5158
+ ${selectColumn(chatColumns, "chat", "account_login", "chatAccount")},
5159
+ ${selectColumn(chatColumns, "chat", "service_name", "chatService")},
5120
5160
  chat.guid as chatGuid,
5121
5161
  chat.display_name as displayName,
5122
5162
  message.text as text,
@@ -5131,8 +5171,14 @@ function getIMessageMessages(channel, options = {}) {
5131
5171
  order by message.ROWID asc
5132
5172
  limit ?
5133
5173
  `).all(options.afterRowId || 0, scanLimit);
5134
- return rows.filter((row) => row.handle && row.text && imessageHandleAllowed(channel, row.handle)).slice(0, limit).map((row) => {
5174
+ return rows.filter((row) => row.handle && row.text && imessageHandleAllowed(channel, row.handle) && rowMatchesAccount(channel, row) && rowMatchesService(channel, row)).slice(0, limit).map((row) => {
5135
5175
  const item = { rowId: row.rowId, handle: row.handle, text: row.text, date: row.date };
5176
+ if (row.account)
5177
+ item.account = row.account;
5178
+ if (row.accountGuid)
5179
+ item.accountGuid = row.accountGuid;
5180
+ if (row.service || row.handleService || row.chatService)
5181
+ item.service = row.service || row.handleService || row.chatService;
5136
5182
  if (row.chatGuid)
5137
5183
  item.chatGuid = row.chatGuid;
5138
5184
  if (row.displayName)
@@ -9,6 +9,9 @@ export interface IMessageRow {
9
9
  handle: string;
10
10
  chatGuid?: string;
11
11
  displayName?: string;
12
+ account?: string;
13
+ accountGuid?: string;
14
+ service?: string;
12
15
  text: string;
13
16
  date?: number;
14
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"imessage.d.ts","sourceRoot":"","sources":["../../src/lib/imessage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGxE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIzG;AAMD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAoB7G;AAYD,wBAAsB,YAAY,CAChC,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,CAAC,CAUvB;AAUD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAExE;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,WAAW,EAAE,CA2Cf;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,aAAa,CAWvF;AAUD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAmCpG"}
1
+ {"version":3,"file":"imessage.d.ts","sourceRoot":"","sources":["../../src/lib/imessage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGxE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIzG;AAMD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAoB7G;AAYD,wBAAsB,YAAY,CAChC,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,CAAC,CAUvB;AAUD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAExE;AAoDD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,WAAW,EAAE,CAmEf;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,aAAa,CAWvF;AAUD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAmCpG"}
package/dist/mcp/index.js CHANGED
@@ -5122,7 +5122,7 @@ function text(value) {
5122
5122
  return { content: [{ type: "text", text: typeof value === "string" ? value : JSON.stringify(value, null, 2) }] };
5123
5123
  }
5124
5124
  function buildServer() {
5125
- const server = new McpServer({ name: "bridge", version: "0.2.0" });
5125
+ const server = new McpServer({ name: "bridge", version: "0.2.1" });
5126
5126
  server.tool("bridge_status", {}, async () => text(await doctor()));
5127
5127
  server.tool("bridge_config", {}, async () => text(redactConfig(await loadConfig())));
5128
5128
  server.tool("bridge_session_list", {}, async () => text(listBridgeSessions(await loadState())));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/bridge",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Agent messaging bridge for Telegram and other channels",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",