Add a troubleshooting profile with the scoped log, and a deterministic conf-key lookup
This commit is contained in:
@@ -707,6 +707,80 @@ function vv_ai_token_stats(): array {
|
||||
return $out;
|
||||
}
|
||||
|
||||
// ── Scoped lookups for the WebGUI dock ───────────────────────────────────────────────────────
|
||||
|
||||
// The tail of one named script's log, for the troubleshooting profile. vv_ai_recent_logs()
|
||||
// answers "is anything wrong anywhere"; this answers "why did THIS fail", which is a different
|
||||
// question and needs the ordinary lines too, not just WARN and ERROR — the last thing a script
|
||||
// printed before stopping is usually not labelled as a warning.
|
||||
//
|
||||
// Contained to LOG_DIR by realpath. The id arrives from a request, and a log path that escaped
|
||||
// would read arbitrary files into a model's context.
|
||||
function vv_ai_scoped_log(string $id, int $lines = 120): array {
|
||||
$rel = preg_replace('/\.sh$/', '', trim($id)) . '.log';
|
||||
if ($rel === '' || str_contains($rel, "\0")) return ['ok' => false, 'error' => 'bad id'];
|
||||
|
||||
$base = realpath(LOG_DIR);
|
||||
$path = realpath(LOG_DIR . '/' . $rel);
|
||||
if ($base === false || $path === false) return ['ok' => false, 'error' => 'no log yet'];
|
||||
if (!str_starts_with($path, $base . '/')) return ['ok' => false, 'error' => 'outside log dir'];
|
||||
if (!is_file($path)) return ['ok' => false, 'error' => 'no log yet'];
|
||||
|
||||
$all = @file($path, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
return [
|
||||
'ok' => true,
|
||||
'path' => $rel,
|
||||
'total' => count($all),
|
||||
'mtime' => @filemtime($path) ?: null,
|
||||
'tail' => array_slice($all, -max(10, min($lines, 400))),
|
||||
];
|
||||
}
|
||||
|
||||
// Where a conf key actually lives. Deterministic on purpose: the model should narrate this, not
|
||||
// work it out. Searches the host's own conf and master, reports file, line and value.
|
||||
//
|
||||
// This is the "Jonny" case — someone is sure a setting is in master.conf and it is really in the
|
||||
// host conf. Failing to find it is the unhelpful answer; finding it and saying where is the
|
||||
// useful one, and neither should depend on the model guessing which file to trust.
|
||||
function vv_ai_find_conf_key(string $key): array {
|
||||
if (!preg_match('/^[A-Za-z][A-Za-z0-9_]{1,63}$/', $key)) return ['ok' => false];
|
||||
|
||||
$me = vv_detect_host();
|
||||
$order = array_values(array_unique(array_filter([
|
||||
$me !== 'unknown' ? $me . '.conf' : null,
|
||||
'master.conf',
|
||||
])));
|
||||
foreach (vv_get_conf_files() as $f) if (!in_array($f, $order, true)) $order[] = $f;
|
||||
|
||||
// Two passes, active before commented, across every file — not first-match-wins per line.
|
||||
// These confs document each setting in a comment block above it, and those blocks contain
|
||||
// lines like "# RSYNC_ENABLED=false → ALL rsync stops everywhere". A single pass matched
|
||||
// that prose at line 529 and reported the setting as commented out while the live value sat
|
||||
// active at line 546. Reporting an enabled setting as disabled is the exact failure this
|
||||
// lookup exists to prevent, so the active definition always wins.
|
||||
$q = preg_quote($key, '/');
|
||||
$files = [];
|
||||
foreach ($order as $file) $files[$file] = @file(CONF_DIR . '/' . $file, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
|
||||
foreach ($files as $file => $lines) {
|
||||
foreach ($lines as $i => $line) {
|
||||
if (preg_match('/^\s*' . $q . '\s*=/', $line)) {
|
||||
return ['ok' => true, 'file' => $file, 'line' => $i + 1,
|
||||
'text' => trim($line), 'commented' => false];
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($files as $file => $lines) {
|
||||
foreach ($lines as $i => $line) {
|
||||
if (preg_match('/^\s*#\s*' . $q . '\s*=/', $line)) {
|
||||
return ['ok' => true, 'file' => $file, 'line' => $i + 1,
|
||||
'text' => trim($line), 'commented' => true];
|
||||
}
|
||||
}
|
||||
}
|
||||
return ['ok' => false];
|
||||
}
|
||||
|
||||
function vv_ai_job_dir(): string {
|
||||
if (!is_dir(VV_AI_JOB_DIR)) @mkdir(VV_AI_JOB_DIR, 0700, true);
|
||||
return VV_AI_JOB_DIR;
|
||||
|
||||
Reference in New Issue
Block a user