391 lines · 12.2 KB · Zig
RawDownload
const std = @import("std");const builtin = @import("builtin");const wasm = @import("wasm.zig"); pub const TerminalSupport = enum {    unsupported,    supported,     pub fn isSupported(self: TerminalSupport) bool {        return self == .supported;    }}; pub const Capabilities = struct {    os_sandbox: bool,    background_processes: bool,    url_open: bool,    native_url_open: bool,    terminal: TerminalSupport,}; pub const UrlOpenError = std.mem.Allocator.Error; pub const UrlOpener = struct {    context: ?*anyopaque = null,    open_fn: *const fn (        ?*anyopaque,        std.mem.Allocator,        []const u8,    ) UrlOpenError!bool,     /// Borrows `url` for this call. The caller retains ownership. Returns    /// `false` when the host cannot launch the URL so callers can keep a    /// manual fallback available.    pub fn open(        self: UrlOpener,        alloc: std.mem.Allocator,        url: []const u8,    ) UrlOpenError!bool {        return self.open_fn(self.context, alloc, url);    }}; pub const unavailable_url_opener: UrlOpener = .{    .open_fn = unavailableUrlOpen,}; fn unavailableUrlOpen(    _: ?*anyopaque,    _: std.mem.Allocator,    _: []const u8,) UrlOpenError!bool {    return false;} pub const TerminalTitle = struct {    context: ?*anyopaque = null,    set_model_fn: *const fn (?*anyopaque, []const u8) void,    clear_fn: *const fn (?*anyopaque) void,     /// Borrows `model` for this call. Terminal write failures are intentionally    /// non-fatal because the title is presentation metadata.    pub fn setModel(self: TerminalTitle, model: []const u8) void {        self.set_model_fn(self.context, model);    }     pub fn clear(self: TerminalTitle) void {        self.clear_fn(self.context);    }}; pub const unavailable_terminal_title: TerminalTitle = .{    .set_model_fn = ignoreTerminalTitleSetModel,    .clear_fn = ignoreTerminalTitleClear,}; fn ignoreTerminalTitleSetModel(_: ?*anyopaque, _: []const u8) void {} fn ignoreTerminalTitleClear(_: ?*anyopaque) void {} pub const SecretStoreLoadError = std.mem.Allocator.Error || error{    StoredKeyInsecure,    StoredKeyUnreadable,}; pub const SecretStoreWriteError = std.mem.Allocator.Error || error{    StoredKeyWriteFailed,}; pub const SecretStore = struct {    context: ?*anyopaque = null,    backend_label: []const u8,    is_disabled_fn: *const fn (?*anyopaque) bool,    load_fn: *const fn (        ?*anyopaque,        std.mem.Allocator,    ) SecretStoreLoadError!?[]u8,    store_fn: *const fn (        ?*anyopaque,        std.mem.Allocator,        []const u8,    ) SecretStoreWriteError!void,    store_interactive_fn: *const fn (        ?*anyopaque,    ) SecretStoreWriteError!bool,     pub fn isDisabled(self: SecretStore) bool {        return self.is_disabled_fn(self.context);    }     /// Returns an owned secret, or null when none is stored. The caller must    /// zero and free a returned secret with the allocator passed to this call.    pub fn load(        self: SecretStore,        alloc: std.mem.Allocator,    ) SecretStoreLoadError!?[]u8 {        return self.load_fn(self.context, alloc);    }     /// Borrows `value` for this call. The caller retains ownership.    pub fn store(        self: SecretStore,        alloc: std.mem.Allocator,        value: []const u8,    ) SecretStoreWriteError!void {        return self.store_fn(self.context, alloc, value);    }     /// Lets the host collect and store a secret without exposing its bytes to    /// Core. Returns false when the host has no interactive secret prompt.    pub fn storeInteractive(        self: SecretStore,    ) SecretStoreWriteError!bool {        return self.store_interactive_fn(self.context);    }}; pub const unavailable_secret_store: SecretStore = .{    .backend_label = "configured credential store",    .is_disabled_fn = unavailableSecretStoreIsDisabled,    .load_fn = unavailableSecretStoreLoad,    .store_fn = unavailableSecretStoreWrite,    .store_interactive_fn = unavailableSecretStoreInteractiveWrite,}; fn unavailableSecretStoreIsDisabled(_: ?*anyopaque) bool {    return false;} fn unavailableSecretStoreLoad(    _: ?*anyopaque,    _: std.mem.Allocator,) SecretStoreLoadError!?[]u8 {    return null;} fn unavailableSecretStoreWrite(    _: ?*anyopaque,    _: std.mem.Allocator,    _: []const u8,) SecretStoreWriteError!void {    return error.StoredKeyWriteFailed;} fn unavailableSecretStoreInteractiveWrite(    _: ?*anyopaque,) SecretStoreWriteError!bool {    return false;} pub const ClipboardError = error{CopyFailed}; pub const unavailable_clipboard = Clipboard{    .copy_fn = copyUnavailable,}; fn copyUnavailable(_: ?*anyopaque, _: []const u8) ClipboardError!bool {    return false;} fn copy_file_unavailable(_: ?*anyopaque, _: std.mem.Allocator, _: []const u8) ClipboardError!bool {    return false;} pub const Clipboard = struct {    context: ?*anyopaque = null,    copy_fn: *const fn (        ?*anyopaque,        []const u8,    ) ClipboardError!bool,    copy_file_fn: *const fn (        ?*anyopaque,        std.mem.Allocator,        []const u8,    ) ClipboardError!bool = copy_file_unavailable,     /// Borrows `text` for this call. The caller retains ownership. Returns    /// `false` when the host has no clipboard implementation.    pub fn copy(        self: Clipboard,        text: []const u8,    ) ClipboardError!bool {        return self.copy_fn(self.context, text);    }     /// Borrows `path` for this call. The caller retains ownership. Returns    /// `false` when the host cannot publish file references to its clipboard.    pub fn copy_file(        self: Clipboard,        alloc: std.mem.Allocator,        path: []const u8,    ) ClipboardError!bool {        return self.copy_file_fn(self.context, alloc, path);    }}; pub fn current() Capabilities {    return capabilitiesForTarget(builtin.cpu.arch, builtin.os.tag);} fn capabilitiesForTarget(    arch: std.Target.Cpu.Arch,    os_tag: std.Target.Os.Tag,) Capabilities {    if (wasm.isTarget(arch)) {        return .{            .os_sandbox = wasm.os_sandbox,            .background_processes = wasm.background_processes,            .url_open = false,            .native_url_open = false,            .terminal = terminalSupportForOs(os_tag),        };    }    return nativeForOs(os_tag);} pub fn terminalSupportForOs(os_tag: std.Target.Os.Tag) TerminalSupport {    return if (os_tag == .macos or os_tag == .linux)        .supported    else        .unsupported;} pub fn nativeForOs(os_tag: std.Target.Os.Tag) Capabilities {    return .{        .os_sandbox = os_tag == .macos,        .background_processes = os_tag != .windows and os_tag != .wasi,        .url_open = os_tag == .macos or os_tag == .linux,        .native_url_open = os_tag == .macos,        .terminal = terminalSupportForOs(os_tag),    };} /// Returns an owned description of the current operating system. The caller/// owns the returned slice and must free it with `alloc`.pub fn operatingSystemText(alloc: std.mem.Allocator) std.mem.Allocator.Error![]u8 {    if (comptime wasm.isTarget(builtin.cpu.arch)) {        return wasm.operatingSystemText(alloc, builtin.os.tag);    }    if (comptime builtin.os.tag == .windows or builtin.os.tag == .wasi) {        return alloc.dupe(u8, @tagName(builtin.os.tag));    }     const uts = std.posix.uname();    const sysname = std.mem.sliceTo(&uts.sysname, 0);    const release = std.mem.sliceTo(&uts.release, 0);    if (release.len == 0) return alloc.dupe(u8, sysname);    return std.fmt.allocPrint(alloc, "{s} {s}", .{ sysname, release });} test "terminal title forwards borrowed model bytes and clear" {    const Capture = struct {        model: [64]u8 = undefined,        model_len: usize = 0,        clear_count: usize = 0,         fn setModel(raw: ?*anyopaque, model: []const u8) void {            const self: *@This() = @ptrCast(@alignCast(raw.?));            self.model_len = @min(model.len, self.model.len);            @memcpy(self.model[0..self.model_len], model[0..self.model_len]);        }         fn clear(raw: ?*anyopaque) void {            const self: *@This() = @ptrCast(@alignCast(raw.?));            self.clear_count += 1;        }         fn model_text(self: *const @This()) []const u8 {            return self.model[0..self.model_len];        }    };     var capture = Capture{};    const terminal_title = TerminalTitle{        .context = &capture,        .set_model_fn = Capture.setModel,        .clear_fn = Capture.clear,    };     terminal_title.setModel("provider/model");    terminal_title.clear();     try std.testing.expectEqualStrings("provider/model", capture.model_text());    try std.testing.expectEqual(@as(usize, 1), capture.clear_count);} test "unavailable terminal title accepts set and clear" {    unavailable_terminal_title.setModel("provider/model");    unavailable_terminal_title.clear();} test "native host capabilities expose sandbox and background process support" {    const macos = nativeForOs(.macos);    try std.testing.expect(macos.os_sandbox);    try std.testing.expect(macos.background_processes);    try std.testing.expect(macos.url_open);    try std.testing.expect(macos.native_url_open);    try std.testing.expectEqual(TerminalSupport.supported, macos.terminal);     const linux = nativeForOs(.linux);    try std.testing.expect(!linux.os_sandbox);    try std.testing.expect(linux.background_processes);    try std.testing.expect(linux.url_open);    try std.testing.expect(!linux.native_url_open);    try std.testing.expectEqual(TerminalSupport.supported, linux.terminal);     const windows = nativeForOs(.windows);    try std.testing.expect(!windows.background_processes);    try std.testing.expect(!windows.url_open);    try std.testing.expect(!windows.native_url_open);    try std.testing.expectEqual(TerminalSupport.unsupported, windows.terminal);     const wasi = nativeForOs(.wasi);    try std.testing.expect(!wasi.background_processes);    try std.testing.expect(!wasi.url_open);    try std.testing.expect(!wasi.native_url_open);    try std.testing.expectEqual(TerminalSupport.unsupported, wasi.terminal);     try std.testing.expectEqual(        TerminalSupport.unsupported,        terminalSupportForOs(.freebsd),    );} test "host boundary routes WebAssembly targets to WASM capabilities" {    const emscripten = capabilitiesForTarget(.wasm32, .emscripten);    try std.testing.expect(!emscripten.os_sandbox);    try std.testing.expect(!emscripten.background_processes);    try std.testing.expect(!emscripten.url_open);    try std.testing.expect(!emscripten.native_url_open);    try std.testing.expectEqual(TerminalSupport.unsupported, emscripten.terminal);     const wasi = capabilitiesForTarget(.wasm64, .wasi);    try std.testing.expect(!wasi.os_sandbox);    try std.testing.expect(!wasi.background_processes);    try std.testing.expect(!wasi.url_open);    try std.testing.expect(!wasi.native_url_open);    try std.testing.expectEqual(TerminalSupport.unsupported, wasi.terminal);} test "unavailable URL opener keeps the manual fallback available" {    try std.testing.expect(!try unavailable_url_opener.open(        std.testing.allocator,        "https://example.test",    ));} test "unavailable secret store reports absence and refuses writes" {    try std.testing.expect(!unavailable_secret_store.isDisabled());    try std.testing.expect((try unavailable_secret_store.load(std.testing.allocator)) == null);    try std.testing.expectError(        error.StoredKeyWriteFailed,        unavailable_secret_store.store(std.testing.allocator, "secret"),    );    try std.testing.expect(!try unavailable_secret_store.storeInteractive());} test "unavailable clipboard rejects text and file references" {    try std.testing.expect(!try unavailable_clipboard.copy("text"));    try std.testing.expect(!try unavailable_clipboard.copy_file(std.testing.allocator, "/tmp/report.md"));} test "current host describes its operating system" {    const text = try operatingSystemText(std.testing.allocator);    defer std.testing.allocator.free(text);     try std.testing.expect(text.len > 0);    if (comptime builtin.os.tag == .windows or builtin.os.tag == .wasi) {        try std.testing.expectEqualStrings(@tagName(builtin.os.tag), text);    }}