mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-25 02:11:40 +00:00
Add Windows app storage inspector canvas 🤖🤖🤖 (#2620)
* feat: add Windows app storage inspector canvas * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: address storage inspector review findings * Correction to package and plugin * fix: harden storage inspector cleanup * Harden cleanup operation outcomes * Add select a file or folder path in the result tabs to navigate the treemap to its deepest visible parent folder. * Fixes to ensure selftesst pass --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
5755234c4f
commit
df9116a689
@@ -0,0 +1,137 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { stat } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const PROCESS_SCRIPT = `
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$rows = @(Get-CimInstance Win32_Process |
|
||||
Where-Object {
|
||||
$_.ExecutablePath -like '*\\Microsoft Scout\\*' -or
|
||||
$_.CommandLine -like '*\\Microsoft Scout\\*'
|
||||
} |
|
||||
Select-Object ProcessId, Name, ExecutablePath)
|
||||
ConvertTo-Json -Compress -Depth 3 -InputObject $rows
|
||||
`;
|
||||
|
||||
function normalize(filePath) {
|
||||
return path.resolve(filePath).replaceAll("/", "\\").toLowerCase();
|
||||
}
|
||||
|
||||
function isWithin(candidatePath, parentPath) {
|
||||
const relative = path.relative(path.resolve(parentPath), path.resolve(candidatePath));
|
||||
return relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
||||
}
|
||||
|
||||
function getDirectoryAggregate(result, directoryPath) {
|
||||
const normalizedPath = normalize(directoryPath);
|
||||
return result.directories.find((directory) => normalize(directory.path) === normalizedPath);
|
||||
}
|
||||
|
||||
async function describeDirectory(result, id, name, directoryPath, kind) {
|
||||
let stats;
|
||||
try {
|
||||
stats = await stat(directoryPath);
|
||||
} catch (error) {
|
||||
if (error?.code === "ENOENT") {
|
||||
return undefined;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
if (!stats.isDirectory()) {
|
||||
return undefined;
|
||||
}
|
||||
const aggregate = getDirectoryAggregate(result, directoryPath);
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
path: directoryPath,
|
||||
kind,
|
||||
bytes: aggregate?.bytes ?? 0,
|
||||
files: aggregate?.files ?? 0,
|
||||
modifiedAt: stats.mtime.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async function inspectProcesses() {
|
||||
if (process.platform !== "win32") {
|
||||
return { status: "unsupported", processCount: 0, processes: [] };
|
||||
}
|
||||
try {
|
||||
const { stdout } = await execFileAsync("powershell.exe", [
|
||||
"-NoLogo",
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-EncodedCommand",
|
||||
Buffer.from(PROCESS_SCRIPT, "utf16le").toString("base64"),
|
||||
], { windowsHide: true, maxBuffer: 4 * 1024 * 1024 });
|
||||
const parsed = JSON.parse(stdout.trim() || "[]");
|
||||
const processes = Array.isArray(parsed) ? parsed : [parsed];
|
||||
return {
|
||||
status: processes.length ? "running" : "not-running",
|
||||
processCount: processes.length,
|
||||
processes,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
status: "unknown",
|
||||
processCount: 0,
|
||||
processes: [],
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function analyzeMicrosoftScout(result) {
|
||||
const userProfile = process.env.USERPROFILE ?? "";
|
||||
const localAppData = process.env.LOCALAPPDATA ?? path.join(userProfile, "AppData", "Local");
|
||||
const installRoot = path.resolve(localAppData, "Programs", "Clawpilot", "Microsoft Scout");
|
||||
const userDataRoot = path.resolve(userProfile, ".scout");
|
||||
const processInspection = await inspectProcesses();
|
||||
|
||||
const locations = (await Promise.all([
|
||||
describeDirectory(result, "scout-install", "Installed application", installRoot, "application"),
|
||||
describeDirectory(result, "scout-user-data", "User data", userDataRoot, "user-data"),
|
||||
])).filter(Boolean);
|
||||
|
||||
const cleanupLocations = (await Promise.all([
|
||||
describeDirectory(result, "scout-copilot-cache", "Copilot package cache", path.join(userDataRoot, "copilot", "cache"), "cache"),
|
||||
describeDirectory(result, "scout-logs", "Logs", path.join(userDataRoot, "logs"), "logs"),
|
||||
describeDirectory(result, "scout-crashpad", "Crash reports", path.join(userDataRoot, "Crashpad"), "crash-reports"),
|
||||
describeDirectory(result, "scout-temp", "Temporary data", path.join(userDataRoot, "temp"), "temporary"),
|
||||
])).filter(Boolean);
|
||||
|
||||
const canClean = processInspection.status === "not-running";
|
||||
const cleanupItems = cleanupLocations.map((location) => ({
|
||||
...location,
|
||||
entryType: "directory",
|
||||
cleanupEligible: canClean,
|
||||
risk: "low",
|
||||
reason: `${location.name} can be regenerated by Microsoft Scout`,
|
||||
}));
|
||||
const roots = [installRoot, userDataRoot];
|
||||
const topFiles = result.largestFiles
|
||||
.filter((file) => roots.some((root) => isWithin(file.path, root)))
|
||||
.sort((left, right) => right.bytes - left.bytes)
|
||||
.slice(0, 20);
|
||||
|
||||
return {
|
||||
id: "microsoft-scout",
|
||||
status: locations.length ? processInspection.status : "not-found",
|
||||
processCount: processInspection.processCount,
|
||||
processInspectionError: processInspection.error,
|
||||
locations,
|
||||
cleanupItems,
|
||||
topFiles,
|
||||
totalBytes: locations.reduce((total, location) => total + location.bytes, 0),
|
||||
cleanupBytes: cleanupItems.reduce((total, item) => total + item.bytes, 0),
|
||||
message: !locations.length
|
||||
? "Microsoft Scout storage was not found in its standard Windows locations."
|
||||
: processInspection.status === "running"
|
||||
? "Microsoft Scout is running. Close it before cleaning its cache, logs, or temporary data."
|
||||
: processInspection.status === "not-running"
|
||||
? "Microsoft Scout is not running. Regenerable storage locations can be selected for cleanup."
|
||||
: "Microsoft Scout process state could not be verified, so cleanup is disabled.",
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user