@ykurilov/agent-chat-voice 0.62.0 → 0.63.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ykurilov/agent-chat-voice
2
2
 
3
+ ## 0.63.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 0479393: Hono: обычный HTTP-запрос к адресу голоса (без подъёма WebSocket) снова получает сам отказ — 401 «не вошёл», 402
8
+ «лимит» и т. д., — а не 404. Код закрытия по-прежнему получает только браузер с WebSocket.
9
+ - @ykurilov/agent-chat-core@0.63.1
10
+
11
+ ## 0.63.0
12
+
13
+ ### Patch Changes
14
+
15
+ - @ykurilov/agent-chat-core@0.63.0
16
+
3
17
  ## 0.62.0
4
18
 
5
19
  ### Patch Changes
package/dist/hono.js CHANGED
@@ -5,7 +5,8 @@ function mountVoiceRelay(app, path, relay, upgradeWebSocket) {
5
5
  async (context, next) => {
6
6
  const access = await relay.authorize(context.req.raw);
7
7
  if ("refusal" in access) {
8
- if (access.closeCode === void 0) return access.refusal;
8
+ const upgrading = context.req.header("upgrade")?.toLowerCase() === "websocket";
9
+ if (access.closeCode === void 0 || !upgrading) return access.refusal;
9
10
  context.set("voiceCloseCode", access.closeCode);
10
11
  } else {
11
12
  context.set("voiceUser", access.user);
package/dist/hono.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hono.ts"],"sourcesContent":["import type { Context, Hono } from \"hono\";\nimport type { UpgradeWebSocket } from \"hono/ws\";\nimport type { RelayConnection, VoiceRelay } from \"./server/voice-relay\";\n\ntype VoiceRelayEnv = { Variables: { voiceUser: { id: string }; voiceChatId: string; voiceCloseCode: number | undefined } };\n\n/**\n * Подключает ретранслятор голоса к Hono: `GET {path}` поднимается до WebSocket.\n * `upgradeWebSocket` даёт адаптер вашего сервера, например `createNodeWebSocket` из `@hono/node-ws`.\n * Допуск решается до подъёма. Отказ с причиной (не вошёл, лимит, частота, сервис недоступен) браузер видит только кодом\n * закрытия: соединение поднимается и сразу закрывается этим кодом. Чужая страница — обычный HTTP-ответ 403.\n */\nexport function mountVoiceRelay(app: Hono, path: string, relay: VoiceRelay, upgradeWebSocket: UpgradeWebSocket): void {\n (app as unknown as Hono<VoiceRelayEnv>).get(\n path,\n async (context, next) => {\n const access = await relay.authorize(context.req.raw);\n if (\"refusal\" in access) {\n if (access.closeCode === undefined) return access.refusal;\n context.set(\"voiceCloseCode\", access.closeCode);\n } else {\n context.set(\"voiceUser\", access.user);\n context.set(\"voiceChatId\", access.chatId);\n }\n await next();\n },\n upgradeWebSocket((context: Context<VoiceRelayEnv>) => {\n const user = context.get(\"voiceUser\");\n const chatId = context.get(\"voiceChatId\");\n const closeCode = context.get(\"voiceCloseCode\");\n let connection: RelayConnection | undefined;\n // Куски звука и «конец звука» должны дойти по порядку, даже если платформа отдаёт их вразнобой (Blob).\n let queue: Promise<void> = Promise.resolve();\n return {\n onOpen(_event, socket) {\n if (closeCode !== undefined) return socket.close(closeCode);\n connection = relay.accept({ send: (data) => socket.send(data), close: (code, reason) => socket.close(code, reason) }, user, chatId);\n },\n onMessage(event) {\n const data = event.data as unknown;\n queue = queue.then(async () => {\n if (typeof data === \"string\") connection?.receive(data);\n else if (data instanceof Blob) connection?.receive(new Uint8Array(await data.arrayBuffer()));\n else if (ArrayBuffer.isView(data)) connection?.receive(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));\n else if (data instanceof ArrayBuffer) connection?.receive(data);\n });\n },\n onClose: () => connection?.closed(),\n onError: () => connection?.closed(),\n };\n }),\n );\n}\n"],"mappings":";AAYO,SAAS,gBAAgB,KAAW,MAAc,OAAmB,kBAA0C;AACpH,EAAC,IAAuC;AAAA,IACtC;AAAA,IACA,OAAO,SAAS,SAAS;AACvB,YAAM,SAAS,MAAM,MAAM,UAAU,QAAQ,IAAI,GAAG;AACpD,UAAI,aAAa,QAAQ;AACvB,YAAI,OAAO,cAAc,OAAW,QAAO,OAAO;AAClD,gBAAQ,IAAI,kBAAkB,OAAO,SAAS;AAAA,MAChD,OAAO;AACL,gBAAQ,IAAI,aAAa,OAAO,IAAI;AACpC,gBAAQ,IAAI,eAAe,OAAO,MAAM;AAAA,MAC1C;AACA,YAAM,KAAK;AAAA,IACb;AAAA,IACA,iBAAiB,CAAC,YAAoC;AACpD,YAAM,OAAO,QAAQ,IAAI,WAAW;AACpC,YAAM,SAAS,QAAQ,IAAI,aAAa;AACxC,YAAM,YAAY,QAAQ,IAAI,gBAAgB;AAC9C,UAAI;AAEJ,UAAI,QAAuB,QAAQ,QAAQ;AAC3C,aAAO;AAAA,QACL,OAAO,QAAQ,QAAQ;AACrB,cAAI,cAAc,OAAW,QAAO,OAAO,MAAM,SAAS;AAC1D,uBAAa,MAAM,OAAO,EAAE,MAAM,CAAC,SAAS,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,MAAM,WAAW,OAAO,MAAM,MAAM,MAAM,EAAE,GAAG,MAAM,MAAM;AAAA,QACpI;AAAA,QACA,UAAU,OAAO;AACf,gBAAM,OAAO,MAAM;AACnB,kBAAQ,MAAM,KAAK,YAAY;AAC7B,gBAAI,OAAO,SAAS,SAAU,aAAY,QAAQ,IAAI;AAAA,qBAC7C,gBAAgB,KAAM,aAAY,QAAQ,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC,CAAC;AAAA,qBAClF,YAAY,OAAO,IAAI,EAAG,aAAY,QAAQ,IAAI,WAAW,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC;AAAA,qBAC3G,gBAAgB,YAAa,aAAY,QAAQ,IAAI;AAAA,UAChE,CAAC;AAAA,QACH;AAAA,QACA,SAAS,MAAM,YAAY,OAAO;AAAA,QAClC,SAAS,MAAM,YAAY,OAAO;AAAA,MACpC;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/hono.ts"],"sourcesContent":["import type { Context, Hono } from \"hono\";\nimport type { UpgradeWebSocket } from \"hono/ws\";\nimport type { RelayConnection, VoiceRelay } from \"./server/voice-relay\";\n\ntype VoiceRelayEnv = { Variables: { voiceUser: { id: string }; voiceChatId: string; voiceCloseCode: number | undefined } };\n\n/**\n * Подключает ретранслятор голоса к Hono: `GET {path}` поднимается до WebSocket.\n * `upgradeWebSocket` даёт адаптер вашего сервера, например `createNodeWebSocket` из `@hono/node-ws`.\n * Допуск решается до подъёма. Отказ с причиной (не вошёл, лимит, частота, сервис недоступен) браузер видит только кодом\n * закрытия: соединение поднимается и сразу закрывается этим кодом. Чужая страница — обычный HTTP-ответ 403.\n */\nexport function mountVoiceRelay(app: Hono, path: string, relay: VoiceRelay, upgradeWebSocket: UpgradeWebSocket): void {\n (app as unknown as Hono<VoiceRelayEnv>).get(\n path,\n async (context, next) => {\n const access = await relay.authorize(context.req.raw);\n if (\"refusal\" in access) {\n // Код закрытия нужен только браузеру с WebSocket; обычному запросу — сам HTTP-отказ, а не «не найдено».\n const upgrading = context.req.header(\"upgrade\")?.toLowerCase() === \"websocket\";\n if (access.closeCode === undefined || !upgrading) return access.refusal;\n context.set(\"voiceCloseCode\", access.closeCode);\n } else {\n context.set(\"voiceUser\", access.user);\n context.set(\"voiceChatId\", access.chatId);\n }\n await next();\n },\n upgradeWebSocket((context: Context<VoiceRelayEnv>) => {\n const user = context.get(\"voiceUser\");\n const chatId = context.get(\"voiceChatId\");\n const closeCode = context.get(\"voiceCloseCode\");\n let connection: RelayConnection | undefined;\n // Куски звука и «конец звука» должны дойти по порядку, даже если платформа отдаёт их вразнобой (Blob).\n let queue: Promise<void> = Promise.resolve();\n return {\n onOpen(_event, socket) {\n if (closeCode !== undefined) return socket.close(closeCode);\n connection = relay.accept({ send: (data) => socket.send(data), close: (code, reason) => socket.close(code, reason) }, user, chatId);\n },\n onMessage(event) {\n const data = event.data as unknown;\n queue = queue.then(async () => {\n if (typeof data === \"string\") connection?.receive(data);\n else if (data instanceof Blob) connection?.receive(new Uint8Array(await data.arrayBuffer()));\n else if (ArrayBuffer.isView(data)) connection?.receive(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));\n else if (data instanceof ArrayBuffer) connection?.receive(data);\n });\n },\n onClose: () => connection?.closed(),\n onError: () => connection?.closed(),\n };\n }),\n );\n}\n"],"mappings":";AAYO,SAAS,gBAAgB,KAAW,MAAc,OAAmB,kBAA0C;AACpH,EAAC,IAAuC;AAAA,IACtC;AAAA,IACA,OAAO,SAAS,SAAS;AACvB,YAAM,SAAS,MAAM,MAAM,UAAU,QAAQ,IAAI,GAAG;AACpD,UAAI,aAAa,QAAQ;AAEvB,cAAM,YAAY,QAAQ,IAAI,OAAO,SAAS,GAAG,YAAY,MAAM;AACnE,YAAI,OAAO,cAAc,UAAa,CAAC,UAAW,QAAO,OAAO;AAChE,gBAAQ,IAAI,kBAAkB,OAAO,SAAS;AAAA,MAChD,OAAO;AACL,gBAAQ,IAAI,aAAa,OAAO,IAAI;AACpC,gBAAQ,IAAI,eAAe,OAAO,MAAM;AAAA,MAC1C;AACA,YAAM,KAAK;AAAA,IACb;AAAA,IACA,iBAAiB,CAAC,YAAoC;AACpD,YAAM,OAAO,QAAQ,IAAI,WAAW;AACpC,YAAM,SAAS,QAAQ,IAAI,aAAa;AACxC,YAAM,YAAY,QAAQ,IAAI,gBAAgB;AAC9C,UAAI;AAEJ,UAAI,QAAuB,QAAQ,QAAQ;AAC3C,aAAO;AAAA,QACL,OAAO,QAAQ,QAAQ;AACrB,cAAI,cAAc,OAAW,QAAO,OAAO,MAAM,SAAS;AAC1D,uBAAa,MAAM,OAAO,EAAE,MAAM,CAAC,SAAS,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,MAAM,WAAW,OAAO,MAAM,MAAM,MAAM,EAAE,GAAG,MAAM,MAAM;AAAA,QACpI;AAAA,QACA,UAAU,OAAO;AACf,gBAAM,OAAO,MAAM;AACnB,kBAAQ,MAAM,KAAK,YAAY;AAC7B,gBAAI,OAAO,SAAS,SAAU,aAAY,QAAQ,IAAI;AAAA,qBAC7C,gBAAgB,KAAM,aAAY,QAAQ,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC,CAAC;AAAA,qBAClF,YAAY,OAAO,IAAI,EAAG,aAAY,QAAQ,IAAI,WAAW,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC;AAAA,qBAC3G,gBAAgB,YAAa,aAAY,QAAQ,IAAI;AAAA,UAChE,CAAC;AAAA,QACH;AAAA,QACA,SAAS,MAAM,YAAY,OAAO;AAAA,QAClC,SAAS,MAAM,YAAY,OAAO;AAAA,MACpC;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ykurilov/agent-chat-voice",
3
- "version": "0.62.0",
3
+ "version": "0.63.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -44,7 +44,7 @@
44
44
  "directory": "packages/voice"
45
45
  },
46
46
  "dependencies": {
47
- "@ykurilov/agent-chat-core": "0.62.0"
47
+ "@ykurilov/agent-chat-core": "0.63.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@hono/node-server": "^2.1.1",
@@ -52,8 +52,8 @@
52
52
  "@types/ws": "^8.18.2",
53
53
  "hono": "^4.13.10",
54
54
  "ws": "^8.22.0",
55
- "@ykurilov/agent-chat-server": "0.62.0",
56
- "@ykurilov/agent-chat-react": "0.62.0"
55
+ "@ykurilov/agent-chat-react": "0.63.1",
56
+ "@ykurilov/agent-chat-server": "0.63.1"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "hono": "^4.13.10",