102 lines
2.8 KiB
JavaScript
102 lines
2.8 KiB
JavaScript
|
|
"use strict";
|
||
|
|
|
||
|
|
const fs = require("node:fs");
|
||
|
|
const net = require("node:net");
|
||
|
|
|
||
|
|
const { getCliDiscoveryFilePath } = require("./discoveryPath.cjs");
|
||
|
|
const { CAPABILITY_SURFACES } = require("../capabilities/constants.cjs");
|
||
|
|
const { createNdjsonRpcClient } = require("../capabilities/rpcTransport.cjs");
|
||
|
|
|
||
|
|
function createError(code, message) {
|
||
|
|
const err = new Error(message);
|
||
|
|
err.code = code;
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
function loadDiscovery() {
|
||
|
|
const discoveryPath = getCliDiscoveryFilePath();
|
||
|
|
let raw;
|
||
|
|
try {
|
||
|
|
raw = fs.readFileSync(discoveryPath, "utf8");
|
||
|
|
} catch (err) {
|
||
|
|
throw createError(
|
||
|
|
"APP_NOT_RUNNING",
|
||
|
|
`Netcatty is not running or discovery file is missing at ${discoveryPath}. Start Netcatty first.`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
let parsed;
|
||
|
|
try {
|
||
|
|
parsed = JSON.parse(raw);
|
||
|
|
} catch (err) {
|
||
|
|
throw createError(
|
||
|
|
"DISCOVERY_INVALID",
|
||
|
|
`Netcatty discovery file at ${discoveryPath} is invalid JSON.`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!parsed?.port || !parsed?.token) {
|
||
|
|
throw createError(
|
||
|
|
"DISCOVERY_INVALID",
|
||
|
|
`Netcatty discovery file at ${discoveryPath} is missing required port/token fields.`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return parsed;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function connectClient() {
|
||
|
|
const discovery = loadDiscovery();
|
||
|
|
const socket = await new Promise((resolve, reject) => {
|
||
|
|
const sock = net.createConnection({ host: "127.0.0.1", port: discovery.port }, () => resolve(sock));
|
||
|
|
sock.setEncoding("utf8");
|
||
|
|
sock.once("error", (err) => {
|
||
|
|
reject(createError("CONNECT_FAILED", `Failed to connect to Netcatty TCP bridge: ${err?.message || err}`));
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
const client = createNdjsonRpcClient({
|
||
|
|
socket,
|
||
|
|
surface: CAPABILITY_SURFACES.BUILTIN,
|
||
|
|
createError,
|
||
|
|
messages: {
|
||
|
|
connectionClosed: "Connection to Netcatty TCP bridge closed.",
|
||
|
|
connectionClosedWhileCall: "Connection to Netcatty TCP bridge is closed.",
|
||
|
|
connectionError: (error) => `Connection to Netcatty TCP bridge failed: ${error?.message || error}`,
|
||
|
|
rpcTimeout: (method, timeoutMs) => (
|
||
|
|
`Timed out waiting for Netcatty RPC response to "${method}" after ${timeoutMs}ms.`
|
||
|
|
),
|
||
|
|
writeFailed: (method, error) => (
|
||
|
|
`Failed to send Netcatty RPC "${method}": ${error?.message || error}`
|
||
|
|
),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const authResult = await client.call("auth/verify", { token: discovery.token });
|
||
|
|
if (!authResult?.ok) {
|
||
|
|
throw createError("AUTH_FAILED", "Failed to authenticate to Netcatty TCP bridge.");
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const statusResult = await client.call("netcatty/getStatus", {});
|
||
|
|
client.ingestBridgeStatus(statusResult);
|
||
|
|
} catch {
|
||
|
|
// Keep the default RPC timeout when bridge status cannot be fetched.
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
discovery,
|
||
|
|
async call(method, params) {
|
||
|
|
return await client.call(method, params);
|
||
|
|
},
|
||
|
|
close() {
|
||
|
|
client.close();
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
connectClient,
|
||
|
|
createError,
|
||
|
|
};
|