@antelopejs/dms-ai 0.1.2 → 0.1.3
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 +49 -0
- package/dist/channels/bridge.d.ts +22 -0
- package/dist/channels/bridge.js +87 -0
- package/dist/channels/bridge.js.map +1 -0
- package/dist/channels/channel-service.d.ts +5 -0
- package/dist/channels/channel-service.js +68 -0
- package/dist/channels/channel-service.js.map +1 -0
- package/dist/channels/message-budget.d.ts +3 -0
- package/dist/channels/message-budget.js +29 -0
- package/dist/channels/message-budget.js.map +1 -0
- package/dist/channels/registry.d.ts +7 -0
- package/dist/channels/registry.js +47 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/sse-stream.d.ts +19 -0
- package/dist/channels/sse-stream.js +59 -0
- package/dist/channels/sse-stream.js.map +1 -0
- package/dist/chatbox/passthrough.d.ts +3 -0
- package/dist/chatbox/passthrough.js +53 -0
- package/dist/chatbox/passthrough.js.map +1 -0
- package/dist/constants/channels.d.ts +30 -0
- package/dist/constants/channels.js +38 -0
- package/dist/constants/channels.js.map +1 -0
- package/dist/constants/chatbox.d.ts +10 -0
- package/dist/constants/chatbox.js +35 -0
- package/dist/constants/chatbox.js.map +1 -0
- package/dist/constants/sidecar.d.ts +4 -0
- package/dist/constants/sidecar.js +3 -1
- package/dist/constants/sidecar.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/sidecar-socket.d.ts +2 -0
- package/dist/lifecycle/sidecar-socket.js +37 -0
- package/dist/lifecycle/sidecar-socket.js.map +1 -0
- package/dist/lifecycle/sidecar-unavailable-error.d.ts +3 -0
- package/dist/lifecycle/sidecar-unavailable-error.js +10 -0
- package/dist/lifecycle/sidecar-unavailable-error.js.map +1 -0
- package/dist/routes/channels.d.ts +8 -0
- package/dist/routes/channels.js +52 -0
- package/dist/routes/channels.js.map +1 -0
- package/dist/routes/chatbox.d.ts +7 -0
- package/dist/routes/chatbox.js +47 -0
- package/dist/routes/chatbox.js.map +1 -0
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.js +2 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/sidecar-info.js +0 -2
- package/dist/routes/sidecar-info.js.map +1 -1
- package/frontend-vue/app/components/SettingsView.vue +8 -0
- package/frontend-vue/app/plugins/ai.client.ts +137 -58
- package/frontend-vue/app/runtime/channel-client.ts +265 -0
- package/frontend-vue/app/runtime/chat-transport.ts +82 -0
- package/frontend-vue/app/runtime/constants.ts +30 -14
- package/frontend-vue/app/runtime/current-page.ts +9 -5
- package/frontend-vue/app/runtime/event-stream.ts +75 -0
- package/frontend-vue/app/runtime/overlay.ts +23 -6
- package/frontend-vue/app/runtime/sidecar-status.ts +10 -25
- package/frontend-vue/app/runtime/theme-bridge.ts +16 -4
- package/frontend-vue/package.json +3 -0
- package/frontend-vue/tests/backoff.test.ts +15 -15
- package/frontend-vue/tests/channel-client.test.ts +247 -0
- package/frontend-vue/tests/chat-transport.test.ts +67 -0
- package/frontend-vue/tests/sidecar-status.test.ts +39 -0
- package/package.json +5 -3
- package/sidecar/chatbox/dist/assets/index-C8u7mkHn.css +1 -0
- package/sidecar/chatbox/dist/assets/index-tdZvY7ee.js +52 -0
- package/sidecar/chatbox/dist/index.html +2 -2
- package/sidecar/dist/agent/disposals.d.ts +5 -0
- package/sidecar/dist/agent/disposals.js +17 -0
- package/sidecar/dist/agent/disposals.js.map +1 -0
- package/sidecar/dist/agent/effective-mode.d.ts +2 -0
- package/sidecar/dist/agent/effective-mode.js +20 -0
- package/sidecar/dist/agent/effective-mode.js.map +1 -0
- package/sidecar/dist/agent/provider.d.ts +2 -2
- package/sidecar/dist/agent/runner-events.d.ts +9 -1
- package/sidecar/dist/agent/runner.d.ts +2 -2
- package/sidecar/dist/agent/runner.js +11 -5
- package/sidecar/dist/agent/runner.js.map +1 -1
- package/sidecar/dist/agent/session.d.ts +3 -3
- package/sidecar/dist/agent/session.js +54 -34
- package/sidecar/dist/agent/session.js.map +1 -1
- package/sidecar/dist/agent/switching-runner.js +16 -14
- package/sidecar/dist/agent/switching-runner.js.map +1 -1
- package/sidecar/dist/agent/turn-end-reasons.d.ts +3 -0
- package/sidecar/dist/agent/turn-end-reasons.js +24 -0
- package/sidecar/dist/agent/turn-end-reasons.js.map +1 -0
- package/sidecar/dist/constants/agent.d.ts +6 -1
- package/sidecar/dist/constants/agent.js +13 -1
- package/sidecar/dist/constants/agent.js.map +1 -1
- package/sidecar/dist/constants/claude.d.ts +27 -1
- package/sidecar/dist/constants/claude.js +49 -0
- package/sidecar/dist/constants/claude.js.map +1 -1
- package/sidecar/dist/constants/codex.d.ts +6 -0
- package/sidecar/dist/constants/codex.js +13 -0
- package/sidecar/dist/constants/codex.js.map +1 -1
- package/sidecar/dist/constants/settings.js.map +1 -1
- package/sidecar/dist/constants/timing.d.ts +1 -0
- package/sidecar/dist/constants/timing.js +1 -0
- package/sidecar/dist/constants/timing.js.map +1 -1
- package/sidecar/dist/protocol/events.d.ts +36 -0
- package/sidecar/dist/protocol/events.js +13 -0
- package/sidecar/dist/protocol/events.js.map +1 -1
- package/sidecar/dist/providers/claude/activity.d.ts +5 -0
- package/sidecar/dist/providers/claude/activity.js +63 -0
- package/sidecar/dist/providers/claude/activity.js.map +1 -0
- package/sidecar/dist/providers/claude/adapter.js +21 -13
- package/sidecar/dist/providers/claude/adapter.js.map +1 -1
- package/sidecar/dist/providers/claude/config.d.ts +2 -1
- package/sidecar/dist/providers/claude/config.js +4 -0
- package/sidecar/dist/providers/claude/config.js.map +1 -1
- package/sidecar/dist/providers/claude/provider.js +18 -25
- package/sidecar/dist/providers/claude/provider.js.map +1 -1
- package/sidecar/dist/providers/claude/result.d.ts +3 -0
- package/sidecar/dist/providers/claude/result.js +61 -0
- package/sidecar/dist/providers/claude/result.js.map +1 -0
- package/sidecar/dist/providers/claude/safe-mode.d.ts +4 -0
- package/sidecar/dist/providers/claude/safe-mode.js +24 -0
- package/sidecar/dist/providers/claude/safe-mode.js.map +1 -0
- package/sidecar/dist/providers/codex/adapter.js +8 -2
- package/sidecar/dist/providers/codex/adapter.js.map +1 -1
- package/sidecar/dist/providers/codex/client.d.ts +1 -0
- package/sidecar/dist/providers/codex/client.js +1 -0
- package/sidecar/dist/providers/codex/client.js.map +1 -1
- package/sidecar/dist/providers/codex/config.js +2 -1
- package/sidecar/dist/providers/codex/config.js.map +1 -1
- package/sidecar/dist/providers/codex/process.js +113 -33
- package/sidecar/dist/providers/codex/process.js.map +1 -1
- package/sidecar/dist/providers/codex/provider.js +6 -4
- package/sidecar/dist/providers/codex/provider.js.map +1 -1
- package/sidecar/dist/server/routing.js +39 -21
- package/sidecar/dist/server/routing.js.map +1 -1
- package/sidecar/dist/server/turn-progress.d.ts +13 -0
- package/sidecar/dist/server/turn-progress.js +72 -0
- package/sidecar/dist/server/turn-progress.js.map +1 -0
- package/sidecar/dist/server/ws.js +4 -2
- package/sidecar/dist/server/ws.js.map +1 -1
- package/sidecar/dist/state/types.d.ts +2 -1
- package/sidecar/dist/state/types.js +1 -0
- package/sidecar/dist/state/types.js.map +1 -1
- package/sidecar/package.json +1 -1
- package/frontend-vue/app/runtime/ws-client.ts +0 -196
- package/frontend-vue/tests/sidecar-auth.test.ts +0 -87
- package/sidecar/chatbox/dist/assets/index-CEZ1NLp9.js +0 -52
- package/sidecar/chatbox/dist/assets/index-D_51Vqqk.css +0 -1
package/README.md
CHANGED
|
@@ -23,6 +23,36 @@ The module starts its bundled sidecar with the project as its working directory.
|
|
|
23
23
|
set `DMS_AI=0` before starting the backend to disable the sidecar. If
|
|
24
24
|
`@antelopejs/dms-builder` is installed, the assistant also exposes its builder integration.
|
|
25
25
|
|
|
26
|
+
The assistant is a development tool, on purpose: the backend and the sidecar refuse to run with
|
|
27
|
+
`NODE_ENV=production`, and the chat is reserved to owners. Remote *development* is supported (see
|
|
28
|
+
below); a hosted use would be a separate product with its own design.
|
|
29
|
+
|
|
30
|
+
## Remote access
|
|
31
|
+
|
|
32
|
+
The sidecar only listens on `127.0.0.1`, and the browser never talks to it: everything goes through
|
|
33
|
+
the DMS frontend server, over the same HTTP relay as the rest of the dashboard. The assistant
|
|
34
|
+
therefore works wherever the dashboard does — another machine on the LAN, Tailscale, an SSH tunnel to
|
|
35
|
+
the frontend port, a reverse proxy or a gateway — and the sidecar's port and client credential stay on
|
|
36
|
+
the server.
|
|
37
|
+
|
|
38
|
+
| Route (owner only) | Purpose |
|
|
39
|
+
| ------------------------------------------ | ------------------------------------------------------------------------ |
|
|
40
|
+
| `GET /ai/channels/host,chat/events` | One event stream per tab: the dashboard's and the chat's sidecar frames. |
|
|
41
|
+
| `POST /ai/channels/:connectionId/messages` | One client message, relayed to that channel's sidecar socket. |
|
|
42
|
+
| `GET /api/ai/chatbox/*` | The chat document and its assets, from the sidecar's static server. |
|
|
43
|
+
|
|
44
|
+
Browsers keep at most six HTTP/1.1 connections per origin, and every dashboard tab already holds two
|
|
45
|
+
long-lived ones for the DMS. The assistant adds its stream only in the visible tab whose panel is
|
|
46
|
+
open, which leaves two dashboard tabs usable side by side; over HTTPS with HTTP/2 the limit goes away.
|
|
47
|
+
Over plain HTTP on an address other than `localhost`, start the frontend server with
|
|
48
|
+
`DMS_COOKIE_SECURE=false`, or the browser drops the `Secure` session cookie.
|
|
49
|
+
|
|
50
|
+
The chat document is served from the dashboard's own origin and renders model output, so it runs
|
|
51
|
+
under a strict Content-Security-Policy: scripts from the bundle only, and no image, font or request
|
|
52
|
+
outside the origin (bar the Iconify API its icons come from). The frame is not sandboxed: without
|
|
53
|
+
`allow-same-origin` its own module scripts are refused, and with it a same-origin frame can lift the
|
|
54
|
+
sandbox anyway.
|
|
55
|
+
|
|
26
56
|
## Configuration
|
|
27
57
|
|
|
28
58
|
Both keys are optional. Left out, the sidecar keeps the defaults it uses when run standalone
|
|
@@ -72,6 +102,25 @@ may have picked it precisely so their code does not reach Anthropic.
|
|
|
72
102
|
Switching provider disposes the live sessions of the previous one — an open conversation loses its
|
|
73
103
|
in-agent context, its transcript is kept.
|
|
74
104
|
|
|
105
|
+
### Safe mode and permission modes
|
|
106
|
+
|
|
107
|
+
Safe mode, the default whenever `@antelopejs/dms-builder` is loaded, lets the agent change the
|
|
108
|
+
project only through the Builder tools. It holds whatever permission mode is selected:
|
|
109
|
+
|
|
110
|
+
- On Claude, a `PreToolUse` hook refuses `Write`, `Edit`, `MultiEdit`, `NotebookEdit` and `Bash`.
|
|
111
|
+
The CLI runs hooks before it applies the permission mode, whereas `acceptEdits` and
|
|
112
|
+
`bypassPermissions` approve those tools without consulting the permission prompt, and every
|
|
113
|
+
mode, *Normal* included, runs the shell commands the CLI deems read-only (`ls`, `wc`, `cat`,
|
|
114
|
+
`grep`, `git log`…) without asking. Reading, listing and searching stay available through
|
|
115
|
+
`Read`, `Glob` and `Grep`, allowed inside the workspace and asked outside it.
|
|
116
|
+
- On Codex, the sandbox stays read-only, without network, and every escalation is declined.
|
|
117
|
+
- Safe mode caps *Auto* at *Accept edits*: the sidecar stops approving prompts on its own, and the
|
|
118
|
+
Claude CLI never runs in `bypassPermissions`, so the prompts that remain (reads outside the
|
|
119
|
+
workspace, web access) still reach you.
|
|
120
|
+
|
|
121
|
+
A switch between safe and vibe mode applies from the next tool call on Claude and from the next
|
|
122
|
+
turn on Codex, without discarding the conversation.
|
|
123
|
+
|
|
75
124
|
### Enabling Codex
|
|
76
125
|
|
|
77
126
|
1. **Install the `codex` CLI.** It is an *optional* peer dependency, so it is never installed for
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import { type ChannelName } from "../constants/channels";
|
|
3
|
+
import type { SseStream } from "./sse-stream";
|
|
4
|
+
export interface ChannelBridge {
|
|
5
|
+
id: string;
|
|
6
|
+
userId: string;
|
|
7
|
+
channel: ChannelName;
|
|
8
|
+
forward: (message: Buffer) => Promise<boolean>;
|
|
9
|
+
close: () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface ChannelSocket {
|
|
12
|
+
channel: ChannelName;
|
|
13
|
+
socket: WebSocket;
|
|
14
|
+
}
|
|
15
|
+
export interface BridgeOptions {
|
|
16
|
+
userId: string;
|
|
17
|
+
sockets: readonly ChannelSocket[];
|
|
18
|
+
stream: SseStream;
|
|
19
|
+
onOpened: (bridge: ChannelBridge) => void;
|
|
20
|
+
onClosed: (bridge: ChannelBridge) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare function openBridges(options: BridgeOptions): ChannelBridge[];
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.openBridges = openBridges;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const ws_1 = __importDefault(require("ws"));
|
|
9
|
+
const channels_1 = require("../constants/channels");
|
|
10
|
+
const EMPTY_PAYLOAD = "{}";
|
|
11
|
+
function rawDataToText(data) {
|
|
12
|
+
if (Array.isArray(data))
|
|
13
|
+
return Buffer.concat(data).toString("utf8");
|
|
14
|
+
if (Buffer.isBuffer(data))
|
|
15
|
+
return data.toString("utf8");
|
|
16
|
+
return Buffer.from(new Uint8Array(data)).toString("utf8");
|
|
17
|
+
}
|
|
18
|
+
function forwardTo(socket, message) {
|
|
19
|
+
if (socket.readyState !== ws_1.default.OPEN)
|
|
20
|
+
return Promise.resolve(false);
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
socket.send(message, { binary: false }, (error) => resolve(!error));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function relayFrames(opened, stream) {
|
|
26
|
+
const { socket, bridge } = opened;
|
|
27
|
+
let isPaused = false;
|
|
28
|
+
socket.on("message", (data) => {
|
|
29
|
+
if (stream.send(bridge.channel, rawDataToText(data)))
|
|
30
|
+
return;
|
|
31
|
+
if (stream.isClosed() || isPaused)
|
|
32
|
+
return;
|
|
33
|
+
isPaused = true;
|
|
34
|
+
socket.pause();
|
|
35
|
+
stream.onDrain(() => {
|
|
36
|
+
isPaused = false;
|
|
37
|
+
socket.resume();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function announce(opened, stream) {
|
|
42
|
+
const connections = Object.fromEntries(opened.map(({ bridge }) => [bridge.channel, bridge.id]));
|
|
43
|
+
stream.send(channels_1.CHANNEL_EVENTS.READY, JSON.stringify({ connections }));
|
|
44
|
+
}
|
|
45
|
+
function openBridges(options) {
|
|
46
|
+
const { stream } = options;
|
|
47
|
+
let isOpen = true;
|
|
48
|
+
const teardown = () => {
|
|
49
|
+
if (!isOpen)
|
|
50
|
+
return;
|
|
51
|
+
isOpen = false;
|
|
52
|
+
stream.close();
|
|
53
|
+
for (const { bridge, socket } of opened) {
|
|
54
|
+
socket.close(channels_1.BRIDGE_CLOSE_CODE);
|
|
55
|
+
options.onClosed(bridge);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const opened = options.sockets.map(({ channel, socket }) => ({
|
|
59
|
+
socket,
|
|
60
|
+
bridge: {
|
|
61
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
62
|
+
userId: options.userId,
|
|
63
|
+
channel,
|
|
64
|
+
forward: (message) => forwardTo(socket, message),
|
|
65
|
+
close: teardown,
|
|
66
|
+
},
|
|
67
|
+
}));
|
|
68
|
+
for (const { bridge } of opened)
|
|
69
|
+
options.onOpened(bridge);
|
|
70
|
+
for (const { socket } of opened) {
|
|
71
|
+
socket.on("error", () => undefined);
|
|
72
|
+
socket.once("close", () => {
|
|
73
|
+
if (!isOpen)
|
|
74
|
+
return;
|
|
75
|
+
stream.send(channels_1.CHANNEL_EVENTS.SIDECAR_DOWN, EMPTY_PAYLOAD);
|
|
76
|
+
teardown();
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
stream.onClose(teardown);
|
|
80
|
+
for (const entry of opened)
|
|
81
|
+
relayFrames(entry, stream);
|
|
82
|
+
announce(opened, stream);
|
|
83
|
+
for (const { socket } of opened)
|
|
84
|
+
socket.resume();
|
|
85
|
+
return opened.map(({ bridge }) => bridge);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/channels/bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HTTPResult, type RequestContext } from "@antelopejs/interface-api";
|
|
2
|
+
import type WebSocket from "ws";
|
|
3
|
+
export type SidecarConnector = (path: string) => Promise<WebSocket>;
|
|
4
|
+
export declare function openChannel(ctx: RequestContext, channels: string, userId: string, connect?: SidecarConnector): Promise<HTTPResult | undefined>;
|
|
5
|
+
export declare function postChannelMessage(connectionId: string, userId: string, message: Buffer): Promise<HTTPResult>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.openChannel = openChannel;
|
|
4
|
+
exports.postChannelMessage = postChannelMessage;
|
|
5
|
+
const interface_api_1 = require("@antelopejs/interface-api");
|
|
6
|
+
const channels_1 = require("../constants/channels");
|
|
7
|
+
const sidecar_1 = require("../constants/sidecar");
|
|
8
|
+
const sidecar_socket_1 = require("../lifecycle/sidecar-socket");
|
|
9
|
+
const bridge_1 = require("./bridge");
|
|
10
|
+
const registry_1 = require("./registry");
|
|
11
|
+
const sse_stream_1 = require("./sse-stream");
|
|
12
|
+
const UNKNOWN_CHANNEL = { error: "unknown_channel" };
|
|
13
|
+
const UNKNOWN_CONNECTION = { error: "unknown_connection" };
|
|
14
|
+
const TOO_MANY_STREAMS = { error: "too_many_streams" };
|
|
15
|
+
function isChannelName(value) {
|
|
16
|
+
return Object.hasOwn(channels_1.CHANNEL_SIDECAR_PATHS, value);
|
|
17
|
+
}
|
|
18
|
+
function parseChannels(value) {
|
|
19
|
+
const names = value.split(channels_1.CHANNEL_LIST_SEPARATOR);
|
|
20
|
+
if (new Set(names).size !== names.length)
|
|
21
|
+
return null;
|
|
22
|
+
if (!names.every(isChannelName))
|
|
23
|
+
return null;
|
|
24
|
+
return names;
|
|
25
|
+
}
|
|
26
|
+
async function connectAll(connect, channels) {
|
|
27
|
+
const attempts = await Promise.allSettled(channels.map((channel) => connect(channels_1.CHANNEL_SIDECAR_PATHS[channel])));
|
|
28
|
+
const sockets = attempts.flatMap((attempt, index) => attempt.status === "fulfilled"
|
|
29
|
+
? [{ channel: channels[index], socket: attempt.value }]
|
|
30
|
+
: []);
|
|
31
|
+
if (sockets.length === channels.length)
|
|
32
|
+
return sockets;
|
|
33
|
+
for (const { socket } of sockets)
|
|
34
|
+
socket.terminate();
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
async function openChannel(ctx, channels, userId, connect = sidecar_socket_1.connectSidecarSocket) {
|
|
38
|
+
const names = parseChannels(channels);
|
|
39
|
+
if (names === null) {
|
|
40
|
+
return new interface_api_1.HTTPResult(channels_1.CHANNEL_STATUS.NOT_FOUND, UNKNOWN_CHANNEL);
|
|
41
|
+
}
|
|
42
|
+
if (!(0, registry_1.reserveSockets)(userId, names.length, channels_1.CHANNEL_MAX_SOCKETS_PER_USER)) {
|
|
43
|
+
return new interface_api_1.HTTPResult(channels_1.CHANNEL_STATUS.TOO_MANY_REQUESTS, TOO_MANY_STREAMS);
|
|
44
|
+
}
|
|
45
|
+
const sockets = await connectAll(connect, names).finally(() => (0, registry_1.releaseSockets)(userId, names.length));
|
|
46
|
+
if (sockets === null) {
|
|
47
|
+
return new interface_api_1.HTTPResult(channels_1.CHANNEL_STATUS.SERVICE_UNAVAILABLE, sidecar_1.SIDECAR_UNAVAILABLE_BODY);
|
|
48
|
+
}
|
|
49
|
+
(0, bridge_1.openBridges)({
|
|
50
|
+
userId,
|
|
51
|
+
sockets,
|
|
52
|
+
stream: (0, sse_stream_1.openSseStream)(ctx),
|
|
53
|
+
onOpened: registry_1.trackBridge,
|
|
54
|
+
onClosed: registry_1.forgetBridge,
|
|
55
|
+
});
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
async function postChannelMessage(connectionId, userId, message) {
|
|
59
|
+
const bridge = (0, registry_1.findOwnedBridge)(connectionId, userId);
|
|
60
|
+
if (bridge === undefined) {
|
|
61
|
+
return new interface_api_1.HTTPResult(channels_1.CHANNEL_STATUS.NOT_FOUND, UNKNOWN_CONNECTION);
|
|
62
|
+
}
|
|
63
|
+
if (!(await bridge.forward(message))) {
|
|
64
|
+
return new interface_api_1.HTTPResult(channels_1.CHANNEL_STATUS.SERVICE_UNAVAILABLE, sidecar_1.SIDECAR_UNAVAILABLE_BODY);
|
|
65
|
+
}
|
|
66
|
+
return new interface_api_1.HTTPResult(channels_1.CHANNEL_STATUS.ACCEPTED);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=channel-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-service.js","sourceRoot":"","sources":["../../src/channels/channel-service.ts"],"names":[],"mappings":";;AA6DA,kCA8BC;AAOD,gDAgBC;AAlHD,6DAA4E;AAE5E,oDAM+B;AAC/B,kDAAgE;AAChE,gEAAmE;AACnE,qCAA2D;AAC3D,yCAMoB;AACpB,6CAA6C;AAK7C,MAAM,eAAe,GAAG,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;AACrD,MAAM,kBAAkB,GAAG,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;AAC3D,MAAM,gBAAgB,GAAG,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;AAEvD,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,MAAM,CAAC,MAAM,CAAC,gCAAqB,EAAE,KAAK,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,iCAAsB,CAAC,CAAC;IAClD,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACtD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,OAAyB,EACzB,QAAgC;IAEhC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,UAAU,CACvC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,gCAAqB,CAAC,OAAO,CAAC,CAAC,CAAC,CACnE,CAAC;IACF,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAClD,OAAO,CAAC,MAAM,KAAK,WAAW;QAC5B,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QACvD,CAAC,CAAC,EAAE,CACP,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC;IACvD,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,OAAO;QAAE,MAAM,CAAC,SAAS,EAAE,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC;AAOM,KAAK,UAAU,WAAW,CAC/B,GAAmB,EACnB,QAAgB,EAChB,MAAc,EACd,UAA4B,qCAAoB;IAEhD,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,IAAI,0BAAU,CAAC,yBAAc,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,IAAA,yBAAc,EAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,uCAA4B,CAAC,EAAE,CAAC;QACxE,OAAO,IAAI,0BAAU,CAAC,yBAAc,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAC5D,IAAA,yBAAc,EAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CACrC,CAAC;IACF,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,IAAI,0BAAU,CACnB,yBAAc,CAAC,mBAAmB,EAClC,kCAAwB,CACzB,CAAC;IACJ,CAAC;IACD,IAAA,oBAAW,EAAC;QACV,MAAM;QACN,OAAO;QACP,MAAM,EAAE,IAAA,0BAAa,EAAC,GAAG,CAAC;QAC1B,QAAQ,EAAE,sBAAW;QACrB,QAAQ,EAAE,uBAAY;KACvB,CAAC,CAAC;IACH,OAAO,SAAS,CAAC;AACnB,CAAC;AAOM,KAAK,UAAU,kBAAkB,CACtC,YAAoB,EACpB,MAAc,EACd,OAAe;IAEf,MAAM,MAAM,GAAG,IAAA,0BAAe,EAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACrD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,IAAI,0BAAU,CAAC,yBAAc,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACrC,OAAO,IAAI,0BAAU,CACnB,yBAAc,CAAC,mBAAmB,EAClC,kCAAwB,CACzB,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,0BAAU,CAAC,yBAAc,CAAC,QAAQ,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MessageBudget = void 0;
|
|
4
|
+
exports.reserveMessageBytes = reserveMessageBytes;
|
|
5
|
+
const interface_api_1 = require("@antelopejs/interface-api");
|
|
6
|
+
const decorators_1 = require("@antelopejs/interface-core/decorators");
|
|
7
|
+
const channels_1 = require("../constants/channels");
|
|
8
|
+
const CONTENT_LENGTH_HEADER = "content-length";
|
|
9
|
+
const TOO_MANY_MESSAGES = { error: "too_many_messages" };
|
|
10
|
+
let pendingBytes = 0;
|
|
11
|
+
function declaredBytes(request) {
|
|
12
|
+
const declared = Number(request.headers[CONTENT_LENGTH_HEADER]);
|
|
13
|
+
if (!Number.isFinite(declared) || declared < 0) {
|
|
14
|
+
return channels_1.CHANNEL_MESSAGE_MAX_BYTES;
|
|
15
|
+
}
|
|
16
|
+
return Math.min(declared, channels_1.CHANNEL_MESSAGE_MAX_BYTES);
|
|
17
|
+
}
|
|
18
|
+
function reserveMessageBytes(context) {
|
|
19
|
+
const bytes = declaredBytes(context.rawRequest);
|
|
20
|
+
if (pendingBytes + bytes > channels_1.CHANNEL_PENDING_MESSAGES_MAX_BYTES) {
|
|
21
|
+
throw new interface_api_1.HTTPResult(channels_1.CHANNEL_STATUS.TOO_MANY_REQUESTS, TOO_MANY_MESSAGES);
|
|
22
|
+
}
|
|
23
|
+
pendingBytes += bytes;
|
|
24
|
+
context.rawResponse.once("close", () => {
|
|
25
|
+
pendingBytes -= bytes;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
exports.MessageBudget = (0, decorators_1.MakeParameterDecorator)((target, key, param) => (0, interface_api_1.SetParameterProvider)(target, key, param, reserveMessageBytes));
|
|
29
|
+
//# sourceMappingURL=message-budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-budget.js","sourceRoot":"","sources":["../../src/channels/message-budget.ts"],"names":[],"mappings":";;;AA8BA,kDASC;AAtCD,6DAImC;AACnC,sEAA+E;AAC/E,oDAI+B;AAE/B,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;AAC/C,MAAM,iBAAiB,GAAG,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;AAEzD,IAAI,YAAY,GAAG,CAAC,CAAC;AAErB,SAAS,aAAa,CAAC,OAAwB;IAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAChE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO,oCAAyB,CAAC;IACnC,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,oCAAyB,CAAC,CAAC;AACvD,CAAC;AAMD,SAAgB,mBAAmB,CAAC,OAAuB;IACzD,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,YAAY,GAAG,KAAK,GAAG,6CAAkC,EAAE,CAAC;QAC9D,MAAM,IAAI,0BAAU,CAAC,yBAAc,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;IAC5E,CAAC;IACD,YAAY,IAAI,KAAK,CAAC;IACtB,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;QACrC,YAAY,IAAI,KAAK,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC;AAMY,QAAA,aAAa,GAAG,IAAA,mCAAsB,EAAC,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CACzE,IAAA,oCAAoB,EAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAC9D,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ChannelBridge } from "./bridge";
|
|
2
|
+
export declare function reserveSockets(userId: string, count: number, limit: number): boolean;
|
|
3
|
+
export declare function releaseSockets(userId: string, count: number): void;
|
|
4
|
+
export declare function trackBridge(bridge: ChannelBridge): void;
|
|
5
|
+
export declare function forgetBridge(bridge: ChannelBridge): void;
|
|
6
|
+
export declare function findOwnedBridge(connectionId: string, userId: string): ChannelBridge | undefined;
|
|
7
|
+
export declare function closeAllBridges(): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reserveSockets = reserveSockets;
|
|
4
|
+
exports.releaseSockets = releaseSockets;
|
|
5
|
+
exports.trackBridge = trackBridge;
|
|
6
|
+
exports.forgetBridge = forgetBridge;
|
|
7
|
+
exports.findOwnedBridge = findOwnedBridge;
|
|
8
|
+
exports.closeAllBridges = closeAllBridges;
|
|
9
|
+
const bridges = new Map();
|
|
10
|
+
const pendingSockets = new Map();
|
|
11
|
+
function openSocketsOf(userId) {
|
|
12
|
+
let count = pendingSockets.get(userId) ?? 0;
|
|
13
|
+
for (const bridge of bridges.values()) {
|
|
14
|
+
if (bridge.userId === userId)
|
|
15
|
+
count += 1;
|
|
16
|
+
}
|
|
17
|
+
return count;
|
|
18
|
+
}
|
|
19
|
+
function reserveSockets(userId, count, limit) {
|
|
20
|
+
if (openSocketsOf(userId) + count > limit)
|
|
21
|
+
return false;
|
|
22
|
+
pendingSockets.set(userId, (pendingSockets.get(userId) ?? 0) + count);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
function releaseSockets(userId, count) {
|
|
26
|
+
const left = (pendingSockets.get(userId) ?? 0) - count;
|
|
27
|
+
if (left > 0)
|
|
28
|
+
pendingSockets.set(userId, left);
|
|
29
|
+
else
|
|
30
|
+
pendingSockets.delete(userId);
|
|
31
|
+
}
|
|
32
|
+
function trackBridge(bridge) {
|
|
33
|
+
bridges.set(bridge.id, bridge);
|
|
34
|
+
}
|
|
35
|
+
function forgetBridge(bridge) {
|
|
36
|
+
bridges.delete(bridge.id);
|
|
37
|
+
}
|
|
38
|
+
function findOwnedBridge(connectionId, userId) {
|
|
39
|
+
const bridge = bridges.get(connectionId);
|
|
40
|
+
return bridge?.userId === userId ? bridge : undefined;
|
|
41
|
+
}
|
|
42
|
+
function closeAllBridges() {
|
|
43
|
+
for (const bridge of bridges.values())
|
|
44
|
+
bridge.close();
|
|
45
|
+
bridges.clear();
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/channels/registry.ts"],"names":[],"mappings":";;AAcA,wCAQC;AAGD,wCAIC;AAED,kCAEC;AAED,oCAEC;AAGD,0CAMC;AAGD,0CAGC;AAlDD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;AACjD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEjD,SAAS,aAAa,CAAC,MAAc;IACnC,IAAI,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5C,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM;YAAE,KAAK,IAAI,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAGD,SAAgB,cAAc,CAC5B,MAAc,EACd,KAAa,EACb,KAAa;IAEb,IAAI,aAAa,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,KAAK;QAAE,OAAO,KAAK,CAAC;IACxD,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC;AACd,CAAC;AAGD,SAAgB,cAAc,CAAC,MAAc,EAAE,KAAa;IAC1D,MAAM,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC;IACvD,IAAI,IAAI,GAAG,CAAC;QAAE,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;;QAC1C,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC;AAED,SAAgB,WAAW,CAAC,MAAqB;IAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;AACjC,CAAC;AAED,SAAgB,YAAY,CAAC,MAAqB;IAChD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAGD,SAAgB,eAAe,CAC7B,YAAoB,EACpB,MAAc;IAEd,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzC,OAAO,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACxD,CAAC;AAGD,SAAgB,eAAe;IAC7B,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACtD,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RequestContext } from "@antelopejs/interface-api";
|
|
2
|
+
export interface SseSink {
|
|
3
|
+
write(chunk: string): boolean;
|
|
4
|
+
end(): void;
|
|
5
|
+
once(event: "drain", listener: () => void): unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface SseSource {
|
|
8
|
+
sink: SseSink;
|
|
9
|
+
onClientGone: (listener: () => void) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface SseStream {
|
|
12
|
+
send: (eventName: string, data: string) => boolean;
|
|
13
|
+
onDrain: (listener: () => void) => void;
|
|
14
|
+
onClose: (listener: () => void) => void;
|
|
15
|
+
close: () => void;
|
|
16
|
+
isClosed: () => boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function createSseStream(source: SseSource): SseStream;
|
|
19
|
+
export declare function openSseStream(ctx: RequestContext): SseStream;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSseStream = createSseStream;
|
|
4
|
+
exports.openSseStream = openSseStream;
|
|
5
|
+
const channels_1 = require("../constants/channels");
|
|
6
|
+
const LINE_BREAK = /\r\n|\r|\n/;
|
|
7
|
+
function formatEvent(eventName, data) {
|
|
8
|
+
const lines = data.split(LINE_BREAK).map((line) => `data: ${line}`);
|
|
9
|
+
return `event: ${eventName}\n${lines.join("\n")}\n\n`;
|
|
10
|
+
}
|
|
11
|
+
function createSseStream(source) {
|
|
12
|
+
const closeListeners = [];
|
|
13
|
+
let isClosed = false;
|
|
14
|
+
const keepalive = setInterval(() => {
|
|
15
|
+
source.sink.write(channels_1.SSE_KEEPALIVE_FRAME);
|
|
16
|
+
}, channels_1.SSE_KEEPALIVE_INTERVAL_MS);
|
|
17
|
+
keepalive.unref();
|
|
18
|
+
const finish = () => {
|
|
19
|
+
if (isClosed)
|
|
20
|
+
return;
|
|
21
|
+
isClosed = true;
|
|
22
|
+
clearInterval(keepalive);
|
|
23
|
+
for (const listener of closeListeners)
|
|
24
|
+
listener();
|
|
25
|
+
};
|
|
26
|
+
source.onClientGone(finish);
|
|
27
|
+
return {
|
|
28
|
+
send: (eventName, data) => !isClosed && source.sink.write(formatEvent(eventName, data)),
|
|
29
|
+
onDrain: (listener) => source.sink.once("drain", listener),
|
|
30
|
+
onClose: (listener) => {
|
|
31
|
+
if (isClosed)
|
|
32
|
+
listener();
|
|
33
|
+
else
|
|
34
|
+
closeListeners.push(listener);
|
|
35
|
+
},
|
|
36
|
+
close: () => {
|
|
37
|
+
if (isClosed)
|
|
38
|
+
return;
|
|
39
|
+
finish();
|
|
40
|
+
source.sink.end();
|
|
41
|
+
},
|
|
42
|
+
isClosed: () => isClosed,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function openSseStream(ctx) {
|
|
46
|
+
for (const [name, value] of Object.entries(channels_1.SSE_HEADERS)) {
|
|
47
|
+
ctx.response.addHeader(name, value);
|
|
48
|
+
}
|
|
49
|
+
return createSseStream({
|
|
50
|
+
sink: ctx.response.getWriteStream(channels_1.SSE_CONTENT_TYPE, channels_1.SSE_OK_STATUS),
|
|
51
|
+
onClientGone: (listener) => {
|
|
52
|
+
if (ctx.rawResponse.closed)
|
|
53
|
+
listener();
|
|
54
|
+
else
|
|
55
|
+
ctx.rawResponse.once("close", listener);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=sse-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-stream.js","sourceRoot":"","sources":["../../src/channels/sse-stream.ts"],"names":[],"mappings":";;AAwCA,0CA6BC;AAOD,sCAWC;AAtFD,oDAM+B;AAE/B,MAAM,UAAU,GAAG,YAAY,CAAC;AAyBhC,SAAS,WAAW,CAAC,SAAiB,EAAE,IAAY;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACpE,OAAO,UAAU,SAAS,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;AACxD,CAAC;AAGD,SAAgB,eAAe,CAAC,MAAiB;IAC/C,MAAM,cAAc,GAAsB,EAAE,CAAC;IAC7C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,8BAAmB,CAAC,CAAC;IACzC,CAAC,EAAE,oCAAyB,CAAC,CAAC;IAC9B,SAAS,CAAC,KAAK,EAAE,CAAC;IAClB,MAAM,MAAM,GAAG,GAAS,EAAE;QACxB,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,aAAa,CAAC,SAAS,CAAC,CAAC;QACzB,KAAK,MAAM,QAAQ,IAAI,cAAc;YAAE,QAAQ,EAAE,CAAC;IACpD,CAAC,CAAC;IACF,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO;QACL,IAAI,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CACxB,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAC9D,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC;QAC1D,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE;YACpB,IAAI,QAAQ;gBAAE,QAAQ,EAAE,CAAC;;gBACpB,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,EAAE,GAAG,EAAE;YACV,IAAI,QAAQ;gBAAE,OAAO;YACrB,MAAM,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ;KACzB,CAAC;AACJ,CAAC;AAOD,SAAgB,aAAa,CAAC,GAAmB;IAC/C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,sBAAW,CAAC,EAAE,CAAC;QACxD,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,eAAe,CAAC;QACrB,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,2BAAgB,EAAE,wBAAa,CAAC;QAClE,YAAY,EAAE,CAAC,QAAQ,EAAE,EAAE;YACzB,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM;gBAAE,QAAQ,EAAE,CAAC;;gBAClC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC/C,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { HTTPResult, type RequestContext } from "@antelopejs/interface-api";
|
|
2
|
+
export type ChatboxLoader = (path: string) => Promise<Response>;
|
|
3
|
+
export declare function relayChatboxFile(ctx: RequestContext, path: string, load?: ChatboxLoader): Promise<HTTPResult | undefined>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.relayChatboxFile = relayChatboxFile;
|
|
4
|
+
const node_stream_1 = require("node:stream");
|
|
5
|
+
const promises_1 = require("node:stream/promises");
|
|
6
|
+
const interface_api_1 = require("@antelopejs/interface-api");
|
|
7
|
+
const chatbox_1 = require("../constants/chatbox");
|
|
8
|
+
const sidecar_1 = require("../constants/sidecar");
|
|
9
|
+
const sidecar_unavailable_error_1 = require("../lifecycle/sidecar-unavailable-error");
|
|
10
|
+
const spawn_sidecar_1 = require("../lifecycle/spawn-sidecar");
|
|
11
|
+
async function loadFromSidecar(path) {
|
|
12
|
+
await (0, spawn_sidecar_1.ensureSidecarRunning)();
|
|
13
|
+
const port = (0, spawn_sidecar_1.getSidecarPort)();
|
|
14
|
+
if (port === null)
|
|
15
|
+
throw new sidecar_unavailable_error_1.SidecarUnavailableError();
|
|
16
|
+
return fetch(`http://${sidecar_1.SIDECAR_LOOPBACK_HOST}:${port}${path}`);
|
|
17
|
+
}
|
|
18
|
+
async function tryLoad(load, path) {
|
|
19
|
+
try {
|
|
20
|
+
return await load(path);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function applyHeaders(ctx, upstream) {
|
|
27
|
+
for (const [name, value] of Object.entries(chatbox_1.CHATBOX_RESPONSE_HEADERS)) {
|
|
28
|
+
ctx.response.addHeader(name, value);
|
|
29
|
+
}
|
|
30
|
+
for (const name of chatbox_1.CHATBOX_RELAYED_HEADERS) {
|
|
31
|
+
const value = upstream.headers.get(name);
|
|
32
|
+
if (value !== null)
|
|
33
|
+
ctx.response.addHeader(name, value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function relayChatboxFile(ctx, path, load = loadFromSidecar) {
|
|
37
|
+
const upstream = await tryLoad(load, path);
|
|
38
|
+
if (upstream === null) {
|
|
39
|
+
return new interface_api_1.HTTPResult(sidecar_1.SIDECAR_UNAVAILABLE_STATUS, sidecar_1.SIDECAR_UNAVAILABLE_BODY);
|
|
40
|
+
}
|
|
41
|
+
applyHeaders(ctx, upstream);
|
|
42
|
+
const contentType = upstream.headers.get(chatbox_1.CHATBOX_CONTENT_TYPE_HEADER) ??
|
|
43
|
+
chatbox_1.CHATBOX_DEFAULT_CONTENT_TYPE;
|
|
44
|
+
const sink = ctx.response.getWriteStream(contentType, upstream.status);
|
|
45
|
+
if (upstream.body === null) {
|
|
46
|
+
sink.end();
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
const body = node_stream_1.Readable.fromWeb(upstream.body);
|
|
50
|
+
void (0, promises_1.pipeline)(body, sink).catch(() => sink.destroy());
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=passthrough.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passthrough.js","sourceRoot":"","sources":["../../src/chatbox/passthrough.ts"],"names":[],"mappings":";;AAyDA,4CAqBC;AA9ED,6CAAuC;AACvC,mDAAgD;AAEhD,6DAA4E;AAC5E,kDAK8B;AAC9B,kDAI8B;AAC9B,sFAAiF;AACjF,8DAGoC;AAKpC,KAAK,UAAU,eAAe,CAAC,IAAY;IACzC,MAAM,IAAA,oCAAoB,GAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAA,8BAAc,GAAE,CAAC;IAC9B,IAAI,IAAI,KAAK,IAAI;QAAE,MAAM,IAAI,mDAAuB,EAAE,CAAC;IACvD,OAAO,KAAK,CAAC,UAAU,+BAAqB,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,IAAmB,EACnB,IAAY;IAEZ,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,GAAmB,EAAE,QAAkB;IAC3D,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kCAAwB,CAAC,EAAE,CAAC;QACrE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,iCAAuB,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,IAAI;YAAE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAOM,KAAK,UAAU,gBAAgB,CACpC,GAAmB,EACnB,IAAY,EACZ,OAAsB,eAAe;IAErC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC3C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,IAAI,0BAAU,CAAC,oCAA0B,EAAE,kCAAwB,CAAC,CAAC;IAC9E,CAAC;IACD,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC5B,MAAM,WAAW,GACf,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qCAA2B,CAAC;QACjD,sCAA4B,CAAC;IAC/B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvE,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,IAAI,GAAG,sBAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAqC,CAAC,CAAC;IAC9E,KAAK,IAAA,mBAAQ,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACtD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const CHANNELS_ROUTE_PREFIX = "/ai/channels";
|
|
2
|
+
export declare const CHANNEL_EVENTS_ROUTE = "/:channels/events";
|
|
3
|
+
export declare const CHANNEL_MESSAGES_ROUTE = "/:connectionId/messages";
|
|
4
|
+
export declare const CHANNELS_PARAM = "channels";
|
|
5
|
+
export declare const CONNECTION_ID_PARAM = "connectionId";
|
|
6
|
+
export declare const CHANNEL_LIST_SEPARATOR = ",";
|
|
7
|
+
export declare const CHANNEL_SIDECAR_PATHS: {
|
|
8
|
+
readonly host: "/ws/host";
|
|
9
|
+
readonly chat: "/ws/iframe";
|
|
10
|
+
};
|
|
11
|
+
export type ChannelName = keyof typeof CHANNEL_SIDECAR_PATHS;
|
|
12
|
+
export declare const CHANNEL_EVENTS: {
|
|
13
|
+
readonly READY: "ready";
|
|
14
|
+
readonly SIDECAR_DOWN: "sidecar_down";
|
|
15
|
+
};
|
|
16
|
+
export declare const SSE_CONTENT_TYPE = "text/event-stream";
|
|
17
|
+
export declare const SSE_OK_STATUS = 200;
|
|
18
|
+
export declare const SSE_KEEPALIVE_INTERVAL_MS = 15000;
|
|
19
|
+
export declare const SSE_KEEPALIVE_FRAME = ": keepalive\n\n";
|
|
20
|
+
export declare const SSE_HEADERS: Readonly<Record<string, string>>;
|
|
21
|
+
export declare const CHANNEL_MESSAGE_MAX_BYTES: number;
|
|
22
|
+
export declare const CHANNEL_MAX_SOCKETS_PER_USER = 16;
|
|
23
|
+
export declare const CHANNEL_PENDING_MESSAGES_MAX_BYTES: number;
|
|
24
|
+
export declare const CHANNEL_STATUS: {
|
|
25
|
+
readonly ACCEPTED: 204;
|
|
26
|
+
readonly NOT_FOUND: 404;
|
|
27
|
+
readonly TOO_MANY_REQUESTS: 429;
|
|
28
|
+
readonly SERVICE_UNAVAILABLE: 503;
|
|
29
|
+
};
|
|
30
|
+
export declare const BRIDGE_CLOSE_CODE = 1001;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BRIDGE_CLOSE_CODE = exports.CHANNEL_STATUS = exports.CHANNEL_PENDING_MESSAGES_MAX_BYTES = exports.CHANNEL_MAX_SOCKETS_PER_USER = exports.CHANNEL_MESSAGE_MAX_BYTES = exports.SSE_HEADERS = exports.SSE_KEEPALIVE_FRAME = exports.SSE_KEEPALIVE_INTERVAL_MS = exports.SSE_OK_STATUS = exports.SSE_CONTENT_TYPE = exports.CHANNEL_EVENTS = exports.CHANNEL_SIDECAR_PATHS = exports.CHANNEL_LIST_SEPARATOR = exports.CONNECTION_ID_PARAM = exports.CHANNELS_PARAM = exports.CHANNEL_MESSAGES_ROUTE = exports.CHANNEL_EVENTS_ROUTE = exports.CHANNELS_ROUTE_PREFIX = void 0;
|
|
4
|
+
const sidecar_1 = require("./sidecar");
|
|
5
|
+
exports.CHANNELS_ROUTE_PREFIX = "/ai/channels";
|
|
6
|
+
exports.CHANNEL_EVENTS_ROUTE = "/:channels/events";
|
|
7
|
+
exports.CHANNEL_MESSAGES_ROUTE = "/:connectionId/messages";
|
|
8
|
+
exports.CHANNELS_PARAM = "channels";
|
|
9
|
+
exports.CONNECTION_ID_PARAM = "connectionId";
|
|
10
|
+
exports.CHANNEL_LIST_SEPARATOR = ",";
|
|
11
|
+
exports.CHANNEL_SIDECAR_PATHS = {
|
|
12
|
+
host: "/ws/host",
|
|
13
|
+
chat: "/ws/iframe",
|
|
14
|
+
};
|
|
15
|
+
exports.CHANNEL_EVENTS = {
|
|
16
|
+
READY: "ready",
|
|
17
|
+
SIDECAR_DOWN: "sidecar_down",
|
|
18
|
+
};
|
|
19
|
+
exports.SSE_CONTENT_TYPE = "text/event-stream";
|
|
20
|
+
exports.SSE_OK_STATUS = 200;
|
|
21
|
+
exports.SSE_KEEPALIVE_INTERVAL_MS = 15_000;
|
|
22
|
+
exports.SSE_KEEPALIVE_FRAME = ": keepalive\n\n";
|
|
23
|
+
exports.SSE_HEADERS = {
|
|
24
|
+
"Cache-Control": "no-store, no-transform",
|
|
25
|
+
"X-Accel-Buffering": "no",
|
|
26
|
+
Connection: "keep-alive",
|
|
27
|
+
};
|
|
28
|
+
exports.CHANNEL_MESSAGE_MAX_BYTES = 384 * 1024 * 1024;
|
|
29
|
+
exports.CHANNEL_MAX_SOCKETS_PER_USER = 16;
|
|
30
|
+
exports.CHANNEL_PENDING_MESSAGES_MAX_BYTES = 2 * exports.CHANNEL_MESSAGE_MAX_BYTES;
|
|
31
|
+
exports.CHANNEL_STATUS = {
|
|
32
|
+
ACCEPTED: 204,
|
|
33
|
+
NOT_FOUND: 404,
|
|
34
|
+
TOO_MANY_REQUESTS: 429,
|
|
35
|
+
SERVICE_UNAVAILABLE: sidecar_1.SIDECAR_UNAVAILABLE_STATUS,
|
|
36
|
+
};
|
|
37
|
+
exports.BRIDGE_CLOSE_CODE = 1001;
|
|
38
|
+
//# sourceMappingURL=channels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channels.js","sourceRoot":"","sources":["../../src/constants/channels.ts"],"names":[],"mappings":";;;AAAA,uCAAuD;AAG1C,QAAA,qBAAqB,GAAG,cAAc,CAAC;AACvC,QAAA,oBAAoB,GAAG,mBAAmB,CAAC;AAC3C,QAAA,sBAAsB,GAAG,yBAAyB,CAAC;AACnD,QAAA,cAAc,GAAG,UAAU,CAAC;AAC5B,QAAA,mBAAmB,GAAG,cAAc,CAAC;AAOrC,QAAA,sBAAsB,GAAG,GAAG,CAAC;AAM7B,QAAA,qBAAqB,GAAG;IACnC,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,YAAY;CACV,CAAC;AAIE,QAAA,cAAc,GAAG;IAC5B,KAAK,EAAE,OAAO;IACd,YAAY,EAAE,cAAc;CACpB,CAAC;AAEE,QAAA,gBAAgB,GAAG,mBAAmB,CAAC;AACvC,QAAA,aAAa,GAAG,GAAG,CAAC;AACpB,QAAA,yBAAyB,GAAG,MAAM,CAAC;AACnC,QAAA,mBAAmB,GAAG,iBAAiB,CAAC;AACxC,QAAA,WAAW,GAAqC;IAC3D,eAAe,EAAE,wBAAwB;IACzC,mBAAmB,EAAE,IAAI;IACzB,UAAU,EAAE,YAAY;CACzB,CAAC;AAOW,QAAA,yBAAyB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAG9C,QAAA,4BAA4B,GAAG,EAAE,CAAC;AAOlC,QAAA,kCAAkC,GAAG,CAAC,GAAG,iCAAyB,CAAC;AAEnE,QAAA,cAAc,GAAG;IAC5B,QAAQ,EAAE,GAAG;IACb,SAAS,EAAE,GAAG;IACd,iBAAiB,EAAE,GAAG;IACtB,mBAAmB,EAAE,oCAA0B;CACvC,CAAC;AAGE,QAAA,iBAAiB,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const CHATBOX_ROUTE_PREFIX = "/api/ai/chatbox";
|
|
2
|
+
export declare const CHATBOX_INDEX_ROUTE = "/";
|
|
3
|
+
export declare const CHATBOX_ASSET_ROUTE = "::path";
|
|
4
|
+
export declare const CHATBOX_PATH_PARAM = "path";
|
|
5
|
+
export declare const CHATBOX_INDEX_PATH = "/";
|
|
6
|
+
export declare const CHATBOX_DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
7
|
+
export declare const CHATBOX_CONTENT_TYPE_HEADER = "content-type";
|
|
8
|
+
export declare const CHATBOX_RELAYED_HEADERS: readonly ["content-length"];
|
|
9
|
+
export declare const CHATBOX_CONTENT_SECURITY_POLICY: string;
|
|
10
|
+
export declare const CHATBOX_RESPONSE_HEADERS: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CHATBOX_RESPONSE_HEADERS = exports.CHATBOX_CONTENT_SECURITY_POLICY = exports.CHATBOX_RELAYED_HEADERS = exports.CHATBOX_CONTENT_TYPE_HEADER = exports.CHATBOX_DEFAULT_CONTENT_TYPE = exports.CHATBOX_INDEX_PATH = exports.CHATBOX_PATH_PARAM = exports.CHATBOX_ASSET_ROUTE = exports.CHATBOX_INDEX_ROUTE = exports.CHATBOX_ROUTE_PREFIX = void 0;
|
|
4
|
+
exports.CHATBOX_ROUTE_PREFIX = "/api/ai/chatbox";
|
|
5
|
+
exports.CHATBOX_INDEX_ROUTE = "/";
|
|
6
|
+
exports.CHATBOX_ASSET_ROUTE = "::path";
|
|
7
|
+
exports.CHATBOX_PATH_PARAM = "path";
|
|
8
|
+
exports.CHATBOX_INDEX_PATH = "/";
|
|
9
|
+
exports.CHATBOX_DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
10
|
+
exports.CHATBOX_CONTENT_TYPE_HEADER = "content-type";
|
|
11
|
+
exports.CHATBOX_RELAYED_HEADERS = ["content-length"];
|
|
12
|
+
const ICONIFY_API_ORIGINS = [
|
|
13
|
+
"https://api.iconify.design",
|
|
14
|
+
"https://api.simplesvg.com",
|
|
15
|
+
"https://api.unisvg.com",
|
|
16
|
+
];
|
|
17
|
+
exports.CHATBOX_CONTENT_SECURITY_POLICY = [
|
|
18
|
+
"default-src 'self'",
|
|
19
|
+
"script-src 'self'",
|
|
20
|
+
"style-src 'self' 'unsafe-inline'",
|
|
21
|
+
"img-src 'self' data: blob:",
|
|
22
|
+
"font-src 'self' data:",
|
|
23
|
+
`connect-src 'self' ${ICONIFY_API_ORIGINS.join(" ")}`,
|
|
24
|
+
"frame-ancestors 'self'",
|
|
25
|
+
"base-uri 'self'",
|
|
26
|
+
"form-action 'none'",
|
|
27
|
+
"object-src 'none'",
|
|
28
|
+
].join("; ");
|
|
29
|
+
exports.CHATBOX_RESPONSE_HEADERS = {
|
|
30
|
+
"Content-Security-Policy": exports.CHATBOX_CONTENT_SECURITY_POLICY,
|
|
31
|
+
"X-Content-Type-Options": "nosniff",
|
|
32
|
+
"Referrer-Policy": "same-origin",
|
|
33
|
+
"Cache-Control": "no-cache",
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chatbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatbox.js","sourceRoot":"","sources":["../../src/constants/chatbox.ts"],"names":[],"mappings":";;;AAIa,QAAA,oBAAoB,GAAG,iBAAiB,CAAC;AACzC,QAAA,mBAAmB,GAAG,GAAG,CAAC;AAC1B,QAAA,mBAAmB,GAAG,QAAQ,CAAC;AAC/B,QAAA,kBAAkB,GAAG,MAAM,CAAC;AAC5B,QAAA,kBAAkB,GAAG,GAAG,CAAC;AACzB,QAAA,4BAA4B,GAAG,0BAA0B,CAAC;AAC1D,QAAA,2BAA2B,GAAG,cAAc,CAAC;AAG7C,QAAA,uBAAuB,GAAG,CAAC,gBAAgB,CAAU,CAAC;AAEnE,MAAM,mBAAmB,GAAG;IAC1B,4BAA4B;IAC5B,2BAA2B;IAC3B,wBAAwB;CACzB,CAAC;AAUW,QAAA,+BAA+B,GAAG;IAC7C,oBAAoB;IACpB,mBAAmB;IACnB,kCAAkC;IAClC,4BAA4B;IAC5B,uBAAuB;IACvB,sBAAsB,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;IACrD,wBAAwB;IACxB,iBAAiB;IACjB,oBAAoB;IACpB,mBAAmB;CACpB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEA,QAAA,wBAAwB,GAAqC;IACxE,yBAAyB,EAAE,uCAA+B;IAC1D,wBAAwB,EAAE,SAAS;IACnC,iBAAiB,EAAE,aAAa;IAChC,eAAe,EAAE,UAAU;CAC5B,CAAC"}
|