-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommand_runner.hpp
More file actions
124 lines (112 loc) · 6.17 KB
/
Copy pathcommand_runner.hpp
File metadata and controls
124 lines (112 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#pragma once
#include "droidcli_core.h"
#include <cstdint>
namespace droidcli::cli {
// Part of droidcli-infra (ARCHITECTURE.md's Modules diagram), alongside
// process_manager.hpp - both execute OS-specific commands directly
// (CreateProcess, the App Paths/System32 resolution below), not
// droidcli-tools.
//
// One-shot, synchronous, blocking command execution with captured
// stdout/stderr - distinct from ProcessManager, which PID-tracks long-running
// launched_process connectors. Used by DroidHost::run_command() (POST
// /api/run) and by the "run" task-queue command.
struct CommandRunResult {
bool launched = false;
int32_t exit_code = 0;
core::String stdout_text;
core::String stderr_text;
core::String error_message;
};
// Runs `command` in `work_dir` (current directory if empty), waiting up to
// `timeout_ms` for it to finish. On timeout, the process is forcibly
// terminated and error_message is set, but any output captured before the
// timeout is still returned.
//
// `via_shell` (default true, matching the original behavior) routes through
// the platform shell (`cmd.exe /c` on Windows, `sh -c` on POSIX) - needed for
// shell features (pipes, redirects, env var expansion) but on Windows
// `cmd.exe`'s own command-line grammar re-tokenizes the whole string before
// the target program ever sees it, which can silently corrupt an argument
// that itself contains embedded double quotes (observed: an ffmpeg filter
// expression like `s="sin(2*PI*44)"` came out mangled, producing a bogus
// "filename ... syntax is incorrect" error with no ffmpeg output at all -
// cmd.exe never even got to launching ffmpeg correctly). Pass `via_shell =
// false` for a command that's just "<quoted program path> <args>" and needs
// no shell features - CreateProcess's own command-line parsing (the same
// convention every C program's argv uses) handles nested quotes correctly
// where cmd.exe's `/c` grammar does not. On POSIX this flag currently has no
// effect (both paths go through `sh -c`) - the corruption above is a
// cmd.exe-specific quirk, not a POSIX shell one.
CommandRunResult run_command_once(
const core::String& command,
const core::String& work_dir,
int32_t timeout_ms = 30000,
bool via_shell = true);
// The single, authoritative definition of "did this command actually
// succeed" - launched, exited zero, and no error_message (timeout/spawn
// failure both set one). Derived on demand from CommandRunResult's existing
// fields rather than stored as a field of its own, so there's no way for it
// to drift out of sync with them the way two independent inline
// `launched && exit_code == 0` checks (as DroidHost::install_ollama() and
// pull_ollama_model() used to each have their own copy of) could.
inline bool command_succeeded(const CommandRunResult& result)
{
return result.launched && result.exit_code == 0 && result.error_message.empty();
}
// True if `value` contains a path separator - i.e. the caller gave an actual
// path (relative or absolute), not a bare name to be searched for. Exported
// (was previously local to launch_application's own resolution logic) so
// callers building a human-facing display - e.g. DroidHost's approval-prompt
// path rewriting, see "Full paths in the approval prompt" in
// ARCHITECTURE.md - can tell the two cases apart the same way
// launch_application itself does, rather than a second, possibly-diverging
// heuristic.
bool looks_like_path(const core::String& value);
// Windows' "App Paths" registry mechanism (HKCU/HKLM
// SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\<name>.exe) - the same
// one Explorer/Win+R use to resolve a bare name like "chrome" to its actual
// install location even when the app was never added to PATH. Returns an
// empty string if no match is found in either hive. Exported (was previously
// local to launch_application's own resolution) so DroidHost::open_application
// can call it as a standalone, ordered step in the Windows execution ruleset
// - see "Windows execution ruleset" in ARCHITECTURE.md. A no-op returning
// empty on a non-Windows build.
core::String resolve_app_paths_registry(const core::String& name);
// Resolves `name` (e.g. "taskmgr.exe") against the real Windows System
// directory (`GetSystemDirectoryA`) and, failing that, the Windows root
// directory (`GetWindowsDirectoryA` - where `explorer.exe` itself lives,
// not System32), confirming the candidate file actually exists before
// returning it. Deliberately independent of the PATH environment variable -
// System32/the Windows root are always where these binaries live regardless
// of how PATH is configured, so this is more reliable for droidcli's own
// curated Windows-locations data (`scan_windows_locations`, `cli/windows_locations.cpp`)
// than a PATH search would be. Returns an empty string if not found in
// either location. A no-op returning empty on a non-Windows build.
core::String resolve_system_executable(const core::String& name);
struct LaunchAppResult {
bool launched = false;
int64_t pid = 0;
core::String error_message;
// The real, final path the OS actually launched - queried back from the
// live process handle (QueryFullProcessImageName on Windows) after a
// successful launch, not just echoed from whatever the caller or the App
// Paths registry resolution guessed. Empty on failure, or on a platform/
// path where querying it back isn't implemented. See "Windows app
// execution transparency" in ARCHITECTURE.md - the concrete motivation
// was a real incident where open_application("Memory") reported success
// with no way to tell what had actually launched.
core::String resolved_path;
};
// Starts `path_or_name` (resolved against PATH if it's a bare name, same as
// which_executable) with optional `args`, detached and fire-and-forget - no
// waiting for exit, no stdout/stderr capture. Distinct from run_command_once,
// which blocks until the command finishes: GUI applications don't exit on
// their own, so waiting for them would hang. Not PID-tracked by
// ProcessManager - this is a one-off "open this app" launch, not a
// registered launched_process connector.
LaunchAppResult launch_application(
const core::String& path_or_name,
const core::String& args,
const core::String& work_dir);
} // namespace droidcli::cli