FTP und RocketChat kennen die benannten Arbeitsbereiche (#718)
Gemeinsamer Helfer NamedWorkspaces in Core statt dreier Kopien: unit: und Bereichs-Id als Praefix, Lesen braucht Read, Herunterladen braucht Write, Unbekanntes und Verbotenes antworten gleich. Ein Dateiname aus einer RocketChat-Nachricht waehlt keinen Bereich. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1bf26e9ff3
commit
88c42b244d
@@ -0,0 +1,80 @@
|
||||
using ClawdDotNet.Core.Config;
|
||||
|
||||
namespace ClawdDotNet.Core.Organisation;
|
||||
|
||||
/// <summary>#718: A workspace with a member list, as one call may use it.</summary>
|
||||
public sealed record NamedWorkspace(WorkspaceConfig Workspace, string Root, WorkspaceAccess Access);
|
||||
|
||||
/// <summary>
|
||||
/// #718: The workspaces with member lists (#684) for every tool that moves files — FileRW,
|
||||
/// FTP, RocketChat — resolved in one place instead of once per tool.
|
||||
///
|
||||
/// <para><b>Unknown and not allowed answer the same.</b> A name that is no workspace and a
|
||||
/// workspace the agent is not a member of both give <c>null</c>, so an answer never reveals
|
||||
/// that a workspace exists.</para>
|
||||
///
|
||||
/// <para><c>personal</c> and <c>shared</c> are not resolved here: they belong to each tool
|
||||
/// as they always did (FileRW with its <c>sharedAccessLevel</c>), and running installations
|
||||
/// keep what they have.</para>
|
||||
/// </summary>
|
||||
public static class NamedWorkspaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Splits <c>name:path</c>. A prefix counts only if it is <c>personal</c>, <c>shared</c>,
|
||||
/// <c>unit</c> or could be a workspace id — so a colon elsewhere in a path (an NTFS stream,
|
||||
/// a drive letter) is left for the path check to refuse. Without a prefix: <c>personal</c>.
|
||||
/// </summary>
|
||||
public static (string Workspace, string Relative) Split(string path)
|
||||
{
|
||||
var colon = path.IndexOf(':');
|
||||
if (colon > 0)
|
||||
{
|
||||
var prefix = path[..colon];
|
||||
var rest = path[(colon + 1)..];
|
||||
var reserved = prefix.ToLowerInvariant();
|
||||
if (WorkspaceLayout.Reserved.Contains(reserved))
|
||||
return (reserved, rest);
|
||||
if (WorkspaceLayout.IsValidId(prefix))
|
||||
return (prefix, rest);
|
||||
}
|
||||
return (WorkspaceLayout.Personal, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The workspace behind <c>unit</c> or an id, if the agent may use it; <c>null</c>
|
||||
/// otherwise — also for <c>personal</c> and <c>shared</c>, which the caller handles.
|
||||
/// </summary>
|
||||
public static NamedWorkspace? Resolve(string name, string agentId, OrgChart? organisation, string? sharedWorkspacePath)
|
||||
{
|
||||
if (organisation is null
|
||||
|| name.Equals(WorkspaceLayout.Personal, StringComparison.OrdinalIgnoreCase)
|
||||
|| name.Equals(WorkspaceLayout.Shared, StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
|
||||
var workspace = name.Equals(WorkspaceLayout.Unit, StringComparison.OrdinalIgnoreCase)
|
||||
? organisation.UnitWorkspace(organisation.UnitOf(agentId)?.Id)
|
||||
: organisation.Workspace(name);
|
||||
if (workspace is null)
|
||||
return null;
|
||||
|
||||
var access = organisation.AccessTo(agentId, workspace.Id);
|
||||
if (access == WorkspaceAccess.None || WorkspaceLayout.PathOf(sharedWorkspacePath, workspace.Id) is not { } root)
|
||||
return null;
|
||||
return new NamedWorkspace(workspace, root, access);
|
||||
}
|
||||
|
||||
/// <summary>The names an agent can use — for the answer to a name that is not one of them.</summary>
|
||||
public static IReadOnlyList<string> Available(string agentId, OrgChart? organisation, bool withShared)
|
||||
{
|
||||
var names = new List<string> { WorkspaceLayout.Personal };
|
||||
if (withShared)
|
||||
names.Add(WorkspaceLayout.Shared);
|
||||
if (organisation is not null)
|
||||
names.AddRange(organisation.WorkspacesFor(agentId).Select(g =>
|
||||
g.IsOwnUnit ? $"{WorkspaceLayout.Unit} (= {g.Workspace.Id})" : g.Workspace.Id));
|
||||
return names;
|
||||
}
|
||||
|
||||
public static string Unknown(string requested, string agentId, OrgChart? organisation, bool withShared)
|
||||
=> $"Unknown workspace '{requested}'. You can use: {string.Join(", ", Available(agentId, organisation, withShared))}.";
|
||||
}
|
||||
Reference in New Issue
Block a user