[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,171 @@
"use strict";
/**
* Shell-aware command blocklist helpers shared by the main-process AI exec
* paths (in-app bridge handlers, MCP TCP bridge handlers, terminal worker).
*
* The default table in lib/commandBlocklist.json is grouped into
* common / posix / powershell patterns. Callers pass the best shell kind they
* can resolve for the target session:
* - live session objects: resolveSessionBlocklistShellKind(session) mirrors
* the inputs the AI PTY wrapper uses (confirmed kind, live idle prompt,
* remote login-shell hint)
* - metadata-only paths: meta.shellType (often empty; callers that know a
* downstream authoritative check re-runs the defaults should fall back to
* checkBlocklistCommonOnly instead of the strict full table, so
* POSIX-only patterns never block PowerShell-native commands)
*
* User-added patterns (settings list entries that are not part of the default
* table) always apply, on every shell.
*/
const {
DEFAULT_COMMAND_BLOCKLIST,
COMMON_PATTERNS,
isDefaultBlocklistPattern,
selectDefaultBlocklistPatterns,
} = require("../../../lib/commandBlocklist.cjs");
const { resolveEffectiveShellKind } = require("./ptyExecHelpers.cjs");
const {
getFreshIdlePrompt,
isDefaultPowerShellPromptLine,
isDefaultCmdPromptLine,
isDefaultPosixPromptLine,
stripAnsi,
} = require("./shellUtils.cjs");
function compilePatterns(patterns) {
return patterns
.map((pattern) => {
try {
return { pattern, regex: new RegExp(pattern, "i") };
} catch {
return null;
}
})
.filter(Boolean);
}
const compiledDefaultCache = new Map();
function compiledDefaultPatternsFor(shellKind) {
const key = String(shellKind || "").toLowerCase();
let compiled = compiledDefaultCache.get(key);
if (!compiled) {
compiled = compilePatterns(selectDefaultBlocklistPatterns(key));
compiledDefaultCache.set(key, compiled);
}
return compiled;
}
const compiledCommonPatterns = compilePatterns(COMMON_PATTERNS);
// User blocklists are stable between settings updates; compile each list once.
const compiledUserCache = new WeakMap();
function compiledUserPatterns(blocklist) {
let compiled = compiledUserCache.get(blocklist);
if (!compiled) {
const userPatterns = blocklist.filter(
(pattern) => !isDefaultBlocklistPattern(pattern),
);
compiled = compilePatterns(userPatterns);
compiledUserCache.set(blocklist, compiled);
}
return compiled;
}
function firstMatch(command, compiled) {
for (const { pattern, regex } of compiled) {
if (regex.test(command)) {
return { blocked: true, matchedPattern: pattern };
}
}
return null;
}
function firstEnabledDefaultMatch(command, compiled, enabledPatterns) {
for (const { pattern, regex } of compiled) {
if (enabledPatterns.has(pattern) && regex.test(command)) {
return { blocked: true, matchedPattern: pattern };
}
}
return null;
}
function normalizeConfiguredBlocklist(blocklist) {
return Array.isArray(blocklist) ? blocklist : DEFAULT_COMMAND_BLOCKLIST;
}
/**
* User additions + default patterns selected for shellKind.
* Unknown / empty shell kinds keep the strict full default table.
*/
function checkBlocklistForShell(command, shellKind, configuredBlocklist = DEFAULT_COMMAND_BLOCKLIST) {
const blocklist = normalizeConfiguredBlocklist(configuredBlocklist);
const userMatch = firstMatch(command, compiledUserPatterns(blocklist));
if (userMatch) return userMatch;
return firstEnabledDefaultMatch(
command,
compiledDefaultPatternsFor(shellKind),
new Set(blocklist),
) || { blocked: false };
}
/**
* User additions + shell-independent (common) default patterns only.
* For metadata-only call sites that know a downstream authoritative check
* re-runs the full shell-selected defaults on the live session.
*/
function checkBlocklistCommonOnly(command, configuredBlocklist = DEFAULT_COMMAND_BLOCKLIST) {
const blocklist = normalizeConfiguredBlocklist(configuredBlocklist);
const userMatch = firstMatch(command, compiledUserPatterns(blocklist));
if (userMatch) return userMatch;
return firstEnabledDefaultMatch(command, compiledCommonPatterns, new Set(blocklist)) || { blocked: false };
}
/**
* Best-effort shell kind for a live session, mirroring the inputs the AI PTY
* wrapper uses (ptyExecHelpers.resolveEffectiveShellKind): confirmed shell
* kind, live idle prompt, and the remote login-shell probe hint.
*/
function resolveSessionBlocklistShellKind(session) {
if (!session || typeof session !== "object") return "";
let prompt = null;
try {
prompt = getFreshIdlePrompt(session);
} catch {
prompt = null;
}
try {
const baseKind = session.shellKind === "unknown" ? "" : (session.shellKind || "");
const loginShellHint = session._loginShellKind || "";
const resolved = resolveEffectiveShellKind(baseKind, prompt, { loginShellHint }) || "";
if (baseKind || loginShellHint) return resolved;
const lastPromptLine = stripAnsi(String(prompt || ""))
.replace(/\r/g, "\n")
.split("\n")
.pop()
.replace(/\s+$/, "");
if (
isDefaultPowerShellPromptLine(lastPromptLine)
|| isDefaultCmdPromptLine(lastPromptLine)
|| isDefaultPosixPromptLine(lastPromptLine)
) {
return resolved;
}
// Wrapper selection defaults an unclassified remote session to POSIX.
// Safety keeps it unknown so failed probes retain the strict all-groups
// fallback instead of silently omitting PowerShell rules.
return "";
} catch {
return session.shellKind || session._loginShellKind || "";
}
}
module.exports = {
checkBlocklistForShell,
checkBlocklistCommonOnly,
resolveSessionBlocklistShellKind,
};