[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
"use strict";
const os = require("node:os");
const path = require("node:path");
const fs = require("node:fs");
const EXTERNAL_MCP_STATE_DIR_NAME = "external-mcp";
const EXTERNAL_MCP_DISCOVERY_ENV_VAR = "NETCATTY_EXTERNAL_MCP_DISCOVERY_FILE";
const EXTERNAL_MCP_CHAT_SESSION_ID = "__external_mcp__";
const FALLBACK_APP_DATA_DIR_NAME = "Netcatty";
function toUnpackedAsarPath(filePath) {
return filePath.replace(/app\.asar([\\/])/, "app.asar.unpacked$1");
}
function getDefaultAppDataDirName(options = {}) {
const packageJsonPaths = Array.isArray(options.packageJsonPaths) && options.packageJsonPaths.length > 0
? options.packageJsonPaths
: [
process.resourcesPath ? path.join(process.resourcesPath, "app.asar", "package.json") : null,
path.resolve(__dirname, "../../package.json"),
path.join(process.cwd(), "package.json"),
].filter(Boolean);
for (const packageJsonPath of packageJsonPaths) {
try {
const packageJson = require(packageJsonPath);
if (typeof packageJson?.productName === "string" && packageJson.productName) {
return packageJson.productName;
}
} catch {
// Try next candidate.
}
}
// Prefer Electron productName casing over package.json "name" (netcatty).
return FALLBACK_APP_DATA_DIR_NAME;
}
function getPlatformUserDataRoot(appDataDirName) {
if (process.platform === "darwin") {
return path.join(os.homedir(), "Library", "Application Support", appDataDirName);
}
if (process.platform === "win32") {
const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
return path.join(appData, appDataDirName);
}
const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
return path.join(xdgConfigHome, appDataDirName);
}
function getDefaultUserDataDir() {
return getPlatformUserDataRoot(getDefaultAppDataDirName());
}
/**
* Candidate userData roots the launcher may need when env is missing.
* Includes packaged (Netcatty), lowercase package name, and Electron Dev (/dev).
*/
function listCandidateUserDataDirs(options = {}) {
const names = Array.from(new Set([
getDefaultAppDataDirName(options),
"Netcatty",
"netcatty",
"Netcatty Dev",
].filter(Boolean)));
const roots = [];
for (const name of names) {
const root = getPlatformUserDataRoot(name);
roots.push(root);
roots.push(path.join(root, "dev"));
}
return Array.from(new Set(roots));
}
function getConfiguredDiscoveryFilePath() {
return process.env[EXTERNAL_MCP_DISCOVERY_ENV_VAR] || null;
}
function getExternalMcpStateDir(options = {}) {
const discoveryFilePath = getConfiguredDiscoveryFilePath();
if (discoveryFilePath) {
return path.dirname(discoveryFilePath);
}
const userDataDir = typeof options.userDataDir === "string" && options.userDataDir
? options.userDataDir
: getDefaultUserDataDir();
return path.join(userDataDir, EXTERNAL_MCP_STATE_DIR_NAME);
}
function getExternalMcpDiscoveryFilePath(options = {}) {
const discoveryFilePath = getConfiguredDiscoveryFilePath();
if (discoveryFilePath) {
return discoveryFilePath;
}
return path.join(getExternalMcpStateDir(options), "discovery.json");
}
/**
* Resolve an existing discovery file for launcher/bootstrap use.
* Prefers the env override, then the default path, then common Electron userData variants.
*/
function resolveExistingExternalMcpDiscoveryFilePath(options = {}) {
const configured = getConfiguredDiscoveryFilePath();
// Explicit client env must not silently fall back to another profile's file.
if (configured) {
return configured;
}
const primary = getExternalMcpDiscoveryFilePath(
options.userDataDir ? { userDataDir: options.userDataDir } : {},
);
if (fs.existsSync(primary)) {
return primary;
}
for (const userDataDir of listCandidateUserDataDirs(options)) {
const candidate = path.join(userDataDir, EXTERNAL_MCP_STATE_DIR_NAME, "discovery.json");
if (candidate !== primary && fs.existsSync(candidate)) {
return candidate;
}
}
return primary;
}
function getExternalMcpLauncherPath() {
const fileName = process.platform === "win32"
? "netcatty-external-mcp.cmd"
: "netcatty-external-mcp";
return toUnpackedAsarPath(path.join(__dirname, fileName));
}
function buildDiscoveryEnv(discoveryFilePath) {
if (!discoveryFilePath) return {};
return { [EXTERNAL_MCP_DISCOVERY_ENV_VAR]: discoveryFilePath };
}
function formatDiscoveryEnvCliFlags(discoveryEnv, style = "codex") {
const entries = Object.entries(discoveryEnv || {}).filter(([, value]) => typeof value === "string" && value);
if (entries.length === 0) return [];
if (style === "claude" || style === "grok") {
return entries.flatMap(([key, value]) => ["-e", `${key}=${value}`]);
}
// Codex: --env KEY=VALUE
return entries.flatMap(([key, value]) => ["--env", `${key}=${value}`]);
}
module.exports = {
getDefaultAppDataDirName,
getExternalMcpStateDir,
getExternalMcpDiscoveryFilePath,
resolveExistingExternalMcpDiscoveryFilePath,
getExternalMcpLauncherPath,
listCandidateUserDataDirs,
buildDiscoveryEnv,
formatDiscoveryEnvCliFlags,
EXTERNAL_MCP_DISCOVERY_ENV_VAR,
EXTERNAL_MCP_CHAT_SESSION_ID,
EXTERNAL_MCP_STATE_DIR_NAME,
};