[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
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:
@@ -0,0 +1,189 @@
|
||||
"use strict";
|
||||
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
function normalizeOpenCodePath(targetPath, platform = process.platform) {
|
||||
return platform === "win32"
|
||||
? targetPath.replace(/\\/g, "/")
|
||||
: targetPath;
|
||||
}
|
||||
|
||||
function appendOpenCodePathPattern(baseDir, suffix) {
|
||||
const trimmedSuffix = suffix.replace(/^\//, "");
|
||||
return baseDir.endsWith("/")
|
||||
? `${baseDir}${trimmedSuffix}`
|
||||
: `${baseDir}/${trimmedSuffix}`;
|
||||
}
|
||||
|
||||
function toOpenCodeDirectoryBase(dirPath, options = {}) {
|
||||
if (!dirPath || typeof dirPath !== "string") return null;
|
||||
const pathModule = options.pathModule || path;
|
||||
const platform = options.platform || process.platform;
|
||||
try {
|
||||
const resolved = pathModule.resolve(dirPath);
|
||||
let baseDir = resolved;
|
||||
if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) {
|
||||
baseDir = pathModule.dirname(resolved);
|
||||
}
|
||||
return normalizeOpenCodePath(baseDir, platform);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function toOpenCodeDirectoryGlob(dirPath, options = {}) {
|
||||
const baseDir = toOpenCodeDirectoryBase(dirPath, options);
|
||||
return baseDir ? appendOpenCodePathPattern(baseDir, "**") : null;
|
||||
}
|
||||
|
||||
function toOpenCodeDirectoryPermissionPatterns(dirPath, options = {}) {
|
||||
const baseDir = toOpenCodeDirectoryBase(dirPath, options);
|
||||
return baseDir
|
||||
? [
|
||||
baseDir,
|
||||
appendOpenCodePathPattern(baseDir, "*"),
|
||||
appendOpenCodePathPattern(baseDir, "**"),
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
function toOpenCodeFileParentGlob(filePath, options = {}) {
|
||||
if (!filePath || typeof filePath !== "string") return null;
|
||||
const pathModule = options.pathModule || path;
|
||||
try {
|
||||
return toOpenCodeDirectoryGlob(pathModule.dirname(pathModule.resolve(filePath)), options);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function toOpenCodeFileParentPermissionPatterns(filePath, options = {}) {
|
||||
if (!filePath || typeof filePath !== "string") return [];
|
||||
const pathModule = options.pathModule || path;
|
||||
try {
|
||||
return toOpenCodeDirectoryPermissionPatterns(pathModule.dirname(pathModule.resolve(filePath)), options);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function dedupePatterns(patterns) {
|
||||
return [...new Set(patterns.filter(Boolean))];
|
||||
}
|
||||
|
||||
// OpenCode discovers native agent skills from these well-known directories:
|
||||
// its global config dirs (~/.opencode and ~/.config/opencode, both "skill"
|
||||
// and "skills" spellings), Claude/agents-compatible dirs, project-level
|
||||
// .opencode/.claude/.agents dirs, and the remote-skill download cache.
|
||||
// Reads inside them must stay allowed even though Netcatty otherwise locks
|
||||
// external directory access down, or loading a skill's reference files fails
|
||||
// with an OpenCode permission error (issue #1939).
|
||||
const OPENCODE_NATIVE_SKILL_DIR_SUFFIXES = [
|
||||
".opencode/skill",
|
||||
".opencode/skills",
|
||||
".config/opencode/skill",
|
||||
".config/opencode/skills",
|
||||
".claude/skills",
|
||||
".agents/skills",
|
||||
".cache/opencode/skills",
|
||||
];
|
||||
|
||||
// OpenCode's `read` permission checks match worktree-relative paths (e.g.
|
||||
// "../../.opencode/skills/foo/references/doc.md") while `external_directory`
|
||||
// checks match absolute directory globs ("C:/Users/me/.opencode/skills/foo/*").
|
||||
// Anchoring each well-known suffix behind a leading wildcard covers both
|
||||
// forms on every platform (OpenCode normalizes "\\" to "/" before matching).
|
||||
function buildOpenCodeNativeSkillPermissionPatterns() {
|
||||
return OPENCODE_NATIVE_SKILL_DIR_SUFFIXES.flatMap((suffix) => [
|
||||
`*${suffix}`,
|
||||
`*${suffix}/*`,
|
||||
`*${suffix}/**`,
|
||||
]);
|
||||
}
|
||||
|
||||
// OpenCode's default rules gate `.env` secret files behind approval. The
|
||||
// broad skill-directory read allows above would win over those defaults
|
||||
// (last matching rule wins), so re-deny dot-env files inside skill dirs
|
||||
// after the allow entries to keep secret-file protection intact.
|
||||
function buildOpenCodeNativeSkillEnvDenyPatterns() {
|
||||
return OPENCODE_NATIVE_SKILL_DIR_SUFFIXES.flatMap((suffix) => [
|
||||
`*${suffix}/**.env`,
|
||||
`*${suffix}/**.env.*`,
|
||||
]);
|
||||
}
|
||||
|
||||
// Base rules shared by every tool-integration mode so OpenCode's native
|
||||
// skills keep working: allow loading skills and reading their files while
|
||||
// still denying all other external directory access.
|
||||
function buildOpenCodeNativeSkillsPermissionRules() {
|
||||
const external_directory = { "*": "deny" };
|
||||
const read = {};
|
||||
for (const pattern of buildOpenCodeNativeSkillPermissionPatterns()) {
|
||||
external_directory[pattern] = "allow";
|
||||
read[pattern] = "allow";
|
||||
}
|
||||
for (const pattern of buildOpenCodeNativeSkillEnvDenyPatterns()) {
|
||||
read[pattern] = "deny";
|
||||
}
|
||||
return {
|
||||
skill: "allow",
|
||||
read,
|
||||
external_directory,
|
||||
};
|
||||
}
|
||||
|
||||
function buildNetcattySkillsOpenCodePathAllowlist({
|
||||
launcherPath,
|
||||
cliScriptPath,
|
||||
skillPath,
|
||||
discoveryFilePath,
|
||||
cliStateDir,
|
||||
runtimeBinaryPath,
|
||||
tempDir,
|
||||
extraFilePaths,
|
||||
} = {}, options = {}) {
|
||||
const filePaths = [
|
||||
launcherPath,
|
||||
cliScriptPath,
|
||||
skillPath,
|
||||
discoveryFilePath,
|
||||
runtimeBinaryPath,
|
||||
...(Array.isArray(extraFilePaths) ? extraFilePaths : []),
|
||||
];
|
||||
return dedupePatterns([
|
||||
...filePaths.flatMap((filePath) => toOpenCodeFileParentPermissionPatterns(filePath, options)),
|
||||
...(cliStateDir ? toOpenCodeDirectoryPermissionPatterns(cliStateDir, options) : []),
|
||||
...(tempDir ? toOpenCodeDirectoryPermissionPatterns(tempDir, options) : []),
|
||||
]);
|
||||
}
|
||||
|
||||
function buildOpenCodeSkillsPermissionRules(pathAllowlist = []) {
|
||||
const { read, external_directory } = buildOpenCodeNativeSkillsPermissionRules();
|
||||
for (const pattern of pathAllowlist) {
|
||||
external_directory[pattern] = "allow";
|
||||
read[pattern] = "allow";
|
||||
}
|
||||
|
||||
return {
|
||||
bash: "allow",
|
||||
read,
|
||||
list: "deny",
|
||||
glob: "deny",
|
||||
grep: "deny",
|
||||
skill: "allow",
|
||||
external_directory,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildNetcattySkillsOpenCodePathAllowlist,
|
||||
buildOpenCodeNativeSkillEnvDenyPatterns,
|
||||
buildOpenCodeNativeSkillPermissionPatterns,
|
||||
buildOpenCodeNativeSkillsPermissionRules,
|
||||
buildOpenCodeSkillsPermissionRules,
|
||||
toOpenCodeDirectoryPermissionPatterns,
|
||||
toOpenCodeDirectoryGlob,
|
||||
toOpenCodeFileParentPermissionPatterns,
|
||||
toOpenCodeFileParentGlob,
|
||||
};
|
||||
Reference in New Issue
Block a user