mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-17 22:56:54 +00:00
5163dc2e4d
* feat: add fail-closed Local Delegation to Cairn dashboard Port the-workshop Local Delegation seam into signals-dashboard 0.3.0. Orthogonal off/on control beside repo/connected profiles; enable only when local-agent-delegation is installed and a qualified route receipt is present. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 * fix: address Local Delegation review (skill path + README catalog) - Walk marketplace/plugin and _direct install roots for local-agent-delegation - Update plugins/signals-dashboard README and regenerate docs/README.plugins.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 * fix: keep Local Delegation Windows launches from reparsing -i prompt Long LD orientation text split under wt/cmd on Windows (0x80070002). Keep -i short/quote-free; rely on WORKSHOP_LOCAL_DELEGATION=enabled (+ skill) for policy. Add charset/length guard tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 * fix: surface Local Delegation effective state on open toast and badge Keep -i short with one optional ASCII line. Show operators "Local Delegation effective · route <id>" on open and in the summary control. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 * fix: address Local Delegation GHCP review comments - Cross-platform path.join in receipt fixture - Atomic .local-delegation.json write (no symlink follow) - Case-insensitive WORKSHOP_LOCAL_DELEGATION env clear - Windows Terminal: set/clear env via cmd before agent - aria-pressed on Local toggle Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 * fix: store Local Delegation preference user-locally, not in the workshop repo A cloned workshop must not be able to ship preference:on. Key operator intent under ~/.copilot/workshop-local-delegation/ by canonical workshop path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 * fix: preserve path case in Local Delegation preference key Lowercasing collided distinct workshops on case-sensitive filesystems. Normalize separators only; realpath already supplies a stable path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 * fix: allow parentheses in quoted Windows Local Delegation launches Args are quote-wrapped; only block % and ! expanders inside quotes so paths like C:\Work\Project (1) still launch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 * fix: apply quoted-arg Windows safety guard in launch path Complete the parentheses fix: use isSafeQuotedWindowsCmdArg for cmdSafe. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750 --------- Co-authored-by: Jenny Ferries <jenny.ferries@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Michael Recachinas <mrecachinas@github.com> Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
99 lines
3.1 KiB
JavaScript
99 lines
3.1 KiB
JavaScript
const PROFILES = new Set(["repo", "connected"]);
|
|
const SAFE_MCP_NAME = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
|
|
export function isDeskProfile(value) {
|
|
return typeof value === "string" && PROFILES.has(value.toLowerCase());
|
|
}
|
|
|
|
export function normalizeDeskProfile(value, fallback = "repo") {
|
|
return isDeskProfile(value) ? value.toLowerCase() : fallback;
|
|
}
|
|
|
|
export function isWindowsAppExecutionAlias(candidate, localAppData) {
|
|
if (typeof candidate !== "string" || typeof localAppData !== "string") return false;
|
|
const normalized = candidate.replaceAll("/", "\\").toLowerCase();
|
|
const root = `${localAppData.replaceAll("/", "\\").replace(/\\+$/, "")}` +
|
|
"\\microsoft\\windowsapps\\";
|
|
return normalized.startsWith(root.toLowerCase()) && normalized.endsWith(".exe");
|
|
}
|
|
|
|
export function quoteWindowsCmdArgument(value) {
|
|
return `"${String(value).replaceAll('"', '""')}"`;
|
|
}
|
|
|
|
export function isSafeWindowsCmdShim(value) {
|
|
return typeof value === "string" && !/[%\r\n]/.test(value);
|
|
}
|
|
|
|
/**
|
|
* Args that will be wrapped with quoteWindowsCmdArgument. Inside double quotes,
|
|
* cmd still expands %VAR% and !VAR! (delayed expansion). Other metacharacters
|
|
* like & | < > ^ ( ) are literal when quoted, so paths such as
|
|
* C:\Work\Project (1) must be allowed.
|
|
*/
|
|
export function isSafeQuotedWindowsCmdArg(value) {
|
|
return typeof value === "string" && !/[%!\r\n]/.test(value);
|
|
}
|
|
|
|
export function parsePluginMcpNames(text) {
|
|
let parsed;
|
|
try { parsed = JSON.parse(text); }
|
|
catch {
|
|
// Agency may prefix its pass-through command with human-readable startup
|
|
// lines. Its underlying Copilot JSON is the final object in stdout.
|
|
let start = text.lastIndexOf("{");
|
|
while (start >= 0) {
|
|
try {
|
|
parsed = JSON.parse(text.slice(start));
|
|
break;
|
|
} catch {
|
|
start = text.lastIndexOf("{", start - 1);
|
|
}
|
|
}
|
|
if (start < 0) return null;
|
|
}
|
|
|
|
if (!Array.isArray(parsed?.plugins)) return null;
|
|
|
|
const names = [];
|
|
const seen = new Set();
|
|
for (const entry of parsed.plugins) {
|
|
if (entry?.kind !== "mcp" || entry.enabled === false) continue;
|
|
if (entry.scope !== "plugin" && entry.source !== "plugin") continue;
|
|
if (typeof entry.name !== "string" || !SAFE_MCP_NAME.test(entry.name)) continue;
|
|
if (!seen.has(entry.name)) {
|
|
seen.add(entry.name);
|
|
names.push(entry.name);
|
|
}
|
|
}
|
|
return names;
|
|
}
|
|
|
|
export function buildDeskAgentArgv({
|
|
deskName,
|
|
workshopDir,
|
|
useAgency,
|
|
agencyCommand = "agency",
|
|
copilotCommand = "copilot",
|
|
profile = "repo",
|
|
pluginMcpNames = [],
|
|
discoverySucceeded = true,
|
|
}) {
|
|
const argv = useAgency
|
|
? [agencyCommand, "copilot"]
|
|
: [copilotCommand, "--name", deskName];
|
|
|
|
if (profile === "repo") {
|
|
if (useAgency) argv.push("--no-default-mcps");
|
|
}
|
|
|
|
if (profile === "repo" && discoverySucceeded) {
|
|
for (const name of pluginMcpNames) {
|
|
if (SAFE_MCP_NAME.test(name)) argv.push("--disable-mcp-server", name);
|
|
}
|
|
}
|
|
|
|
argv.push("--add-dir", workshopDir);
|
|
return argv;
|
|
}
|