src / config.ts
import { createConfigSchematics } from "@lmstudio/sdk";
import os from "os";
import path from "path";
const lines = (items: string[]) => items.join("\n");
const normalizePath = (value: string) => value.replace(/\\/g, "/");
// Portable defaults: every user gets a workspace under their own home folder.
const defaultWorkspace = normalizePath(
path.join(os.homedir(), "Documents", "LMStudioWorkspace"),
);
const defaultGeneratedFolder = `${defaultWorkspace}/generated`;
const defaultNotesFolder = `${defaultWorkspace}/notes`;
export const configSchematics = createConfigSchematics()
.field("workspace", "string", {
displayName: "Workspace Folder",
subtitle: "Root folder available to workspace file, memory, command, and Python tools.",
placeholder: "C:/Users/YourName/Documents/LMStudioWorkspace",
}, defaultWorkspace)
.field("braveSearchApiKey", "string", {
displayName: "Brave Search API Key",
subtitle: "Optional. Leave blank to use the DuckDuckGo HTML fallback.",
placeholder: "BSA...",
}, "")
.field("webEnabled", "boolean", {
displayName: "Enable Web Tools",
subtitle: "Allow web_search, open_url, and open_top_search_results.",
}, true)
.field("localUrls", "boolean", {
displayName: "Allow Local / Private URLs",
subtitle: "Required for localhost services such as ComfyUI. Disable to block private-network URLs.",
}, true)
.field("comfyuiUrl", "string", {
displayName: "ComfyUI URL",
subtitle: "Base URL for ComfyUI.",
placeholder: "http://127.0.0.1:8188",
}, "http://127.0.0.1:8188")
.field("pythonExe", "string", {
displayName: "Python Executable",
subtitle: "Command or full path used by run_python.",
placeholder: "python",
}, "python")
.field("filesRead", "boolean", {
displayName: "Files: Allow Read",
subtitle: "Allow listing, searching, and reading files inside the workspace.",
}, true)
.field("filesWrite", "boolean", {
displayName: "Files: Allow Write",
subtitle: "Allow creating permitted text files inside writable roots.",
}, true)
.field("filesOverwrite", "boolean", {
displayName: "Files: Allow Overwrite",
subtitle: "Allow replacing existing permitted files when the tool explicitly requests overwrite.",
}, false)
.field("filesDelete", "boolean", {
displayName: "Files: Allow Delete",
subtitle: "Reserved permission. Current plugin does not expose a delete-file tool.",
}, false)
.field("filesAllowedRoots", "string", {
displayName: "Files: Allowed Read Roots",
subtitle: "One absolute path per line. Workspace containment still applies.",
isParagraph: true,
}, defaultWorkspace)
.field("filesWriteableRoots", "string", {
displayName: "Files: Writable Roots",
subtitle: "One absolute path per line. Writes outside these roots are rejected.",
isParagraph: true,
}, lines([
defaultWorkspace,
defaultGeneratedFolder,
defaultNotesFolder,
]))
.field("filesAllowedWriteExtensions", "string", {
displayName: "Files: Allowed Write Extensions",
subtitle: "One extension per line, including the leading dot.",
isParagraph: true,
}, lines([".txt", ".md", ".json", ".jsonl", ".csv", ".log", ".yaml", ".yml"]))
.field("filesBlockedWriteExtensions", "string", {
displayName: "Files: Blocked Write Extensions",
subtitle: "One extension per line. These always remain blocked.",
isParagraph: true,
}, lines([".exe", ".bat", ".cmd", ".ps1", ".vbs", ".js", ".mjs", ".cjs", ".msi", ".dll", ".scr", ".reg", ".lnk"]))
.field("filesMaxWriteBytes", "numeric", {
displayName: "Files: Max Write Size (bytes)",
subtitle: "Maximum UTF-8 size for a single write_text_file call.",
min: 1024,
max: 104857600,
}, 1048576)
.field("commandsEnabled", "boolean", {
displayName: "Commands: Enabled",
subtitle: "Master switch for run_command.",
}, true)
.field("commandsMode", "select", {
displayName: "Commands: Permission Mode",
subtitle: "readOnly is recommended. full is not recommended.",
options: [
{ displayName: "Off", value: "off" },
{ displayName: "Read Only", value: "readOnly" },
{ displayName: "Developer", value: "developer" },
{ displayName: "Allowlist Only", value: "allowlist" },
{ displayName: "Full (Dangerous)", value: "full" },
],
}, "readOnly")
.field("commandsTimeoutSeconds", "numeric", {
displayName: "Commands: Timeout (seconds)",
subtitle: "Commands are terminated after this duration.",
min: 1,
max: 60,
}, 20)
.field("commandsMaxOutputChars", "numeric", {
displayName: "Commands: Max Output Characters",
subtitle: "Maximum stdout/stderr returned to the model.",
min: 1000,
max: 80000,
}, 12000)
.field("commandsAllowShellOperators", "boolean", {
displayName: "Commands: Allow Shell Operators",
subtitle: "Allow pipes, redirects, chaining, ampersands, and similar shell syntax. Keep disabled for safety.",
}, false)
.field("commandsAllowNetwork", "boolean", {
displayName: "Commands: Allow Network Commands",
subtitle: "Allow curl, wget, SSH, and similar network-capable commands.",
}, false)
.field("commandsAllowInstall", "boolean", {
displayName: "Commands: Allow Install Commands",
subtitle: "Allow npm/pip/winget/choco installation commands.",
}, false)
.field("commandsAllowedExact", "string", {
displayName: "Commands: Exact Allowlist",
subtitle: "One exact command line per line.",
isParagraph: true,
}, lines(["whoami", "hostname", "ver", "node --version", "npm --version", "python --version", "git --version", "nvidia-smi"]))
.field("commandsAllowedPrefixes", "string", {
displayName: "Commands: Prefix Allowlist",
subtitle: "One allowed command prefix per line.",
isParagraph: true,
}, lines(["dir", "type", "findstr", "where", "git status", "git log", "git diff", "git branch", "git remote -v", "npm list", "npm outdated", "cmd /c dir", "cmd /c echo", "cmd /c type"]))
.field("commandsBlockedPatterns", "string", {
displayName: "Commands: Blocked Patterns",
subtitle: "One case-insensitive blocked substring per line. Applied in addition to built-in blocks.",
isParagraph: true,
}, lines([
"del ", "erase ", "rmdir ", "rd ", "remove-item", " rm ", "format", "diskpart",
"shutdown", "restart-computer", "stop-computer", "reg ", "set-executionpolicy", "netsh",
"powershell -enc", "encodedcommand", "invoke-webrequest", "curl ", "wget ", "start-process",
"schtasks", " sc ", "bcdedit", "takeown", "icacls", "cipher", "robocopy", "xcopy",
"npm install", "npm i ", "pip install", "choco ", "winget ", "msiexec",
]))
.field("pythonEnabled", "boolean", {
displayName: "Python: Enabled",
subtitle: "Master switch for run_python.",
}, true)
.field("pythonWorkspaceOnly", "boolean", {
displayName: "Python: Workspace Only",
subtitle: "Run Python with the workspace as its working directory. Static safety filters still apply.",
}, true)
.field("pythonTimeoutSeconds", "numeric", {
displayName: "Python: Timeout (seconds)",
subtitle: "Python processes are terminated after this duration.",
min: 1,
max: 60,
}, 20)
.field("pythonMaxOutputChars", "numeric", {
displayName: "Python: Max Output Characters",
subtitle: "Maximum stdout/stderr returned to the model.",
min: 1000,
max: 80000,
}, 12000)
.field("pythonBlockedImports", "string", {
displayName: "Python: Blocked Imports",
subtitle: "One module name per line.",
isParagraph: true,
}, lines(["subprocess", "os", "shutil", "socket", "requests", "urllib", "ctypes", "winreg"]))
.field("pythonBlockedPatterns", "string", {
displayName: "Python: Blocked Code Patterns",
subtitle: "One case-insensitive blocked substring per line.",
isParagraph: true,
}, lines(["__import__", "eval(", "exec(", "compile(", "open(", "input(", "globals(", "locals(", "getattr(", "setattr(", "delattr("]))
.build();