eng: handle repo-assist easy wins
Add root NuGet package source mapping and Dependabot config, allow .NET SDK feature-band roll-forward, and apply low-risk allocation cleanup from repo-assist suggestions. Closes #208 Closes #214 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
c1296be7fd
commit
d83e81d451
13
.github/dependabot.yml
vendored
Normal file
13
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
|
||||
- package-ecosystem: "nuget"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
12
NuGet.Config
Normal file
12
NuGet.Config
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
<packageSourceMapping>
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "10.0.100",
|
||||
"rollForward": "latestPatch"
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenClaw.Shared.Capabilities;
|
||||
@ -122,8 +121,8 @@ public class SystemCapability : NodeCapabilityBase
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
var pathext = Environment.GetEnvironmentVariable("PATHEXT") ?? ".EXE;.CMD;.BAT;.COM";
|
||||
extensions.AddRange(pathext.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(e => e.ToLowerInvariant()));
|
||||
foreach (var e in pathext.Split(';', StringSplitOptions.RemoveEmptyEntries))
|
||||
extensions.Add(e.ToLowerInvariant());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -266,7 +265,9 @@ public class SystemCapability : NodeCapabilityBase
|
||||
var envResult = ExecEnvSanitizer.Sanitize(env);
|
||||
if (envResult.Blocked.Length > 0)
|
||||
{
|
||||
var blockedList = string.Join(", ", envResult.Blocked.OrderBy(n => n, StringComparer.OrdinalIgnoreCase));
|
||||
var blockedNames = (string[])envResult.Blocked.Clone();
|
||||
Array.Sort(blockedNames, StringComparer.OrdinalIgnoreCase);
|
||||
var blockedList = string.Join(", ", blockedNames);
|
||||
Logger.Warn($"system.run DENIED: blocked environment overrides [{blockedList}]");
|
||||
return Error($"Unsafe environment variable override blocked: {blockedList}");
|
||||
}
|
||||
@ -346,18 +347,26 @@ public class SystemCapability : NodeCapabilityBase
|
||||
}
|
||||
|
||||
var data = _approvalPolicy.GetPolicyData();
|
||||
return Success(new
|
||||
var rules = data.Rules;
|
||||
var rulesSummary = new object[rules.Count];
|
||||
for (var i = 0; i < rules.Count; i++)
|
||||
{
|
||||
enabled = true,
|
||||
defaultAction = data.DefaultAction.ToString().ToLowerInvariant(),
|
||||
rules = data.Rules.Select(r => new
|
||||
var r = rules[i];
|
||||
rulesSummary[i] = new
|
||||
{
|
||||
pattern = r.Pattern,
|
||||
action = r.Action.ToString().ToLowerInvariant(),
|
||||
shells = r.Shells,
|
||||
description = r.Description,
|
||||
enabled = r.Enabled
|
||||
}).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
return Success(new
|
||||
{
|
||||
enabled = true,
|
||||
defaultAction = data.DefaultAction.ToString().ToLowerInvariant(),
|
||||
rules = rulesSummary
|
||||
});
|
||||
}
|
||||
|
||||
@ -403,10 +412,13 @@ public class SystemCapability : NodeCapabilityBase
|
||||
|
||||
if (ruleEl.TryGetProperty("shells", out var shellsEl) && shellsEl.ValueKind == System.Text.Json.JsonValueKind.Array)
|
||||
{
|
||||
rule.Shells = shellsEl.EnumerateArray()
|
||||
.Where(s => s.ValueKind == System.Text.Json.JsonValueKind.String)
|
||||
.Select(s => s.GetString() ?? "")
|
||||
.ToArray();
|
||||
var shellsList = new List<string>(shellsEl.GetArrayLength());
|
||||
foreach (var s in shellsEl.EnumerateArray())
|
||||
{
|
||||
if (s.ValueKind == System.Text.Json.JsonValueKind.String)
|
||||
shellsList.Add(s.GetString() ?? "");
|
||||
}
|
||||
rule.Shells = shellsList.ToArray();
|
||||
}
|
||||
|
||||
rules.Add(rule);
|
||||
|
||||
@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
@ -98,8 +97,7 @@ public class ExecApprovalPolicy
|
||||
};
|
||||
}
|
||||
|
||||
// Compute once; only used if any rule has shell filters.
|
||||
var normalizedShell = (shell ?? "powershell").ToLowerInvariant();
|
||||
var shellSpan = (shell ?? "powershell").AsSpan();
|
||||
|
||||
foreach (var rule in _rules)
|
||||
{
|
||||
@ -111,7 +109,7 @@ public class ExecApprovalPolicy
|
||||
var shellMatched = false;
|
||||
foreach (var s in rule.Shells)
|
||||
{
|
||||
if (s.Equals(normalizedShell, StringComparison.OrdinalIgnoreCase))
|
||||
if (s.AsSpan().Equals(shellSpan, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
shellMatched = true;
|
||||
break;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace OpenClaw.Shared;
|
||||
|
||||
@ -6,7 +7,8 @@ public static class GatewayUrlHelper
|
||||
{
|
||||
public const string ValidationMessage = "Gateway URL must be a valid URL (ws://, wss://, http://, or https://).";
|
||||
|
||||
private static readonly char[] s_authorityTerminators = { '/', '?', '#' };
|
||||
private static readonly SearchValues<char> s_authorityTerminators =
|
||||
SearchValues.Create("/?#");
|
||||
|
||||
public static bool IsValidGatewayUrl(string? gatewayUrl) =>
|
||||
TryNormalizeWebSocketUrl(gatewayUrl, out _);
|
||||
@ -143,11 +145,8 @@ public static class GatewayUrlHelper
|
||||
}
|
||||
|
||||
var authorityStart = schemeSeparator + 3;
|
||||
var authorityEnd = url.IndexOfAny(s_authorityTerminators, authorityStart);
|
||||
if (authorityEnd < 0)
|
||||
{
|
||||
authorityEnd = url.Length;
|
||||
}
|
||||
var relativeEnd = url.AsSpan(authorityStart).IndexOfAny(s_authorityTerminators);
|
||||
var authorityEnd = relativeEnd < 0 ? url.Length : authorityStart + relativeEnd;
|
||||
|
||||
var atIndex = url.IndexOf('@', authorityStart);
|
||||
if (atIndex < 0 || atIndex >= authorityEnd)
|
||||
|
||||
@ -164,9 +164,9 @@ public class LocalCommandRunner : ICommandRunner
|
||||
|
||||
private static (string fileName, string arguments) BuildProcessArgs(CommandRequest request)
|
||||
{
|
||||
var shell = (request.Shell ?? "powershell").ToLowerInvariant();
|
||||
var shell = request.Shell ?? "powershell";
|
||||
var command = request.Command;
|
||||
var isCmd = shell == "cmd";
|
||||
var isCmd = shell.Equals("cmd", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (request.Args is { Length: > 0 })
|
||||
{
|
||||
@ -176,12 +176,11 @@ public class LocalCommandRunner : ICommandRunner
|
||||
command = command + " " + string.Join(" ", quoted);
|
||||
}
|
||||
|
||||
return shell switch
|
||||
{
|
||||
"cmd" => ("cmd.exe", $"/C {command}"),
|
||||
"pwsh" => ("pwsh.exe", $"-NoProfile -NonInteractive -Command {command}"),
|
||||
_ => ("powershell.exe", $"-NoProfile -NonInteractive -Command {command}")
|
||||
};
|
||||
if (isCmd)
|
||||
return ("cmd.exe", $"/C {command}");
|
||||
if (shell.Equals("pwsh", StringComparison.OrdinalIgnoreCase))
|
||||
return ("pwsh.exe", $"-NoProfile -NonInteractive -Command {command}");
|
||||
return ("powershell.exe", $"-NoProfile -NonInteractive -Command {command}");
|
||||
}
|
||||
|
||||
private void KillProcess(Process process)
|
||||
|
||||
@ -41,18 +41,18 @@ public class NotificationCategorizer
|
||||
["error"] = ("⚠️ Error", "error"),
|
||||
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static readonly FrozenDictionary<string, string> CategoryTitles =
|
||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
private static readonly FrozenDictionary<string, (string title, string type)> CategoryTypeMap =
|
||||
new Dictionary<string, (string title, string type)>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["health"] = "🩸 Blood Sugar Alert",
|
||||
["urgent"] = "🚨 Urgent Alert",
|
||||
["reminder"] = "⏰ Reminder",
|
||||
["stock"] = "📦 Stock Alert",
|
||||
["email"] = "📧 Email",
|
||||
["calendar"] = "📅 Calendar",
|
||||
["error"] = "⚠️ Error",
|
||||
["build"] = "🔨 Build",
|
||||
["info"] = "🤖 OpenClaw",
|
||||
["health"] = ("🩸 Blood Sugar Alert", "health"),
|
||||
["urgent"] = ("🚨 Urgent Alert", "urgent"),
|
||||
["reminder"] = ("⏰ Reminder", "reminder"),
|
||||
["stock"] = ("📦 Stock Alert", "stock"),
|
||||
["email"] = ("📧 Email", "email"),
|
||||
["calendar"] = ("📅 Calendar", "calendar"),
|
||||
["error"] = ("⚠️ Error", "error"),
|
||||
["build"] = ("🔨 Build", "build"),
|
||||
["info"] = ("🤖 OpenClaw", "info"),
|
||||
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
@ -83,9 +83,10 @@ public class NotificationCategorizer
|
||||
if (!rule.Enabled) continue;
|
||||
if (MatchesRule(searchText, rule))
|
||||
{
|
||||
if (CategoryTypeMap.TryGetValue(rule.Category, out var categoryResult))
|
||||
return categoryResult;
|
||||
var cat = rule.Category.ToLowerInvariant();
|
||||
var title = CategoryTitles.GetValueOrDefault(cat, "🤖 OpenClaw");
|
||||
return (title, cat);
|
||||
return ("🤖 OpenClaw", cat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user