hivecode-mcp 0.14.0 → 0.14.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/hive-mux.js CHANGED
@@ -19,6 +19,7 @@ import * as decoding from 'lib0/decoding'
19
19
  import { ObservableV2 } from 'lib0/observable'
20
20
  import { WebsocketProvider } from 'y-websocket'
21
21
  import { F, encodeFrame, decodeFrame, muxPath, MUX_VERSION } from './hive-mux-proto.js'
22
+ import { relayFetch } from './hive-transport.js' // /health through the proxy, as the sockets go
22
23
 
23
24
  const messageSync = 0
24
25
 
@@ -257,7 +258,7 @@ class MuxConnection {
257
258
  * decided once -- HIVE_MUX=1 forces mux, HIVE_MUX=0 forces a socket per file, anything else asks
258
259
  * the relay's /health -- and file providers created before the answer are bound when it comes.
259
260
  */
260
- export function createFileTransport({ relay, room, wsOpts = {}, log = () => { }, env = process.env, fetchImpl = globalThis.fetch, onFallback = () => { }, retryMs = 1000, detectForMs = 90000 }) {
261
+ export function createFileTransport({ relay, room, wsOpts = {}, log = () => { }, env = process.env, fetchImpl = relayFetch, onFallback = () => { }, retryMs = 1000, detectForMs = 90000 }) {
261
262
  const forced = env.HIVE_MUX === '0' || env.HIVE_MUX === '1'
262
263
  let mode = env.HIVE_MUX === '0' ? 'socket' : env.HIVE_MUX === '1' ? 'mux' : null
263
264
  let mux = null
@@ -35,6 +35,8 @@
35
35
  // the only place the wording lives, and tests/relay-diagnosis-test.mjs COUNTS the producers so a
36
36
  // third copy fails a test rather than shipping.
37
37
 
38
+ import { relayFetch } from './hive-transport.js'
39
+
38
40
  /** A free-tier per-room cap. At or below this, Q431 is the likely refusal and seats are the fix. */
39
41
  export const FREE_TIER_ROOM_CAP = 8
40
42
 
@@ -67,7 +69,9 @@ export function healthUrlFor(relay) {
67
69
  export async function probeRelayHealth(relay, { timeoutMs = 2500, fetchImpl } = {}) {
68
70
  const url = healthUrlFor(relay)
69
71
  if (!url) return { ok: false, url: null, error: 'no usable relay URL' }
70
- const f = fetchImpl || (typeof fetch === 'function' ? fetch : null)
72
+ // relayFetch, not the built-in fetch: behind a proxy the built-in one cannot reach the relay at all,
73
+ // and this diagnosis would then blame the relay for what is this machine's network.
74
+ const f = fetchImpl || relayFetch
71
75
  if (!f) return { ok: false, url, error: 'no fetch available in this runtime' }
72
76
  try {
73
77
  const ac = new AbortController()
package/hive-room-key.js CHANGED
@@ -22,6 +22,7 @@ import path from 'path'
22
22
  import crypto from 'crypto'
23
23
  import { sign, decodeUnsafe, keyFingerprint, roomFingerprint, isSecuredRoom, makeSecuredRoomId } from './token.js'
24
24
  import { validateMemberName } from './hive-mailbox.js'
25
+ import { relayFetch } from './hive-transport.js' // through the proxy, as every call to the relay
25
26
 
26
27
  export const keysDir = () => process.env.HIVE_KEYS_DIR || path.join(os.homedir(), '.hivecode', 'keys')
27
28
  const keyFile = (room) => path.join(keysDir(), `${room}.json`)
@@ -143,7 +144,7 @@ export async function revokeInvite({ room, relay, name }) {
143
144
  const failures = []
144
145
  for (const i of live) {
145
146
  try {
146
- const r = await fetch(`${httpBase(relay || rec.relay)}/__hive/revoke`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ room, token: owner, jti: i.jti }) })
147
+ const r = await relayFetch(`${httpBase(relay || rec.relay)}/__hive/revoke`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ room, token: owner, jti: i.jti }) })
147
148
  const body = await r.json().catch(() => ({}))
148
149
  if (r.ok && body.ok) { i.revokedAt = Date.now(); done++; dropped += Number(body.dropped) || 0 }
149
150
  else failures.push(`HTTP ${r.status}${body.error ? ` ${body.error}` : ''}`)
package/hive-setup.js CHANGED
@@ -40,6 +40,7 @@ import { VERSION } from './hive-version.js'
40
40
  import { currentRelayFor, controlUrl, PUBLIC_DEFAULT_RELAY } from './hive-privacy.js'
41
41
  import { validateMemberName } from './hive-mailbox.js'
42
42
  import { installStopHook, installAntigravityStopHook } from './hive-hook-install.js'
43
+ import { relayFetch } from './hive-transport.js' // /health through the proxy, as every call to the relay
43
44
 
44
45
  export const SERVER_KEY = 'hivecode'
45
46
  const BACKUP_SUFFIX = '.before-hivecode'
@@ -340,11 +341,12 @@ function defaultName() {
340
341
  return v.ok ? cand : v.suggested
341
342
  }
342
343
 
343
- async function wakeRelay(relay) {
344
+ /** Exported for tests/proxy-fetch-test.mjs. */
345
+ export async function wakeRelay(relay) {
344
346
  const url = relay.replace(/^wss:/, 'https:').replace(/^ws:/, 'http:').replace(/\/+$/, '') + '/health'
345
347
  const t0 = Date.now()
346
348
  try {
347
- const r = await fetch(url, { signal: AbortSignal.timeout(75000) })
349
+ const r = await relayFetch(url, { signal: AbortSignal.timeout(75000) })
348
350
  return { ok: r.ok, ms: Date.now() - t0, status: r.status }
349
351
  } catch (e) { return { ok: false, ms: Date.now() - t0, error: e.name === 'TimeoutError' ? 'no answer in 75s' : e.message } }
350
352
  }
@@ -26,6 +26,9 @@
26
26
 
27
27
  import WebSocket from 'ws'
28
28
  import { startSync, parseLink } from './sync.js'
29
+ import { realpathSync as __hiveRealpath } from 'fs'
30
+ import { resolve as __hiveResolve } from 'path'
31
+ import { fileURLToPath as __hiveFileURLToPath } from 'url'
29
32
  import {
30
33
  makeQueue,
31
34
  slackPost,
@@ -39,6 +42,7 @@ import {
39
42
  preflightSocketMode,
40
43
  connectSocketMode,
41
44
  parseSlackAllow,
45
+ notFromAPerson,
42
46
  } from './hive-slack.js'
43
47
 
44
48
  export { stopAll, resumeAll, resolveName, route }
@@ -219,7 +223,7 @@ export function makeAgent({
219
223
  /** The one entry point for Slack events. Only a message in the configured channel or in a DM, or
220
224
  * the native stop button, does anything; everything else is ignored before any actor lookup. */
221
225
  async function onEvent(ev) {
222
- if (!ev || ev.bot_id || ev.subtype || (botUserId && ev.user === botUserId)) return { ignored: 'bot_or_subtype' }
226
+ if (notFromAPerson(ev) || (botUserId && ev.user === botUserId)) return { ignored: 'bot_or_subtype' }
223
227
  const targetChannel = ev.channel || channel
224
228
  const isDm = ev.channel_type === 'im' || (typeof targetChannel === 'string' && targetChannel.startsWith('D'))
225
229
  const here = targetChannel === channel || isDm
@@ -391,6 +395,14 @@ export async function main(argv = process.argv.slice(2), {
391
395
  })
392
396
  }
393
397
 
394
- if (process.argv[1] && import.meta.url === `file:///${process.argv[1].replace(/\\/g, '/')}`) {
398
+ // Run directly (node <file>, or through an npx / node_modules/.bin symlink)? Compared by real path:
399
+ // the old `file:///${argv[1]}` test built "file:////..." on macOS and Linux, never matched, and the
400
+ // process exited 0 having done nothing.
401
+ function __hiveIsEntry() {
402
+ if (!process.argv[1]) return false
403
+ const me = __hiveFileURLToPath(import.meta.url)
404
+ try { return __hiveRealpath(process.argv[1]) === __hiveRealpath(me) } catch { return __hiveResolve(process.argv[1]) === __hiveResolve(me) }
405
+ }
406
+ if (__hiveIsEntry()) {
395
407
  main().then((code) => { if (code !== 0) process.exit(code) })
396
408
  }