@cortexkit/opencode-magic-context 0.30.0 → 0.30.2
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/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/index.js +188 -121
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/shared/announcement.d.ts +1 -1
- package/dist/shared/announcement.d.ts.map +1 -1
- package/dist/shared/rpc-client.d.ts +8 -0
- package/dist/shared/rpc-client.d.ts.map +1 -1
- package/dist/shared/rpc-notifications.d.ts +28 -10
- package/dist/shared/rpc-notifications.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts +22 -3
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts +4 -14
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/dist/tui/data/notification-socket.d.ts +39 -0
- package/dist/tui/data/notification-socket.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/shared/announcement.ts +2 -4
- package/src/shared/rpc-client.ts +14 -0
- package/src/shared/rpc-notifications.test.ts +68 -11
- package/src/shared/rpc-notifications.ts +75 -36
- package/src/shared/rpc-server.ts +249 -150
- package/src/tui/data/context-db.ts +10 -64
- package/src/tui/data/notification-socket.ts +229 -0
- package/src/tui/index.tsx +68 -118
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding-local.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/memory/embedding-local.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"embedding-local.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/memory/embedding-local.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAiRhF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAwBnE;AA0ED,qBAAa,sBAAuB,YAAW,iBAAiB;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;gBAE7C,KAAK,SAAgC,EAAE,cAAc,SAAM;IAMjE,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAmLpC,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,cAAc;IAShB,KAAK,CACP,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAoCzB,UAAU,CACZ,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,EAAE,CAAC;IAyC7B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B9B,QAAQ,IAAI,OAAO;CAGtB"}
|
package/dist/index.js
CHANGED
|
@@ -165515,12 +165515,30 @@ var init_safe_notification_target = __esm(() => {
|
|
|
165515
165515
|
// src/shared/rpc-notifications.ts
|
|
165516
165516
|
var exports_rpc_notifications = {};
|
|
165517
165517
|
__export(exports_rpc_notifications, {
|
|
165518
|
+
registerNotificationSink: () => registerNotificationSink,
|
|
165518
165519
|
pushNotification: () => pushNotification,
|
|
165519
165520
|
isTuiConnected: () => isTuiConnected,
|
|
165520
165521
|
drainNotifications: () => drainNotifications
|
|
165521
165522
|
});
|
|
165523
|
+
function registerNotificationSink(sink) {
|
|
165524
|
+
sinks.add(sink);
|
|
165525
|
+
return () => {
|
|
165526
|
+
sinks.delete(sink);
|
|
165527
|
+
};
|
|
165528
|
+
}
|
|
165529
|
+
function notificationMatchesSink(notification, sink) {
|
|
165530
|
+
return notification.sessionId === undefined || sink.sessionId === undefined || notification.sessionId === sink.sessionId;
|
|
165531
|
+
}
|
|
165522
165532
|
function pushNotification(type, payload, sessionId) {
|
|
165523
|
-
|
|
165533
|
+
const notification = { id: nextNotificationId++, type, payload, sessionId };
|
|
165534
|
+
queue.push(notification);
|
|
165535
|
+
for (const sink of sinks) {
|
|
165536
|
+
if (!notificationMatchesSink(notification, sink))
|
|
165537
|
+
continue;
|
|
165538
|
+
try {
|
|
165539
|
+
sink.send(notification);
|
|
165540
|
+
} catch {}
|
|
165541
|
+
}
|
|
165524
165542
|
if (queue.length > 100) {
|
|
165525
165543
|
const newestPerSession = new Map;
|
|
165526
165544
|
for (const n of queue) {
|
|
@@ -165540,10 +165558,6 @@ function pushNotification(type, payload, sessionId) {
|
|
|
165540
165558
|
}
|
|
165541
165559
|
}
|
|
165542
165560
|
function drainNotifications(lastReceivedId = 0, sessionId) {
|
|
165543
|
-
const now = Date.now();
|
|
165544
|
-
lastDrainAtAny = now;
|
|
165545
|
-
if (sessionId !== undefined)
|
|
165546
|
-
lastDrainAtBySession.set(sessionId, now);
|
|
165547
165561
|
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
165548
165562
|
if (lastReceivedId > 0) {
|
|
165549
165563
|
queue = queue.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
|
|
@@ -165551,17 +165565,20 @@ function drainNotifications(lastReceivedId = 0, sessionId) {
|
|
|
165551
165565
|
return queue.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
|
|
165552
165566
|
}
|
|
165553
165567
|
function isTuiConnected(sessionId) {
|
|
165554
|
-
|
|
165555
|
-
|
|
165556
|
-
|
|
165557
|
-
return
|
|
165568
|
+
if (sinks.size === 0)
|
|
165569
|
+
return false;
|
|
165570
|
+
if (sessionId === undefined)
|
|
165571
|
+
return true;
|
|
165572
|
+
for (const sink of sinks) {
|
|
165573
|
+
if (sink.sessionId === undefined || sink.sessionId === sessionId)
|
|
165574
|
+
return true;
|
|
165558
165575
|
}
|
|
165559
|
-
return
|
|
165576
|
+
return false;
|
|
165560
165577
|
}
|
|
165561
|
-
var queue, nextNotificationId = 1,
|
|
165578
|
+
var queue, nextNotificationId = 1, sinks;
|
|
165562
165579
|
var init_rpc_notifications = __esm(() => {
|
|
165563
165580
|
queue = [];
|
|
165564
|
-
|
|
165581
|
+
sinks = new Set;
|
|
165565
165582
|
});
|
|
165566
165583
|
|
|
165567
165584
|
// src/plugin/conflict-warning-hook.ts
|
|
@@ -166818,8 +166835,8 @@ async function injectWasmOrtForElectron() {
|
|
|
166818
166835
|
try {
|
|
166819
166836
|
const { createRequire: createRequireFn } = await import("node:module");
|
|
166820
166837
|
const requireFn = createRequireFn(import.meta.url);
|
|
166821
|
-
const
|
|
166822
|
-
const distDir =
|
|
166838
|
+
const mainEntry = requireFn.resolve("onnxruntime-web");
|
|
166839
|
+
const distDir = dirname8(mainEntry);
|
|
166823
166840
|
const wasmPathsPrefix = `${pathToFileURL(distDir).href}/`;
|
|
166824
166841
|
if (ortWeb.env?.wasm) {
|
|
166825
166842
|
ortWeb.env.wasm.wasmPaths = wasmPathsPrefix;
|
|
@@ -166939,7 +166956,7 @@ class LocalEmbeddingProvider {
|
|
|
166939
166956
|
if (this.disposing) {
|
|
166940
166957
|
return;
|
|
166941
166958
|
}
|
|
166942
|
-
await injectWasmOrtForElectron();
|
|
166959
|
+
const injectedWasmOrt = await injectWasmOrtForElectron();
|
|
166943
166960
|
const transformersSpec = "@huggingface/transformers";
|
|
166944
166961
|
const transformersModule = await import(transformersSpec);
|
|
166945
166962
|
const env = transformersModule.env;
|
|
@@ -166969,7 +166986,8 @@ class LocalEmbeddingProvider {
|
|
|
166969
166986
|
for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
|
|
166970
166987
|
try {
|
|
166971
166988
|
const pipeline = await withQuietConsole(() => createPipeline("feature-extraction", this.model, {
|
|
166972
|
-
dtype: "fp32"
|
|
166989
|
+
dtype: "fp32",
|
|
166990
|
+
...injectedWasmOrt ? { device: "auto" } : {}
|
|
166973
166991
|
}));
|
|
166974
166992
|
if (this.disposing) {
|
|
166975
166993
|
await pipeline.dispose?.();
|
|
@@ -184722,13 +184740,11 @@ function shouldShowAnnouncement() {
|
|
|
184722
184740
|
}
|
|
184723
184741
|
return ordering > 0;
|
|
184724
184742
|
}
|
|
184725
|
-
var ANNOUNCEMENT_VERSION = "0.30.
|
|
184743
|
+
var ANNOUNCEMENT_VERSION = "0.30.2", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
|
|
184726
184744
|
var init_announcement = __esm(() => {
|
|
184727
184745
|
init_data_path();
|
|
184728
184746
|
ANNOUNCEMENT_FEATURES = [
|
|
184729
|
-
"
|
|
184730
|
-
"TUI sidebar badge color follows your theme again (#198): it uses the theme background like sibling badges, with a fallback so the label can't disappear on transparent themes.",
|
|
184731
|
-
"doctor clears a stale plugin cache more reliably (#199): it compares against the plugin's latest published version and preserves the cache when offline."
|
|
184747
|
+
"Fixed high idle CPU from the TUI sidebar (#200): it now uses a single persistent connection to the plugin instead of polling, so an idle session no longer burns CPU."
|
|
184732
184748
|
];
|
|
184733
184749
|
});
|
|
184734
184750
|
|
|
@@ -202627,7 +202643,6 @@ function calibrateBuckets(input) {
|
|
|
202627
202643
|
// src/plugin/rpc-handlers.ts
|
|
202628
202644
|
init_announcement();
|
|
202629
202645
|
init_logger();
|
|
202630
|
-
init_rpc_notifications();
|
|
202631
202646
|
var workMetricsCarryBySession = new Map;
|
|
202632
202647
|
function resolveSidebarWorkMetrics(db, sessionId, persistedNewWork, persistedTotalInput) {
|
|
202633
202648
|
if (!openCodeDbExists()) {
|
|
@@ -203139,12 +203154,6 @@ function registerRpcHandlers(rpcServer, args) {
|
|
|
203139
203154
|
const resolved = typeof config2.toast_duration_ms === "number" && Number.isFinite(config2.toast_duration_ms) ? config2.toast_duration_ms : 5000;
|
|
203140
203155
|
return { toastDurationMs: resolved };
|
|
203141
203156
|
});
|
|
203142
|
-
rpcServer.handle("pending-notifications", async (params) => {
|
|
203143
|
-
const lastReceivedId = Number(params.lastReceivedId ?? 0);
|
|
203144
|
-
const sessionId = typeof params.sessionId === "string" && params.sessionId.length > 0 ? params.sessionId : undefined;
|
|
203145
|
-
const notifications = drainNotifications(Number.isFinite(lastReceivedId) ? lastReceivedId : 0, sessionId);
|
|
203146
|
-
return { messages: notifications };
|
|
203147
|
-
});
|
|
203148
203157
|
rpcServer.handle("get-announcement", async () => {
|
|
203149
203158
|
if (!shouldShowAnnouncement()) {
|
|
203150
203159
|
return { show: false };
|
|
@@ -204537,6 +204546,7 @@ init_models_dev_cache();
|
|
|
204537
204546
|
|
|
204538
204547
|
// src/shared/rpc-server.ts
|
|
204539
204548
|
init_logger();
|
|
204549
|
+
init_rpc_notifications();
|
|
204540
204550
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
204541
204551
|
import {
|
|
204542
204552
|
chmodSync as chmodSync3,
|
|
@@ -204548,7 +204558,6 @@ import {
|
|
|
204548
204558
|
unlinkSync as unlinkSync4,
|
|
204549
204559
|
writeFileSync as writeFileSync11
|
|
204550
204560
|
} from "node:fs";
|
|
204551
|
-
import { createServer } from "node:http";
|
|
204552
204561
|
import { dirname as dirname9 } from "node:path";
|
|
204553
204562
|
|
|
204554
204563
|
// src/shared/rpc-utils.ts
|
|
@@ -204606,6 +204615,9 @@ function isValidPort(port) {
|
|
|
204606
204615
|
}
|
|
204607
204616
|
|
|
204608
204617
|
// src/shared/rpc-server.ts
|
|
204618
|
+
var MAX_BODY_BYTES = 1048576;
|
|
204619
|
+
var WS_AUTH_TIMEOUT_MS = 5000;
|
|
204620
|
+
var WS_CLOSE_UNAUTHORIZED = 4401;
|
|
204609
204621
|
function tokensMatch(presented, expected) {
|
|
204610
204622
|
const a = Buffer.from(presented, "utf8");
|
|
204611
204623
|
const b = Buffer.from(expected, "utf8");
|
|
@@ -204621,6 +204633,7 @@ class MagicContextRpcServer {
|
|
|
204621
204633
|
portFilePath;
|
|
204622
204634
|
portDir;
|
|
204623
204635
|
startedAt = Date.now();
|
|
204636
|
+
sockets = new Set;
|
|
204624
204637
|
token = randomBytes(32).toString("hex");
|
|
204625
204638
|
constructor(storageDir, directory) {
|
|
204626
204639
|
this.portFilePath = rpcPortFilePath(storageDir, directory);
|
|
@@ -204630,49 +204643,77 @@ class MagicContextRpcServer {
|
|
|
204630
204643
|
this.handlers.set(method, handler);
|
|
204631
204644
|
}
|
|
204632
204645
|
async start() {
|
|
204633
|
-
|
|
204634
|
-
|
|
204635
|
-
|
|
204636
|
-
|
|
204637
|
-
|
|
204638
|
-
|
|
204639
|
-
|
|
204640
|
-
|
|
204641
|
-
|
|
204642
|
-
|
|
204643
|
-
|
|
204644
|
-
|
|
204645
|
-
|
|
204646
|
-
|
|
204647
|
-
|
|
204648
|
-
|
|
204649
|
-
|
|
204650
|
-
|
|
204651
|
-
|
|
204652
|
-
|
|
204653
|
-
|
|
204654
|
-
|
|
204655
|
-
try {
|
|
204656
|
-
rmSync3(tmpPath, { force: true });
|
|
204657
|
-
} catch {}
|
|
204658
|
-
writeFileSync11(tmpPath, JSON.stringify({
|
|
204659
|
-
port: this.port,
|
|
204660
|
-
pid: process.pid,
|
|
204661
|
-
started_at: this.startedAt,
|
|
204662
|
-
token: this.token
|
|
204663
|
-
}), { encoding: "utf-8", mode: 384 });
|
|
204664
|
-
renameSync5(tmpPath, this.portFilePath);
|
|
204665
|
-
try {
|
|
204666
|
-
chmodSync3(this.portFilePath, 384);
|
|
204667
|
-
} catch {}
|
|
204668
|
-
log(`[rpc] server listening on 127.0.0.1:${this.port}`);
|
|
204669
|
-
} catch (err) {
|
|
204670
|
-
log(`[rpc] failed to write port file: ${err}`);
|
|
204646
|
+
const self2 = this;
|
|
204647
|
+
const server = Bun.serve({
|
|
204648
|
+
port: 0,
|
|
204649
|
+
hostname: "127.0.0.1",
|
|
204650
|
+
fetch(req, srv) {
|
|
204651
|
+
return self2.handleFetch(req, srv);
|
|
204652
|
+
},
|
|
204653
|
+
websocket: {
|
|
204654
|
+
open(ws) {
|
|
204655
|
+
ws.data.authTimer = setTimeout(() => {
|
|
204656
|
+
if (!ws.data.authed)
|
|
204657
|
+
ws.close(WS_CLOSE_UNAUTHORIZED, "auth timeout");
|
|
204658
|
+
}, WS_AUTH_TIMEOUT_MS);
|
|
204659
|
+
},
|
|
204660
|
+
message(ws, raw) {
|
|
204661
|
+
self2.handleWsMessage(ws, raw);
|
|
204662
|
+
},
|
|
204663
|
+
close(ws) {
|
|
204664
|
+
if (ws.data.authTimer)
|
|
204665
|
+
clearTimeout(ws.data.authTimer);
|
|
204666
|
+
ws.data.unregister?.();
|
|
204667
|
+
self2.sockets.delete(ws);
|
|
204671
204668
|
}
|
|
204672
|
-
|
|
204673
|
-
});
|
|
204674
|
-
server.unref();
|
|
204669
|
+
}
|
|
204675
204670
|
});
|
|
204671
|
+
this.server = server;
|
|
204672
|
+
this.port = server.port ?? 0;
|
|
204673
|
+
try {
|
|
204674
|
+
this.warnIfOtherLiveInstance();
|
|
204675
|
+
const dir = dirname9(this.portFilePath);
|
|
204676
|
+
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
204677
|
+
try {
|
|
204678
|
+
chmodSync3(dir, 448);
|
|
204679
|
+
} catch {}
|
|
204680
|
+
const tmpPath = `${this.portFilePath}.tmp`;
|
|
204681
|
+
try {
|
|
204682
|
+
rmSync3(tmpPath, { force: true });
|
|
204683
|
+
} catch {}
|
|
204684
|
+
writeFileSync11(tmpPath, JSON.stringify({
|
|
204685
|
+
port: this.port,
|
|
204686
|
+
pid: process.pid,
|
|
204687
|
+
started_at: this.startedAt,
|
|
204688
|
+
token: this.token
|
|
204689
|
+
}), { encoding: "utf-8", mode: 384 });
|
|
204690
|
+
renameSync5(tmpPath, this.portFilePath);
|
|
204691
|
+
try {
|
|
204692
|
+
chmodSync3(this.portFilePath, 384);
|
|
204693
|
+
} catch {}
|
|
204694
|
+
log(`[rpc] server listening on 127.0.0.1:${this.port}`);
|
|
204695
|
+
} catch (err) {
|
|
204696
|
+
log(`[rpc] failed to write port file: ${err}`);
|
|
204697
|
+
}
|
|
204698
|
+
return this.port;
|
|
204699
|
+
}
|
|
204700
|
+
stop() {
|
|
204701
|
+
for (const ws of this.sockets) {
|
|
204702
|
+
try {
|
|
204703
|
+
if (ws.data.authTimer)
|
|
204704
|
+
clearTimeout(ws.data.authTimer);
|
|
204705
|
+
ws.data.unregister?.();
|
|
204706
|
+
ws.close();
|
|
204707
|
+
} catch {}
|
|
204708
|
+
}
|
|
204709
|
+
this.sockets.clear();
|
|
204710
|
+
if (this.server) {
|
|
204711
|
+
this.server.stop(true);
|
|
204712
|
+
this.server = null;
|
|
204713
|
+
}
|
|
204714
|
+
try {
|
|
204715
|
+
unlinkSync4(this.portFilePath);
|
|
204716
|
+
} catch {}
|
|
204676
204717
|
}
|
|
204677
204718
|
warnIfOtherLiveInstance() {
|
|
204678
204719
|
try {
|
|
@@ -204687,73 +204728,99 @@ class MagicContextRpcServer {
|
|
|
204687
204728
|
}
|
|
204688
204729
|
} catch {}
|
|
204689
204730
|
}
|
|
204690
|
-
|
|
204691
|
-
|
|
204692
|
-
|
|
204693
|
-
|
|
204731
|
+
async handleFetch(req, srv) {
|
|
204732
|
+
const url2 = new URL(req.url);
|
|
204733
|
+
if (url2.pathname === "/ws") {
|
|
204734
|
+
const ok = srv.upgrade(req, { data: { authed: false } });
|
|
204735
|
+
if (ok)
|
|
204736
|
+
return;
|
|
204737
|
+
return new Response("upgrade failed", { status: 400 });
|
|
204694
204738
|
}
|
|
204695
|
-
|
|
204696
|
-
|
|
204697
|
-
} catch {}
|
|
204698
|
-
}
|
|
204699
|
-
dispatch(req, res) {
|
|
204700
|
-
const url2 = req.url ?? "";
|
|
204701
|
-
if (req.method === "GET" && url2 === "/health") {
|
|
204702
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
204703
|
-
res.end(JSON.stringify({ ok: true, pid: process.pid }));
|
|
204704
|
-
return;
|
|
204739
|
+
if (req.method === "GET" && url2.pathname === "/health") {
|
|
204740
|
+
return json2({ ok: true, pid: process.pid });
|
|
204705
204741
|
}
|
|
204706
|
-
if (req.method !== "POST" || !url2.startsWith("/rpc/")) {
|
|
204707
|
-
|
|
204708
|
-
res.end("Not Found");
|
|
204709
|
-
return;
|
|
204742
|
+
if (req.method !== "POST" || !url2.pathname.startsWith("/rpc/")) {
|
|
204743
|
+
return new Response("Not Found", { status: 404 });
|
|
204710
204744
|
}
|
|
204711
|
-
const auth = req.headers.authorization;
|
|
204745
|
+
const auth = req.headers.get("authorization");
|
|
204712
204746
|
const presented = typeof auth === "string" ? auth.replace(/^Bearer\s+/i, "") : "";
|
|
204713
204747
|
if (!tokensMatch(presented, this.token)) {
|
|
204714
|
-
|
|
204715
|
-
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
204716
|
-
req.resume();
|
|
204717
|
-
return;
|
|
204748
|
+
return json2({ error: "Unauthorized" }, 401);
|
|
204718
204749
|
}
|
|
204719
|
-
const method = url2.slice(5);
|
|
204750
|
+
const method = url2.pathname.slice(5);
|
|
204720
204751
|
const handler = this.handlers.get(method);
|
|
204721
204752
|
if (!handler) {
|
|
204722
|
-
|
|
204723
|
-
res.end(JSON.stringify({ error: `Unknown method: ${method}` }));
|
|
204724
|
-
return;
|
|
204753
|
+
return json2({ error: `Unknown method: ${method}` }, 404);
|
|
204725
204754
|
}
|
|
204726
|
-
|
|
204727
|
-
|
|
204728
|
-
|
|
204729
|
-
|
|
204730
|
-
|
|
204731
|
-
|
|
204732
|
-
req.destroy();
|
|
204733
|
-
}
|
|
204734
|
-
});
|
|
204735
|
-
req.on("end", () => {
|
|
204736
|
-
let params = {};
|
|
204755
|
+
const bodyText = await req.text();
|
|
204756
|
+
if (bodyText.length > MAX_BODY_BYTES) {
|
|
204757
|
+
return new Response("Request too large", { status: 413 });
|
|
204758
|
+
}
|
|
204759
|
+
let params = {};
|
|
204760
|
+
if (bodyText.length > 0) {
|
|
204737
204761
|
try {
|
|
204738
|
-
|
|
204739
|
-
params = JSON.parse(body);
|
|
204740
|
-
}
|
|
204762
|
+
params = JSON.parse(bodyText);
|
|
204741
204763
|
} catch {
|
|
204742
|
-
|
|
204743
|
-
|
|
204764
|
+
return json2({ error: "Invalid JSON" }, 400);
|
|
204765
|
+
}
|
|
204766
|
+
}
|
|
204767
|
+
try {
|
|
204768
|
+
const result = await handler(params);
|
|
204769
|
+
return json2(result);
|
|
204770
|
+
} catch (err) {
|
|
204771
|
+
log(`[rpc] handler error: ${method} => ${err}`);
|
|
204772
|
+
return json2({ error: String(err) }, 500);
|
|
204773
|
+
}
|
|
204774
|
+
}
|
|
204775
|
+
handleWsMessage(ws, raw) {
|
|
204776
|
+
let msg;
|
|
204777
|
+
try {
|
|
204778
|
+
msg = JSON.parse(typeof raw === "string" ? raw : raw.toString("utf8"));
|
|
204779
|
+
} catch {
|
|
204780
|
+
return;
|
|
204781
|
+
}
|
|
204782
|
+
if (msg.type === "hello") {
|
|
204783
|
+
if (!tokensMatch(typeof msg.token === "string" ? msg.token : "", this.token)) {
|
|
204784
|
+
ws.send(JSON.stringify({ type: "error", error: "unauthorized" }));
|
|
204785
|
+
ws.close(WS_CLOSE_UNAUTHORIZED, "bad token");
|
|
204744
204786
|
return;
|
|
204745
204787
|
}
|
|
204746
|
-
|
|
204747
|
-
|
|
204748
|
-
|
|
204749
|
-
}
|
|
204750
|
-
|
|
204751
|
-
|
|
204752
|
-
|
|
204753
|
-
|
|
204754
|
-
|
|
204788
|
+
if (ws.data.authTimer) {
|
|
204789
|
+
clearTimeout(ws.data.authTimer);
|
|
204790
|
+
ws.data.authTimer = undefined;
|
|
204791
|
+
}
|
|
204792
|
+
ws.data.authed = true;
|
|
204793
|
+
ws.data.sessionId = typeof msg.sessionId === "string" && msg.sessionId.length > 0 ? msg.sessionId : undefined;
|
|
204794
|
+
const sink = {
|
|
204795
|
+
sessionId: ws.data.sessionId,
|
|
204796
|
+
send: (notification) => {
|
|
204797
|
+
ws.send(JSON.stringify({ type: "notification", notification }));
|
|
204798
|
+
}
|
|
204799
|
+
};
|
|
204800
|
+
ws.data.unregister = registerNotificationSink(sink);
|
|
204801
|
+
this.sockets.add(ws);
|
|
204802
|
+
const lastReceivedId = Number(msg.lastReceivedId ?? 0);
|
|
204803
|
+
const backlog = drainNotifications(Number.isFinite(lastReceivedId) ? lastReceivedId : 0, ws.data.sessionId);
|
|
204804
|
+
for (const notification of backlog) {
|
|
204805
|
+
ws.send(JSON.stringify({ type: "notification", notification }));
|
|
204806
|
+
}
|
|
204807
|
+
ws.send(JSON.stringify({ type: "hello-ack" }));
|
|
204808
|
+
return;
|
|
204809
|
+
}
|
|
204810
|
+
if (msg.type === "ack") {
|
|
204811
|
+
const lastReceivedId = Number(msg.lastReceivedId ?? 0);
|
|
204812
|
+
if (Number.isFinite(lastReceivedId) && lastReceivedId > 0) {
|
|
204813
|
+
drainNotifications(lastReceivedId, ws.data.sessionId);
|
|
204814
|
+
}
|
|
204815
|
+
}
|
|
204755
204816
|
}
|
|
204756
204817
|
}
|
|
204818
|
+
function json2(body, status = 200) {
|
|
204819
|
+
return new Response(JSON.stringify(body), {
|
|
204820
|
+
status,
|
|
204821
|
+
headers: { "Content-Type": "application/json" }
|
|
204822
|
+
});
|
|
204823
|
+
}
|
|
204757
204824
|
|
|
204758
204825
|
// src/index.ts
|
|
204759
204826
|
var server = async (ctx) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-handlers.d.ts","sourceRoot":"","sources":["../../src/plugin/rpc-handlers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,OAAO,EACH,KAAK,eAAe,IAAI,QAAQ,EAGnC,MAAM,mCAAmC,CAAC;AAc3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc-handlers.d.ts","sourceRoot":"","sources":["../../src/plugin/rpc-handlers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,OAAO,EACH,KAAK,eAAe,IAAI,QAAQ,EAGnC,MAAM,mCAAmC,CAAC;AAc3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAoBlF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAe,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA0FtF,wBAAgB,oBAAoB,CAChC,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,qBAAqB,CAAC,EAAE,MAAM,EAK9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,eAAe,CAuVjB;AAED,wBAAgB,iBAAiB,CAC7B,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,qBAAqB,CAAC,EAAE,MAAM,GAC/B,YAAY,CAyKd;AA4BD;;GAEG;AACH,wBAAgB,mBAAmB,CAC/B,SAAS,EAAE,qBAAqB,EAChC,IAAI,EAAE;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,gBAAgB,CAAC;CACtC,GACF,IAAI,CA4ON"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Bump only when there are user-visible changes worth a startup dialog.
|
|
19
19
|
* Does NOT need to match the published package version.
|
|
20
20
|
*/
|
|
21
|
-
export declare const ANNOUNCEMENT_VERSION = "0.30.
|
|
21
|
+
export declare const ANNOUNCEMENT_VERSION = "0.30.2";
|
|
22
22
|
/**
|
|
23
23
|
* Short, user-facing bullet strings. Keep each line ~80 chars or shorter so the
|
|
24
24
|
* TUI dialog renders cleanly without horizontal scroll on a typical terminal.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"announcement.d.ts","sourceRoot":"","sources":["../../src/shared/announcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"announcement.d.ts","sourceRoot":"","sources":["../../src/shared/announcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAEvD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,qDAAqD,CAAC;AAyBtF;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAGjD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAS1D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CA0BhD"}
|
|
@@ -9,6 +9,14 @@ export declare class MagicContextRpcClient {
|
|
|
9
9
|
call<T = Record<string, unknown>>(method: string, params?: Record<string, unknown>): Promise<T>;
|
|
10
10
|
/** Check if the RPC server is reachable. */
|
|
11
11
|
isAvailable(): Promise<boolean>;
|
|
12
|
+
/** Resolve the live server's port + bearer token (for opening the WS push
|
|
13
|
+
* channel). Reuses the same health-checked port-file discovery as `call`,
|
|
14
|
+
* so the WS client and the HTTP client always agree on which server instance
|
|
15
|
+
* (and token) to use. Returns null when no live server is found. */
|
|
16
|
+
resolveEndpoint(): Promise<{
|
|
17
|
+
port: number;
|
|
18
|
+
token: string | null;
|
|
19
|
+
} | null>;
|
|
12
20
|
private resolvePort;
|
|
13
21
|
private readPortFile;
|
|
14
22
|
private healthCheck;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-client.ts"],"names":[],"mappings":"AAiBA,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,aAAa,CAAS;gBAElB,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAKjD,iFAAiF;IAC3E,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,OAAO,CAAC,CAAC,CAAC;IAwDb,4CAA4C;IACtC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-client.ts"],"names":[],"mappings":"AAiBA,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,aAAa,CAAS;gBAElB,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAKjD,iFAAiF;IAC3E,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,OAAO,CAAC,CAAC,CAAC;IAwDb,4CAA4C;IACtC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IASrC;;;yEAGqE;IAC/D,eAAe,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;YAUjE,WAAW;IAmCzB,OAAO,CAAC,YAAY;YA4BN,WAAW;YAWX,gBAAgB;IAU9B,KAAK,IAAI,IAAI;CAKhB"}
|
|
@@ -12,16 +12,33 @@ export interface RpcNotification {
|
|
|
12
12
|
payload: Record<string, unknown>;
|
|
13
13
|
sessionId?: string;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* A connected TUI notification sink — one per authenticated WebSocket. The RPC
|
|
17
|
+
* server registers a sink when a TUI socket authenticates (hello) and removes
|
|
18
|
+
* it on close. `send` is sink-agnostic (the server owns the actual WS socket)
|
|
19
|
+
* so this module stays free of Bun/WS types.
|
|
20
|
+
*/
|
|
21
|
+
export interface NotificationSink {
|
|
22
|
+
/** The TUI's active session at connect time (its hello scope). */
|
|
23
|
+
sessionId?: string;
|
|
24
|
+
/** Deliver one notification over this sink's live socket. */
|
|
25
|
+
send: (notification: RpcNotification) => void;
|
|
26
|
+
}
|
|
27
|
+
/** Register a live TUI sink. Returns an unregister fn (call on socket close). */
|
|
28
|
+
export declare function registerNotificationSink(sink: NotificationSink): () => void;
|
|
29
|
+
/** Push a notification to the TUI. Fans out to any live WS sink immediately and
|
|
30
|
+
* also enqueues it so a TUI that is momentarily disconnected (reconnecting, or
|
|
31
|
+
* not yet connected) still receives it on its next hello via the backlog drain.
|
|
32
|
+
* At-least-once: a live push that the socket drops is re-delivered from the
|
|
33
|
+
* queue on reconnect (pruned only when the client acks via `lastReceivedId`). */
|
|
16
34
|
export declare function pushNotification(type: string, payload: Record<string, unknown>, sessionId?: string): void;
|
|
17
35
|
/** Return pending notifications after acking the client's last received id.
|
|
18
|
-
* Updates lastDrainAt so isTuiConnected() reflects recent activity.
|
|
19
36
|
*
|
|
20
37
|
* Session scoping: when `sessionId` is provided, only notifications tagged for
|
|
21
38
|
* that session (or session-less/global ones) are returned and pruned — a
|
|
22
39
|
* notification tagged for a DIFFERENT session is never handed to this client
|
|
23
40
|
* and is never pruned by this client's ack. This matters because the in-memory
|
|
24
|
-
* queue is per-process but a TUI can end up
|
|
41
|
+
* queue is per-process but a TUI can end up bound to a process that also serves
|
|
25
42
|
* OTHER sessions: e.g. opening OpenCode Desktop on the same project starts a
|
|
26
43
|
* newer RPC server that the TUI's port discovery (newest-pid-wins) then selects,
|
|
27
44
|
* so a Desktop-session upgrade-dialog action would otherwise surface in an
|
|
@@ -31,16 +48,17 @@ export declare function pushNotification(type: string, payload: Record<string, u
|
|
|
31
48
|
*
|
|
32
49
|
* Delivery is at-least-once (non-destructive return + prune-on-ack): a returned
|
|
33
50
|
* notification stays queued until a later call acks it via a higher
|
|
34
|
-
* `lastReceivedId`, so a
|
|
51
|
+
* `lastReceivedId`, so a dropped WS socket re-delivers the backlog on reconnect
|
|
52
|
+
* (the client sends its `lastReceivedId` in the hello). */
|
|
35
53
|
export declare function drainNotifications(lastReceivedId?: number, sessionId?: string): RpcNotification[];
|
|
36
|
-
/** Whether a TUI client is
|
|
37
|
-
*
|
|
54
|
+
/** Whether a TUI client is connected via a live notification socket.
|
|
55
|
+
* Now exact socket liveness (a registered WS sink), not a poll-drain timestamp.
|
|
38
56
|
*
|
|
39
|
-
* Pass `sessionId` (preferred) to ask whether a TUI is
|
|
57
|
+
* Pass `sessionId` (preferred) to ask whether a TUI is connected FOR THAT
|
|
40
58
|
* SESSION — this is what producers (`/ctx-status`, `/ctx-recomp`, the upgrade
|
|
41
59
|
* reminder) must use to decide dialog-vs-message, so a TUI on a different
|
|
42
|
-
* session in the same process does not misroute their delivery.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
60
|
+
* session in the same process does not misroute their delivery. A session-less
|
|
61
|
+
* sink (legacy/global) counts for any session query. Omit `sessionId` only for
|
|
62
|
+
* callers with no session context; they get "any sink connected". */
|
|
45
63
|
export declare function isTuiConnected(sessionId?: string): boolean;
|
|
46
64
|
//# sourceMappingURL=rpc-notifications.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-notifications.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-notifications.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"rpc-notifications.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-notifications.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAKD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC7B,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,IAAI,EAAE,CAAC,YAAY,EAAE,eAAe,KAAK,IAAI,CAAC;CACjD;AAYD,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAK3E;AAcD;;;;kFAIkF;AAClF,wBAAgB,gBAAgB,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,CAAC,EAAE,MAAM,GACnB,IAAI,CAoCN;AAED;;;;;;;;;;;;;;;;;4DAiB4D;AAC5D,wBAAgB,kBAAkB,CAAC,cAAc,SAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,CAe5F;AAED;;;;;;;;sEAQsE;AACtE,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAO1D"}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
type RpcHandler = (params: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
2
|
+
/**
|
|
3
|
+
* Plugin-private localhost RPC server for TUI ↔ server-plugin communication.
|
|
4
|
+
*
|
|
5
|
+
* Runs on Bun (the OpenCode server runner is a Bun Worker), so it uses
|
|
6
|
+
* `Bun.serve` to host BOTH:
|
|
7
|
+
* - HTTP request/reply routes (`/health`, `/rpc/<method>`) — the TUI's snapshot
|
|
8
|
+
* and dialog-result calls, which are event-driven, not idle; and
|
|
9
|
+
* - a WebSocket endpoint (`/ws`) — a single persistent connection per TUI over
|
|
10
|
+
* which the server PUSHES notifications (dialog/toast actions). This replaces
|
|
11
|
+
* the old 500ms HTTP poll, whose new-connection-per-tick cost was the source
|
|
12
|
+
* of idle TUI CPU (#200). Pi never imports this module, so `Bun.serve` is safe.
|
|
13
|
+
*/
|
|
2
14
|
export declare class MagicContextRpcServer {
|
|
3
15
|
private server;
|
|
4
16
|
private port;
|
|
@@ -6,16 +18,23 @@ export declare class MagicContextRpcServer {
|
|
|
6
18
|
private portFilePath;
|
|
7
19
|
private portDir;
|
|
8
20
|
private startedAt;
|
|
21
|
+
/** Every authenticated WS socket, so dispose can close them all. */
|
|
22
|
+
private sockets;
|
|
9
23
|
private readonly token;
|
|
10
24
|
constructor(storageDir: string, directory: string);
|
|
11
25
|
/** Register an RPC method handler. */
|
|
12
26
|
handle(method: string, handler: RpcHandler): void;
|
|
13
27
|
/** Start the server on a random port, write port to disk. */
|
|
14
28
|
start(): Promise<number>;
|
|
15
|
-
|
|
16
|
-
/** Stop the server and clean up port file. */
|
|
29
|
+
/** Stop the server: close every socket, stop accepting, remove port file. */
|
|
17
30
|
stop(): void;
|
|
18
|
-
private
|
|
31
|
+
private warnIfOtherLiveInstance;
|
|
32
|
+
/** HTTP route handler (Bun fetch). Returns a Response, or undefined when the
|
|
33
|
+
* request was upgraded to a WebSocket. */
|
|
34
|
+
private handleFetch;
|
|
35
|
+
/** WS message handler: hello (auth + sink registration + backlog drain) and
|
|
36
|
+
* ack (cursor advance → queue prune). All other messages are ignored. */
|
|
37
|
+
private handleWsMessage;
|
|
19
38
|
}
|
|
20
39
|
export {};
|
|
21
40
|
//# sourceMappingURL=rpc-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"AAqBA,KAAK,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAiCxF;;;;;;;;;;;GAWG;AACH,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAc;IAC/B,oEAAoE;IACpE,OAAO,CAAC,OAAO,CAAsC;IAMrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmC;gBAE7C,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAKjD,sCAAsC;IACtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIjD,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAmF9B,6EAA6E;IAC7E,IAAI,IAAI,IAAI;IAuBZ,OAAO,CAAC,uBAAuB;IAgB/B;+CAC2C;YAC7B,WAAW;IAuDzB;8EAC0E;IAC1E,OAAO,CAAC,eAAe;CA0D1B"}
|