pyb-ts
Version:
PYB-CLI - Minimal AI Agent with multi-model support and CLI interface
195 lines (194 loc) • 6.28 kB
JavaScript
import { BashTool, inputSchema } from "./tools/BashTool/BashTool.js";
import { FileEditTool } from "./tools/FileEditTool/FileEditTool.js";
import { FileWriteTool } from "./tools/FileWriteTool/FileWriteTool.js";
import { NotebookEditTool } from "./tools/NotebookEditTool/NotebookEditTool.js";
import { getCommandSubcommandPrefix, splitCommand } from "./utils/commands.js";
import {
getCurrentProjectConfig,
saveCurrentProjectConfig
} from "@utils/config";
import { AbortError } from "./utils/errors.js";
import { logError } from "./utils/log.js";
import { grantWritePermissionForOriginalDir } from "./utils/permissions/filesystem.js";
import { getCwd } from "./utils/state.js";
import { PRODUCT_NAME } from "./constants/product.js";
const SAFE_COMMANDS = /* @__PURE__ */ new Set([
"git status",
"git diff",
"git log",
"git branch",
"pwd",
"tree",
"date",
"which"
]);
const bashToolCommandHasExactMatchPermission = (tool, command, allowedTools) => {
if (SAFE_COMMANDS.has(command)) {
return true;
}
if (allowedTools.includes(getPermissionKey(tool, { command }, null))) {
return true;
}
if (allowedTools.includes(getPermissionKey(tool, { command }, command))) {
return true;
}
return false;
};
const bashToolCommandHasPermission = (tool, command, prefix, allowedTools) => {
if (bashToolCommandHasExactMatchPermission(tool, command, allowedTools)) {
return true;
}
return allowedTools.includes(getPermissionKey(tool, { command }, prefix));
};
const bashToolHasPermission = async (tool, command, context, allowedTools, getCommandSubcommandPrefixFn = getCommandSubcommandPrefix) => {
if (bashToolCommandHasExactMatchPermission(tool, command, allowedTools)) {
return { result: true };
}
const subCommands = splitCommand(command).filter((_) => {
if (_ === `cd ${getCwd()}`) {
return false;
}
return true;
});
const commandSubcommandPrefix = await getCommandSubcommandPrefixFn(
command,
context.abortController.signal
);
if (context.abortController.signal.aborted) {
throw new AbortError();
}
if (commandSubcommandPrefix === null) {
return {
result: false,
message: `${PRODUCT_NAME} requested permissions to use ${tool.name}, but you haven't granted it yet.`
};
}
if (commandSubcommandPrefix.commandInjectionDetected) {
if (bashToolCommandHasExactMatchPermission(tool, command, allowedTools)) {
return { result: true };
} else {
return {
result: false,
message: `${PRODUCT_NAME} requested permissions to use ${tool.name}, but you haven't granted it yet.`
};
}
}
if (subCommands.length < 2) {
if (bashToolCommandHasPermission(
tool,
command,
commandSubcommandPrefix.commandPrefix,
allowedTools
)) {
return { result: true };
} else {
return {
result: false,
message: `${PRODUCT_NAME} requested permissions to use ${tool.name}, but you haven't granted it yet.`
};
}
}
if (subCommands.every((subCommand) => {
const prefixResult = commandSubcommandPrefix.subcommandPrefixes.get(subCommand);
if (prefixResult === void 0 || prefixResult.commandInjectionDetected) {
return false;
}
const hasPermission = bashToolCommandHasPermission(
tool,
subCommand,
prefixResult ? prefixResult.commandPrefix : null,
allowedTools
);
return hasPermission;
})) {
return { result: true };
}
return {
result: false,
message: `${PRODUCT_NAME} requested permissions to use ${tool.name}, but you haven't granted it yet.`
};
};
const hasPermissionsToUseTool = async (tool, input, context, _assistantMessage) => {
if (!context.options.safeMode) {
return { result: true };
}
if (context.abortController.signal.aborted) {
throw new AbortError();
}
try {
if (!tool.needsPermissions(input)) {
return { result: true };
}
} catch (e) {
logError(`Error checking permissions: ${e}`);
return { result: false, message: "Error checking permissions" };
}
const projectConfig = getCurrentProjectConfig();
const allowedTools = projectConfig.allowedTools ?? [];
if (tool === BashTool && allowedTools.includes(BashTool.name)) {
return { result: true };
}
switch (tool) {
// For bash tool, check each sub-command's permissions separately
case BashTool: {
const { command } = inputSchema.parse(input);
return await bashToolHasPermission(tool, command, context, allowedTools);
}
// For file editing tools, check session-only permissions
case FileEditTool:
case FileWriteTool:
case NotebookEditTool: {
if (!tool.needsPermissions(input)) {
return { result: true };
}
return {
result: false,
message: `${PRODUCT_NAME} requested permissions to use ${tool.name}, but you haven't granted it yet.`
};
}
// For other tools, check persistent permissions
default: {
const permissionKey = getPermissionKey(tool, input, null);
if (allowedTools.includes(permissionKey)) {
return { result: true };
}
return {
result: false,
message: `${PRODUCT_NAME} requested permissions to use ${tool.name}, but you haven't granted it yet.`
};
}
}
};
async function savePermission(tool, input, prefix) {
const key = getPermissionKey(tool, input, prefix);
if (tool === FileEditTool || tool === FileWriteTool || tool === NotebookEditTool) {
grantWritePermissionForOriginalDir();
return;
}
const projectConfig = getCurrentProjectConfig();
if (projectConfig.allowedTools.includes(key)) {
return;
}
projectConfig.allowedTools.push(key);
projectConfig.allowedTools.sort();
saveCurrentProjectConfig(projectConfig);
}
function getPermissionKey(tool, input, prefix) {
switch (tool) {
case BashTool:
if (prefix) {
return `${BashTool.name}(${prefix}:*)`;
}
return `${BashTool.name}(${BashTool.renderToolUseMessage(input)})`;
default:
return tool.name;
}
}
export {
bashToolCommandHasExactMatchPermission,
bashToolCommandHasPermission,
bashToolHasPermission,
hasPermissionsToUseTool,
savePermission
};
//# sourceMappingURL=permissions.js.map