consortium
Version:
Remote control and session sharing CLI for AI coding agents
267 lines (257 loc) • 7.22 kB
JavaScript
import { e as extend, s as string, a as encodeUtf8 } from './string-BC-7T2zc.mjs';
import { B as BufferedReadableStream, c as ExactReadableEndedError, s as struct } from './struct-deserialize-DqeMe-dl.mjs';
class AutoDisposable {
#disposables = [];
constructor() {
this.dispose = this.dispose.bind(this);
}
addDisposable(disposable) {
this.#disposables.push(disposable);
return disposable;
}
dispose() {
for (const disposable of this.#disposables) {
disposable.dispose();
}
this.#disposables = [];
}
}
class EventEmitter {
listeners = [];
constructor() {
this.event = this.event.bind(this);
}
addEventListener(info) {
this.listeners.push(info);
const remove = () => {
const index = this.listeners.indexOf(info);
if (index !== -1) {
this.listeners.splice(index, 1);
}
};
remove.dispose = remove;
return remove;
}
event = (listener, thisArg, ...args) => {
const info = {
listener,
thisArg,
args
};
return this.addEventListener(info);
};
fire(e) {
for (const info of this.listeners.slice()) {
info.listener.call(info.thisArg, e, ...info.args);
}
}
dispose() {
this.listeners.length = 0;
}
}
const Undefined = Symbol("undefined");
class StickyEventEmitter extends EventEmitter {
#value = Undefined;
addEventListener(info) {
if (this.#value !== Undefined) {
info.listener.call(info.thisArg, this.#value, ...info.args);
}
return super.addEventListener(info);
}
fire(e) {
this.#value = e;
super.fire(e);
}
}
class AdbServiceBase extends AutoDisposable {
#adb;
get adb() {
return this.#adb;
}
constructor(adb) {
super();
this.#adb = adb;
}
}
function hexCharToNumber(char) {
if (char < 48) {
throw new TypeError(`Invalid hex char ${char}`);
}
if (char < 58) {
return char - 48;
}
if (char < 65) {
throw new TypeError(`Invalid hex char ${char}`);
}
if (char < 71) {
return char - 55;
}
if (char < 97) {
throw new TypeError(`Invalid hex char ${char}`);
}
if (char < 103) {
return char - 87;
}
throw new TypeError(`Invalid hex char ${char}`);
}
function hexToNumber(data) {
let result = 0;
for (let i = 0; i < data.length; i += 1) {
result = result << 4 | hexCharToNumber(data[i]);
}
return result;
}
function write4HexDigits(buffer, index, value) {
const start = index;
index += 3;
while (index >= start && value > 0) {
const digit = value & 15;
value >>= 4;
if (digit < 10) {
buffer[index] = digit + 48;
} else {
buffer[index] = digit + 87;
}
index -= 1;
}
while (index >= start) {
buffer[index] = 48;
index -= 1;
}
}
const NOOP = /* @__NO_SIDE_EFFECTS__ */ () => {
};
function unreachable(...args) {
throw new Error("Unreachable. Arguments:\n" + args.join("\n"));
}
function sequenceEqual(a, b) {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i += 1) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
const AdbReverseStringResponse = struct({
length: string(4),
content: string({
field: "length",
convert(value) {
return Number.parseInt(value, 16);
},
back(value) {
return value.toString(16).padStart(4, "0");
}
})
}, { littleEndian: true });
class AdbReverseError extends Error {
constructor(message) {
super(message);
}
}
class AdbReverseNotSupportedError extends AdbReverseError {
constructor() {
super("ADB reverse tunnel is not supported on this device when connected wirelessly.");
}
}
const AdbReverseErrorResponse = extend(AdbReverseStringResponse, {}, {
postDeserialize(value) {
if (value.content === "more than one device/emulator") {
throw new AdbReverseNotSupportedError();
} else {
throw new AdbReverseError(value.content);
}
}
});
function decimalToNumber(buffer) {
let value = 0;
for (const byte of buffer) {
if (byte < 48 || byte > 57) {
return value;
}
value = value * 10 + byte - 48;
}
return value;
}
const OKAY = encodeUtf8("OKAY");
class AdbReverseService extends AdbServiceBase {
#deviceAddressToLocalAddress = /* @__PURE__ */ new Map();
async createBufferedStream(service) {
const socket = await this.adb.createSocket(service);
return new BufferedReadableStream(socket.readable);
}
async sendRequest(service) {
const stream = await this.createBufferedStream(service);
const response = await stream.readExactly(4);
if (!sequenceEqual(response, OKAY)) {
await AdbReverseErrorResponse.deserialize(stream);
}
return stream;
}
/**
* Get a list of all reverse port forwarding on the device.
*/
async list() {
const stream = await this.createBufferedStream("reverse:list-forward");
const response = await AdbReverseStringResponse.deserialize(stream);
return response.content.split("\n").filter((line) => !!line).map((line) => {
const [deviceSerial, localName, remoteName] = line.split(" ");
return { deviceSerial, localName, remoteName };
});
}
/**
* Add a reverse port forwarding for a program that already listens on a port.
*/
async addExternal(deviceAddress, localAddress) {
const stream = await this.sendRequest(`reverse:forward:${deviceAddress};${localAddress}`);
if (deviceAddress.startsWith("tcp:")) {
const position = stream.position;
try {
const length = hexToNumber(await stream.readExactly(4));
const port = decimalToNumber(await stream.readExactly(length));
deviceAddress = `tcp:${port}`;
} catch (e) {
if (e instanceof ExactReadableEndedError && stream.position === position) ; else {
throw e;
}
}
}
return deviceAddress;
}
/**
* Add a reverse port forwarding.
*/
async add(deviceAddress, handler, localAddress) {
localAddress = await this.adb.transport.addReverseTunnel(handler, localAddress);
try {
deviceAddress = await this.addExternal(deviceAddress, localAddress);
this.#deviceAddressToLocalAddress.set(deviceAddress, localAddress);
return deviceAddress;
} catch (e) {
await this.adb.transport.removeReverseTunnel(localAddress);
throw e;
}
}
/**
* Remove a reverse port forwarding.
*/
async remove(deviceAddress) {
const localAddress = this.#deviceAddressToLocalAddress.get(deviceAddress);
if (localAddress) {
await this.adb.transport.removeReverseTunnel(localAddress);
}
await this.sendRequest(`reverse:killforward:${deviceAddress}`);
}
/**
* Remove all reverse port forwarding, including the ones added by other programs.
*/
async removeAll() {
await this.adb.transport.clearReverseTunnels();
this.#deviceAddressToLocalAddress.clear();
await this.sendRequest(`reverse:killforward-all`);
}
}
export { AdbReverseNotSupportedError as A, EventEmitter as E, NOOP as N, StickyEventEmitter as S, AdbServiceBase as a, AdbReverseService as b, AdbReverseError as c, AdbReverseErrorResponse as d, hexToNumber as h, sequenceEqual as s, unreachable as u, write4HexDigits as w };