J.A.R.V.I.S-rust/resources/commands/file_search/search.lua

79 lines
2.6 KiB
Lua
Raw Permalink Normal View History

feat(commands): add PC control packs — volume, apps, file_search, output_device All four packs are pure Lua (sandbox=full) and call out to PowerShell helpers where COM / native APIs are needed. No new Rust code, no rebuild required: post_build.py --sync copies them under target/{debug,release}/resources/. volume/ - volume_up / volume_down: 5x VK_VOLUME_UP|DOWN via keybd_event - volume_mute: VK_VOLUME_MUTE - volume_max: 50x VK_VOLUME_UP (system clamps) Helper _volume_helper.ps1 P/Invokes user32!keybd_event. apps/ - open_browser : start "" https://www.google.com (uses system default browser) - open_notepad : notepad.exe - open_calculator: calc.exe - open_explorer : explorer.exe - open_terminal : wt.exe, falls back to powershell.exe - open_settings : ms-settings: protocol - open_task_manager: taskmgr.exe - lock_screen : rundll32 user32.dll,LockWorkStation - screenshot : System.Drawing capture to ~/Pictures/jarvis_screenshot_*.png file_search/ - find file <query> : recursive Get-ChildItem in Desktop/Documents/Downloads (depth 3, top 5 matches). Strips Russian/English/Ukrainian trigger prefixes from the recognized phrase, then opens explorer /select on the first hit and notifies with the total count. output_device/ - list_output_devices : enumerates active render endpoints via IMMDeviceEnumerator and notifies friendly names with indices - next_output_device : cycles the system default render device via the undocumented IPolicyConfig::SetDefaultEndpoint (eConsole/Multimedia/ Communications). Tested locally — listed 7 devices, current correctly identified at index 5.
2026-05-15 00:54:52 +03:00
local lang = jarvis.context.language
local phrase = (jarvis.context.phrase or ""):lower()
local triggers = {
"найди документ", "найди файл", "поиск файла", "ищи файл", "где файл",
"find file", "search file", "where is file", "locate file",
"знайди файл", "пошук файлу", "де файл",
}
local query = phrase
for _, t in ipairs(triggers) do
local start, finish = string.find(query, t, 1, true)
if start == 1 then
query = query:sub(finish + 1)
break
end
end
query = query:gsub("^%s+", ""):gsub("%s+$", "")
if query == "" then
jarvis.log("warn", "file_search: empty query (phrase=" .. phrase .. ")")
jarvis.system.notify(
lang == "ru" and "Поиск файлов" or "File search",
lang == "ru" and "Что искать?" or "What to search for?"
)
jarvis.audio.play_error()
return { chain = false }
end
local userprofile = jarvis.system.env("USERPROFILE") or "C:\\Users\\Public"
local roots = {
userprofile .. "\\Desktop",
userprofile .. "\\Documents",
userprofile .. "\\Downloads",
}
local escaped = query:gsub("'", "''")
local roots_arg = "'" .. table.concat(roots, "','") .. "'"
local ps = string.format(
[[$ErrorActionPreference='SilentlyContinue'; $r=@(%s); $q='*%s*'; $hits=Get-ChildItem -Path $r -Filter $q -Recurse -Depth 3 -File | Select-Object -First 5; foreach($h in $hits){ Write-Output $h.FullName }]],
roots_arg, escaped
)
local cmd = string.format('powershell -NoProfile -ExecutionPolicy Bypass -Command "%s"', ps:gsub('"', '\\"'))
jarvis.log("info", "file_search query='" .. query .. "'")
local res = jarvis.system.exec(cmd)
if not res.success then
jarvis.log("error", "file_search exec failed: " .. tostring(res.stderr))
jarvis.audio.play_error()
return { chain = false }
end
local stdout = res.stdout or ""
local lines = {}
for line in stdout:gmatch("[^\r\n]+") do
if line ~= "" then table.insert(lines, line) end
end
if #lines == 0 then
jarvis.system.notify(
lang == "ru" and "Поиск файлов" or "File search",
(lang == "ru" and "Не найдено: " or "Not found: ") .. query
)
jarvis.audio.play_not_found()
else
local first = lines[1]
local count = #lines
local title = lang == "ru" and "Найдено файлов: " or "Files found: "
jarvis.system.notify(title .. count, first)
jarvis.log("info", "file_search opening: " .. first)
jarvis.system.exec(string.format('explorer.exe /select,"%s"', first))
jarvis.audio.play_ok()
end
return { chain = false }