@drisp/cli 0.5.24 → 0.5.26
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/{WorkflowInstallWizard-IMU47QWB.js → WorkflowInstallWizard-5FENJHBW.js} +2 -2
- package/dist/athena-gateway.js +61 -242
- package/dist/{chunk-YU2WMPRC.js → chunk-3U37HT4T.js} +381 -114
- package/dist/{chunk-7GEQJQMR.js → chunk-4NX4WFPB.js} +2 -2
- package/dist/{chunk-7UUPLAP4.js → chunk-73V7GXV6.js} +10 -13
- package/dist/{chunk-BTKQ67RE.js → chunk-QYB6N2OT.js} +1 -1
- package/dist/{chunk-QBMYQJFX.js → chunk-YZ7RCBKO.js} +580 -153
- package/dist/cli.js +752 -355
- package/dist/dashboard-daemon.js +4 -4
- package/dist/hook-forwarder.js +1 -1
- package/package.json +1 -1
|
@@ -475,58 +475,10 @@ function writeGatewayTrace(message) {
|
|
|
475
475
|
process.stderr.write(line);
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
// src/
|
|
478
|
+
// src/gateway/transport/uds.ts
|
|
479
479
|
import fs4 from "fs";
|
|
480
|
+
import net from "net";
|
|
480
481
|
import path3 from "path";
|
|
481
|
-
import Database from "better-sqlite3";
|
|
482
|
-
function migrateVersionedSchema(db, schema) {
|
|
483
|
-
db.exec(
|
|
484
|
-
"CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL)"
|
|
485
|
-
);
|
|
486
|
-
const existing = db.prepare("SELECT version FROM schema_version").get();
|
|
487
|
-
if (existing && existing.version > schema.version) {
|
|
488
|
-
throw schema.onNewerVersion?.(existing.version, schema.version) ?? new Error(
|
|
489
|
-
`Database has newer schema version ${existing.version} (expected <= ${schema.version}).`
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
schema.migrate(db, existing?.version);
|
|
493
|
-
if (!existing) {
|
|
494
|
-
db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(
|
|
495
|
-
schema.version
|
|
496
|
-
);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
function openVersionedDb(dbPath, options) {
|
|
500
|
-
if (options.ensureDir && dbPath !== ":memory:") {
|
|
501
|
-
fs4.mkdirSync(path3.dirname(dbPath), {
|
|
502
|
-
recursive: true,
|
|
503
|
-
...options.dirMode !== void 0 ? { mode: options.dirMode } : {}
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
const db = new Database(dbPath);
|
|
507
|
-
db.exec("PRAGMA journal_mode = WAL");
|
|
508
|
-
if (options.foreignKeys) {
|
|
509
|
-
db.exec("PRAGMA foreign_keys = ON");
|
|
510
|
-
}
|
|
511
|
-
if (options.version === void 0) {
|
|
512
|
-
options.migrate(db, void 0);
|
|
513
|
-
} else {
|
|
514
|
-
migrateVersionedSchema(db, {
|
|
515
|
-
version: options.version,
|
|
516
|
-
migrate: options.migrate,
|
|
517
|
-
...options.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
return db;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// src/gateway/transport/types.ts
|
|
524
|
-
var TransportUnreachableError = class extends Error {
|
|
525
|
-
constructor(message) {
|
|
526
|
-
super(message);
|
|
527
|
-
this.name = "TransportUnreachableError";
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
482
|
|
|
531
483
|
// src/gateway/transport/trace.ts
|
|
532
484
|
function traceGatewayFrame(transport, peer, direction, frame) {
|
|
@@ -549,12 +501,55 @@ function redactFrame(value) {
|
|
|
549
501
|
return out;
|
|
550
502
|
}
|
|
551
503
|
|
|
552
|
-
// src/gateway/transport/
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
504
|
+
// src/gateway/transport/framedConnection.ts
|
|
505
|
+
function createFramedConnection(peer, channel) {
|
|
506
|
+
const frameHandlers = /* @__PURE__ */ new Set();
|
|
507
|
+
const closeHandlers = /* @__PURE__ */ new Set();
|
|
508
|
+
const errorHandlers = /* @__PURE__ */ new Set();
|
|
509
|
+
channel.onMessage((text) => {
|
|
510
|
+
let parsed;
|
|
511
|
+
try {
|
|
512
|
+
parsed = JSON.parse(text);
|
|
513
|
+
} catch {
|
|
514
|
+
channel.close();
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
traceGatewayFrame(channel.traceTag, peer, "in", parsed);
|
|
518
|
+
for (const handler of frameHandlers) handler(parsed);
|
|
519
|
+
});
|
|
520
|
+
channel.onError((err) => {
|
|
521
|
+
for (const handler of errorHandlers) handler(err);
|
|
522
|
+
});
|
|
523
|
+
channel.onClose(() => {
|
|
524
|
+
for (const handler of closeHandlers) handler();
|
|
525
|
+
});
|
|
526
|
+
return {
|
|
527
|
+
kind: channel.kind,
|
|
528
|
+
peer,
|
|
529
|
+
send: (frame) => {
|
|
530
|
+
if (!channel.isOpen()) return;
|
|
531
|
+
traceGatewayFrame(channel.traceTag, peer, "out", frame);
|
|
532
|
+
channel.writeFrame(frame);
|
|
533
|
+
},
|
|
534
|
+
close: () => channel.close(),
|
|
535
|
+
onFrame: (cb) => {
|
|
536
|
+
frameHandlers.add(cb);
|
|
537
|
+
return () => frameHandlers.delete(cb);
|
|
538
|
+
},
|
|
539
|
+
onClose: (cb) => {
|
|
540
|
+
closeHandlers.add(cb);
|
|
541
|
+
return () => closeHandlers.delete(cb);
|
|
542
|
+
},
|
|
543
|
+
onError: (cb) => {
|
|
544
|
+
errorHandlers.add(cb);
|
|
545
|
+
return () => errorHandlers.delete(cb);
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
}
|
|
556
549
|
|
|
557
550
|
// src/gateway/transport/framing.ts
|
|
551
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
552
|
+
import { StringDecoder } from "string_decoder";
|
|
558
553
|
var DEFAULT_MAX_LINE_BYTES = 1024 * 1024;
|
|
559
554
|
var LineReaderOverflowError = class extends Error {
|
|
560
555
|
constructor(limit) {
|
|
@@ -564,14 +559,18 @@ var LineReaderOverflowError = class extends Error {
|
|
|
564
559
|
};
|
|
565
560
|
var LineReader = class {
|
|
566
561
|
buffer = "";
|
|
562
|
+
// Streams UTF-8 across chunk boundaries: a multi-byte sequence split by a
|
|
563
|
+
// stream `data` event is buffered here until complete, never truncated to
|
|
564
|
+
// U+FFFD the way a per-chunk `Buffer.toString('utf-8')` would.
|
|
565
|
+
decoder = new StringDecoder("utf8");
|
|
567
566
|
maxBytes;
|
|
568
567
|
constructor(maxBytes = DEFAULT_MAX_LINE_BYTES) {
|
|
569
568
|
this.maxBytes = maxBytes;
|
|
570
569
|
}
|
|
571
570
|
push(chunk) {
|
|
572
|
-
const incoming = typeof chunk === "string" ? chunk :
|
|
573
|
-
if (this.buffer
|
|
574
|
-
this.
|
|
571
|
+
const incoming = typeof chunk === "string" ? chunk : this.decoder.write(chunk);
|
|
572
|
+
if (Buffer2.byteLength(this.buffer) + Buffer2.byteLength(incoming) > this.maxBytes) {
|
|
573
|
+
this.reset();
|
|
575
574
|
throw new LineReaderOverflowError(this.maxBytes);
|
|
576
575
|
}
|
|
577
576
|
this.buffer += incoming;
|
|
@@ -586,21 +585,29 @@ var LineReader = class {
|
|
|
586
585
|
}
|
|
587
586
|
return lines;
|
|
588
587
|
}
|
|
589
|
-
|
|
590
|
-
const remainder = this.buffer.trim();
|
|
588
|
+
reset() {
|
|
591
589
|
this.buffer = "";
|
|
592
|
-
|
|
590
|
+
this.decoder = new StringDecoder("utf8");
|
|
593
591
|
}
|
|
594
592
|
};
|
|
595
593
|
function encodeLine(value) {
|
|
596
594
|
return JSON.stringify(value) + "\n";
|
|
597
595
|
}
|
|
598
596
|
|
|
597
|
+
// src/gateway/transport/types.ts
|
|
598
|
+
var TransportUnreachableError = class extends Error {
|
|
599
|
+
constructor(message) {
|
|
600
|
+
super(message);
|
|
601
|
+
this.name = "TransportUnreachableError";
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
599
605
|
// src/gateway/transport/uds.ts
|
|
600
606
|
function createUdsServerTransport(opts) {
|
|
601
607
|
return {
|
|
602
608
|
kind: "uds",
|
|
603
|
-
listen: (onConnection) => listenUds(opts, onConnection)
|
|
609
|
+
listen: (onConnection) => listenUds(opts, onConnection),
|
|
610
|
+
describe: () => ({ kind: "uds", socketPath: opts.socketPath })
|
|
604
611
|
};
|
|
605
612
|
}
|
|
606
613
|
function createUdsClientTransport(opts) {
|
|
@@ -612,7 +619,7 @@ function createUdsClientTransport(opts) {
|
|
|
612
619
|
async function listenUds(opts, onConnection) {
|
|
613
620
|
const logError = opts.logError ?? ((m) => process.stderr.write(m + "\n"));
|
|
614
621
|
await unlinkIfStale(opts.socketPath);
|
|
615
|
-
|
|
622
|
+
fs4.mkdirSync(path3.dirname(opts.socketPath), { recursive: true, mode: 448 });
|
|
616
623
|
const activeSockets = /* @__PURE__ */ new Set();
|
|
617
624
|
const server = net.createServer({ pauseOnConnect: false }, (socket) => {
|
|
618
625
|
activeSockets.add(socket);
|
|
@@ -626,7 +633,7 @@ async function listenUds(opts, onConnection) {
|
|
|
626
633
|
server.off("error", onError);
|
|
627
634
|
try {
|
|
628
635
|
if (process.platform !== "win32") {
|
|
629
|
-
|
|
636
|
+
fs4.chmodSync(opts.socketPath, 384);
|
|
630
637
|
}
|
|
631
638
|
} catch (err) {
|
|
632
639
|
logError(
|
|
@@ -644,7 +651,7 @@ async function listenUds(opts, onConnection) {
|
|
|
644
651
|
activeSockets.clear();
|
|
645
652
|
server.close(() => {
|
|
646
653
|
try {
|
|
647
|
-
|
|
654
|
+
fs4.unlinkSync(opts.socketPath);
|
|
648
655
|
} catch {
|
|
649
656
|
}
|
|
650
657
|
resolve();
|
|
@@ -683,7 +690,7 @@ async function connectUds(opts) {
|
|
|
683
690
|
return createSocketConnection(socket, "uds");
|
|
684
691
|
}
|
|
685
692
|
async function unlinkIfStale(socketPath) {
|
|
686
|
-
if (!
|
|
693
|
+
if (!fs4.existsSync(socketPath)) return;
|
|
687
694
|
const alive = await new Promise((resolve) => {
|
|
688
695
|
const probe = net.connect(socketPath);
|
|
689
696
|
const timer = setTimeout(() => {
|
|
@@ -702,68 +709,326 @@ async function unlinkIfStale(socketPath) {
|
|
|
702
709
|
});
|
|
703
710
|
if (!alive) {
|
|
704
711
|
try {
|
|
705
|
-
|
|
712
|
+
fs4.unlinkSync(socketPath);
|
|
706
713
|
} catch {
|
|
707
714
|
}
|
|
708
715
|
}
|
|
709
716
|
}
|
|
710
717
|
function createSocketConnection(socket, peer, logError) {
|
|
718
|
+
return createFramedConnection(peer, udsRawChannel(socket, logError));
|
|
719
|
+
}
|
|
720
|
+
function udsRawChannel(socket, logError) {
|
|
711
721
|
const reader = new LineReader();
|
|
712
|
-
const frameHandlers = /* @__PURE__ */ new Set();
|
|
713
|
-
const closeHandlers = /* @__PURE__ */ new Set();
|
|
714
|
-
const errorHandlers = /* @__PURE__ */ new Set();
|
|
715
|
-
socket.on("data", (chunk) => {
|
|
716
|
-
let lines;
|
|
717
|
-
try {
|
|
718
|
-
lines = reader.push(chunk);
|
|
719
|
-
} catch (err) {
|
|
720
|
-
if (err instanceof LineReaderOverflowError) {
|
|
721
|
-
logError?.(`gateway: control connection overflow \u2014 closing`);
|
|
722
|
-
socket.destroy();
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
throw err;
|
|
726
|
-
}
|
|
727
|
-
for (const line of lines) {
|
|
728
|
-
let parsed;
|
|
729
|
-
try {
|
|
730
|
-
parsed = JSON.parse(line);
|
|
731
|
-
} catch {
|
|
732
|
-
socket.destroy();
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
traceGatewayFrame("uds", peer, "in", parsed);
|
|
736
|
-
for (const handler of frameHandlers) handler(parsed);
|
|
737
|
-
}
|
|
738
|
-
});
|
|
739
|
-
socket.on("error", (err) => {
|
|
740
|
-
for (const handler of errorHandlers) handler(err);
|
|
741
|
-
});
|
|
742
|
-
socket.on("close", () => {
|
|
743
|
-
for (const handler of closeHandlers) handler();
|
|
744
|
-
});
|
|
745
722
|
return {
|
|
746
723
|
kind: "uds",
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
traceGatewayFrame("uds", peer, "out", frame);
|
|
751
|
-
socket.write(encodeLine(frame));
|
|
752
|
-
},
|
|
724
|
+
traceTag: "uds",
|
|
725
|
+
isOpen: () => socket.writable,
|
|
726
|
+
writeFrame: (frame) => socket.write(encodeLine(frame)),
|
|
753
727
|
close: () => socket.destroy(),
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
728
|
+
onMessage: (cb) => {
|
|
729
|
+
socket.on("data", (chunk) => {
|
|
730
|
+
let lines;
|
|
731
|
+
try {
|
|
732
|
+
lines = reader.push(chunk);
|
|
733
|
+
} catch (err) {
|
|
734
|
+
if (err instanceof LineReaderOverflowError) {
|
|
735
|
+
logError?.(`gateway: control connection overflow \u2014 closing`);
|
|
736
|
+
} else {
|
|
737
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
738
|
+
logError?.(
|
|
739
|
+
`gateway: control connection error \u2014 closing: ${detail}`
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
socket.destroy();
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
for (const line of lines) cb(line);
|
|
746
|
+
});
|
|
757
747
|
},
|
|
758
748
|
onClose: (cb) => {
|
|
759
|
-
|
|
760
|
-
return () => closeHandlers.delete(cb);
|
|
749
|
+
socket.on("close", cb);
|
|
761
750
|
},
|
|
762
751
|
onError: (cb) => {
|
|
763
|
-
|
|
764
|
-
|
|
752
|
+
socket.on("error", cb);
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/gateway/transport/ws.ts
|
|
758
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
759
|
+
import { createServer as createHttpsServer } from "https";
|
|
760
|
+
import { readFileSync } from "fs";
|
|
761
|
+
|
|
762
|
+
// src/gateway/transport/wsChannel.ts
|
|
763
|
+
function createWsConnection(ws, peer, traceTag) {
|
|
764
|
+
return createFramedConnection(peer, wsRawChannel(ws, traceTag));
|
|
765
|
+
}
|
|
766
|
+
function wsRawChannel(ws, traceTag) {
|
|
767
|
+
return {
|
|
768
|
+
kind: "ws",
|
|
769
|
+
traceTag,
|
|
770
|
+
isOpen: () => ws.readyState === ws.OPEN,
|
|
771
|
+
writeFrame: (frame) => ws.send(JSON.stringify(frame)),
|
|
772
|
+
close: () => ws.close(),
|
|
773
|
+
onMessage: (cb) => {
|
|
774
|
+
ws.on("message", (data) => cb(data.toString()));
|
|
775
|
+
},
|
|
776
|
+
onClose: (cb) => {
|
|
777
|
+
ws.on("close", cb);
|
|
778
|
+
},
|
|
779
|
+
onError: (cb) => {
|
|
780
|
+
ws.on("error", cb);
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// src/gateway/transport/ws.ts
|
|
786
|
+
function createWsServerTransport(opts) {
|
|
787
|
+
if (!opts.allowNonLoopback && !isLoopbackHost(opts.host)) {
|
|
788
|
+
throw new Error(`gateway: refusing non-loopback bind without --insecure`);
|
|
789
|
+
}
|
|
790
|
+
let endpoint = null;
|
|
791
|
+
const scheme = opts.tls ? "wss" : "ws";
|
|
792
|
+
const rateLimit = createConnectRateLimiter(opts.rateLimitPerMin ?? 10);
|
|
793
|
+
const readEndpoint = () => {
|
|
794
|
+
if (!endpoint) {
|
|
795
|
+
throw new Error("gateway: WS transport has not started listening");
|
|
765
796
|
}
|
|
797
|
+
return endpoint;
|
|
766
798
|
};
|
|
799
|
+
return {
|
|
800
|
+
kind: "ws",
|
|
801
|
+
endpoint: readEndpoint,
|
|
802
|
+
describe: () => {
|
|
803
|
+
const ep = readEndpoint();
|
|
804
|
+
return {
|
|
805
|
+
kind: "ws",
|
|
806
|
+
host: ep.host,
|
|
807
|
+
port: ep.port,
|
|
808
|
+
url: ep.url,
|
|
809
|
+
tls: Boolean(opts.tls)
|
|
810
|
+
};
|
|
811
|
+
},
|
|
812
|
+
listen: (onConnection) => new Promise((resolve, reject) => {
|
|
813
|
+
const verifyClient = (info) => rateLimit.allow(info.req.socket.remoteAddress ?? "unknown");
|
|
814
|
+
const { wss, httpsServer } = createWss({
|
|
815
|
+
host: opts.host,
|
|
816
|
+
port: opts.port,
|
|
817
|
+
tls: opts.tls,
|
|
818
|
+
verifyClient
|
|
819
|
+
});
|
|
820
|
+
const onError = (err) => reject(err);
|
|
821
|
+
wss.once("error", onError);
|
|
822
|
+
if (httpsServer) httpsServer.once("error", onError);
|
|
823
|
+
const onListening = () => {
|
|
824
|
+
wss.off("error", onError);
|
|
825
|
+
if (httpsServer) httpsServer.off("error", onError);
|
|
826
|
+
const addr = httpsServer ? httpsServer.address() : wss.address();
|
|
827
|
+
if (typeof addr === "string" || addr === null) {
|
|
828
|
+
wss.close();
|
|
829
|
+
httpsServer?.close();
|
|
830
|
+
reject(
|
|
831
|
+
new Error("gateway: WS listener did not expose TCP address")
|
|
832
|
+
);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
endpoint = {
|
|
836
|
+
host: opts.host,
|
|
837
|
+
port: addr.port,
|
|
838
|
+
url: `${scheme}://${opts.host}:${addr.port}`
|
|
839
|
+
};
|
|
840
|
+
resolve({
|
|
841
|
+
close: () => new Promise((closeResolve) => {
|
|
842
|
+
for (const client2 of wss.clients) client2.terminate();
|
|
843
|
+
wss.close(() => {
|
|
844
|
+
if (httpsServer) httpsServer.close(() => closeResolve());
|
|
845
|
+
else closeResolve();
|
|
846
|
+
});
|
|
847
|
+
})
|
|
848
|
+
});
|
|
849
|
+
};
|
|
850
|
+
if (httpsServer) httpsServer.once("listening", onListening);
|
|
851
|
+
else wss.once("listening", onListening);
|
|
852
|
+
const pingIntervalMs = opts.pingIntervalMs ?? 15e3;
|
|
853
|
+
const pongTimeoutMs = opts.pongTimeoutMs ?? 3e4;
|
|
854
|
+
wss.on("connection", (ws) => {
|
|
855
|
+
attachHeartbeat(ws, pingIntervalMs, pongTimeoutMs);
|
|
856
|
+
onConnection(createWsConnection(ws, `${scheme}:${opts.host}`, "ws"));
|
|
857
|
+
});
|
|
858
|
+
})
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
function loadTlsOptions(tls) {
|
|
862
|
+
return {
|
|
863
|
+
cert: readFileSync(tls.certPath),
|
|
864
|
+
key: readFileSync(tls.keyPath)
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
function createWss(input) {
|
|
868
|
+
if (input.tls) {
|
|
869
|
+
const httpsServer = createHttpsServer(loadTlsOptions(input.tls));
|
|
870
|
+
httpsServer.listen({ host: input.host, port: input.port });
|
|
871
|
+
return {
|
|
872
|
+
wss: new WebSocketServer({
|
|
873
|
+
server: httpsServer,
|
|
874
|
+
verifyClient: input.verifyClient
|
|
875
|
+
}),
|
|
876
|
+
httpsServer
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
return {
|
|
880
|
+
wss: new WebSocketServer({
|
|
881
|
+
host: input.host,
|
|
882
|
+
port: input.port,
|
|
883
|
+
verifyClient: input.verifyClient
|
|
884
|
+
}),
|
|
885
|
+
httpsServer: null
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
function createConnectRateLimiter(maxPerMin) {
|
|
889
|
+
if (maxPerMin <= 0) return { allow: () => true };
|
|
890
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
891
|
+
let pruneCountdown = 256;
|
|
892
|
+
const prune = (cutoff) => {
|
|
893
|
+
for (const [ip, arr] of buckets) {
|
|
894
|
+
const fresh = arr.filter((t) => t > cutoff);
|
|
895
|
+
if (fresh.length === 0) buckets.delete(ip);
|
|
896
|
+
else if (fresh.length !== arr.length) buckets.set(ip, fresh);
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
return {
|
|
900
|
+
allow(ip) {
|
|
901
|
+
const now = Date.now();
|
|
902
|
+
const cutoff = now - 6e4;
|
|
903
|
+
pruneCountdown -= 1;
|
|
904
|
+
if (pruneCountdown <= 0) {
|
|
905
|
+
prune(cutoff);
|
|
906
|
+
pruneCountdown = 256;
|
|
907
|
+
}
|
|
908
|
+
const recent = (buckets.get(ip) ?? []).filter((t) => t > cutoff);
|
|
909
|
+
if (recent.length >= maxPerMin) {
|
|
910
|
+
buckets.set(ip, recent);
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
913
|
+
recent.push(now);
|
|
914
|
+
buckets.set(ip, recent);
|
|
915
|
+
return true;
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
function attachHeartbeat(ws, pingIntervalMs, pongTimeoutMs) {
|
|
920
|
+
if (pingIntervalMs <= 0) return;
|
|
921
|
+
let pongTimer = null;
|
|
922
|
+
const clearPongTimer = () => {
|
|
923
|
+
if (pongTimer) {
|
|
924
|
+
clearTimeout(pongTimer);
|
|
925
|
+
pongTimer = null;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
ws.on("pong", clearPongTimer);
|
|
929
|
+
const interval = setInterval(() => {
|
|
930
|
+
if (ws.readyState !== ws.OPEN) return;
|
|
931
|
+
try {
|
|
932
|
+
ws.ping();
|
|
933
|
+
} catch {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
if (!pongTimer) {
|
|
937
|
+
pongTimer = setTimeout(() => ws.terminate(), pongTimeoutMs);
|
|
938
|
+
}
|
|
939
|
+
}, pingIntervalMs);
|
|
940
|
+
const stop = () => {
|
|
941
|
+
clearInterval(interval);
|
|
942
|
+
clearPongTimer();
|
|
943
|
+
};
|
|
944
|
+
ws.on("close", stop);
|
|
945
|
+
ws.on("error", stop);
|
|
946
|
+
}
|
|
947
|
+
function createWsClientTransport(opts) {
|
|
948
|
+
return {
|
|
949
|
+
kind: "ws",
|
|
950
|
+
connect: () => connectWs(opts)
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
function wsClientOptionsForEndpoint(input) {
|
|
954
|
+
return {
|
|
955
|
+
url: input.url,
|
|
956
|
+
...input.timeoutMs !== void 0 ? { timeoutMs: input.timeoutMs } : {},
|
|
957
|
+
...input.tlsCaPath !== void 0 ? { tlsCaPath: input.tlsCaPath } : {}
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
async function connectWs(opts) {
|
|
961
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
962
|
+
const wsOpts = opts.tlsCaPath ? { ca: readFileSync(opts.tlsCaPath) } : {};
|
|
963
|
+
const ws = new WebSocket(opts.url, wsOpts);
|
|
964
|
+
await new Promise((resolve, reject) => {
|
|
965
|
+
const timer = setTimeout(() => {
|
|
966
|
+
ws.close();
|
|
967
|
+
reject(
|
|
968
|
+
new TransportUnreachableError(`connect timed out after ${timeoutMs}ms`)
|
|
969
|
+
);
|
|
970
|
+
}, timeoutMs);
|
|
971
|
+
ws.once("open", () => {
|
|
972
|
+
clearTimeout(timer);
|
|
973
|
+
resolve();
|
|
974
|
+
});
|
|
975
|
+
ws.once("error", (err) => {
|
|
976
|
+
clearTimeout(timer);
|
|
977
|
+
reject(
|
|
978
|
+
new TransportUnreachableError(
|
|
979
|
+
`gateway not reachable at ${opts.url}: ${err.message}`
|
|
980
|
+
)
|
|
981
|
+
);
|
|
982
|
+
});
|
|
983
|
+
});
|
|
984
|
+
return createWsConnection(ws, opts.url, "ws-client");
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// src/infra/db/openVersionedDb.ts
|
|
988
|
+
import fs5 from "fs";
|
|
989
|
+
import path4 from "path";
|
|
990
|
+
import Database from "better-sqlite3";
|
|
991
|
+
function migrateVersionedSchema(db, schema) {
|
|
992
|
+
db.exec(
|
|
993
|
+
"CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL)"
|
|
994
|
+
);
|
|
995
|
+
const existing = db.prepare("SELECT version FROM schema_version").get();
|
|
996
|
+
if (existing && existing.version > schema.version) {
|
|
997
|
+
throw schema.onNewerVersion?.(existing.version, schema.version) ?? new Error(
|
|
998
|
+
`Database has newer schema version ${existing.version} (expected <= ${schema.version}).`
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
db.transaction(() => {
|
|
1002
|
+
schema.migrate(db, existing?.version);
|
|
1003
|
+
if (!existing) {
|
|
1004
|
+
db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(
|
|
1005
|
+
schema.version
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
})();
|
|
1009
|
+
}
|
|
1010
|
+
function openVersionedDb(dbPath, options) {
|
|
1011
|
+
if (options.ensureDir && dbPath !== ":memory:") {
|
|
1012
|
+
fs5.mkdirSync(path4.dirname(dbPath), {
|
|
1013
|
+
recursive: true,
|
|
1014
|
+
...options.dirMode !== void 0 ? { mode: options.dirMode } : {}
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
const db = new Database(dbPath);
|
|
1018
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
1019
|
+
if (options.foreignKeys) {
|
|
1020
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
1021
|
+
}
|
|
1022
|
+
if (options.version === void 0) {
|
|
1023
|
+
options.migrate(db, void 0);
|
|
1024
|
+
} else {
|
|
1025
|
+
migrateVersionedSchema(db, {
|
|
1026
|
+
version: options.version,
|
|
1027
|
+
migrate: options.migrate,
|
|
1028
|
+
...options.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
return db;
|
|
767
1032
|
}
|
|
768
1033
|
|
|
769
1034
|
// src/shared/gateway-protocol/channelRequestId.ts
|
|
@@ -791,12 +1056,14 @@ export {
|
|
|
791
1056
|
removeDashboardClientConfig,
|
|
792
1057
|
openVersionedDb,
|
|
793
1058
|
TransportUnreachableError,
|
|
794
|
-
traceGatewayFrame,
|
|
795
1059
|
createUdsServerTransport,
|
|
796
1060
|
createUdsClientTransport,
|
|
797
1061
|
resolveGatewayPaths,
|
|
798
1062
|
resolveListenSpec,
|
|
799
1063
|
isLoopbackHost,
|
|
1064
|
+
createWsServerTransport,
|
|
1065
|
+
createWsClientTransport,
|
|
1066
|
+
wsClientOptionsForEndpoint,
|
|
800
1067
|
CHANNEL_REQUEST_ID_REGEX,
|
|
801
1068
|
generateChannelRequestId,
|
|
802
1069
|
isValidChannelRequestId,
|
|
@@ -818,4 +1085,4 @@ export {
|
|
|
818
1085
|
trackSetupCompleted,
|
|
819
1086
|
refreshDashboardAccessToken
|
|
820
1087
|
};
|
|
821
|
-
//# sourceMappingURL=chunk-
|
|
1088
|
+
//# sourceMappingURL=chunk-3U37HT4T.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isLoopbackHost
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3U37HT4T.js";
|
|
4
4
|
|
|
5
5
|
// src/gateway/auth.ts
|
|
6
6
|
import crypto from "crypto";
|
|
@@ -73,4 +73,4 @@ export {
|
|
|
73
73
|
timingSafeTokenEqual,
|
|
74
74
|
requireTokenForBind
|
|
75
75
|
};
|
|
76
|
-
//# sourceMappingURL=chunk-
|
|
76
|
+
//# sourceMappingURL=chunk-4NX4WFPB.js.map
|
|
@@ -952,18 +952,9 @@ function readConfigFile(configPath, baseDir) {
|
|
|
952
952
|
);
|
|
953
953
|
}
|
|
954
954
|
const plugins = (raw.plugins ?? []).map((p) => {
|
|
955
|
-
if (isMarketplaceRef(p))
|
|
956
|
-
try {
|
|
957
|
-
return resolveMarketplacePlugin(p);
|
|
958
|
-
} catch (error) {
|
|
959
|
-
console.error(
|
|
960
|
-
`Warning: skipping plugin "${p}": ${error.message}`
|
|
961
|
-
);
|
|
962
|
-
return null;
|
|
963
|
-
}
|
|
964
|
-
}
|
|
955
|
+
if (isMarketplaceRef(p)) return p;
|
|
965
956
|
return path4.isAbsolute(p) ? p : path4.resolve(baseDir, p);
|
|
966
|
-
})
|
|
957
|
+
});
|
|
967
958
|
const additionalDirectories = (raw.additionalDirectories ?? []).map(
|
|
968
959
|
(dir) => path4.isAbsolute(dir) ? dir : path4.resolve(baseDir, dir)
|
|
969
960
|
);
|
|
@@ -980,7 +971,12 @@ function readConfigFile(configPath, baseDir) {
|
|
|
980
971
|
telemetry: raw.telemetry,
|
|
981
972
|
telemetryDiagnostics: raw.telemetryDiagnostics,
|
|
982
973
|
deviceId: raw.deviceId,
|
|
983
|
-
channels: raw.channels
|
|
974
|
+
channels: raw.channels,
|
|
975
|
+
// Personal capabilities are stored opaque. In particular skill `path`
|
|
976
|
+
// is NOT relative-resolved against baseDir (R1) — Issue 3 resolves it
|
|
977
|
+
// to an absolute path at install time.
|
|
978
|
+
mcpServers: raw.mcpServers,
|
|
979
|
+
skills: raw.skills
|
|
984
980
|
};
|
|
985
981
|
}
|
|
986
982
|
function readExistingConfig(configPath) {
|
|
@@ -2210,6 +2206,7 @@ export {
|
|
|
2210
2206
|
gatherMarketplaceWorkflowSources,
|
|
2211
2207
|
resolveWorkflowInstall,
|
|
2212
2208
|
pullMarketplaceRepo,
|
|
2209
|
+
resolveMarketplacePlugin,
|
|
2213
2210
|
projectConfigPath,
|
|
2214
2211
|
readConfig,
|
|
2215
2212
|
resolveActiveWorkflow,
|
|
@@ -2229,4 +2226,4 @@ export {
|
|
|
2229
2226
|
compileWorkflowPlan,
|
|
2230
2227
|
collectMcpServersWithOptions
|
|
2231
2228
|
};
|
|
2232
|
-
//# sourceMappingURL=chunk-
|
|
2229
|
+
//# sourceMappingURL=chunk-73V7GXV6.js.map
|