Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116const std = @import("std");const tool_dispatch = @import("../../core/tooling/tool_dispatch.zig");const url_policy = @import("url_policy.zig");
const Allocator = std.mem.Allocator;
const whitespace = " \t\r\n";
pub const Input = struct { url: []u8,
pub fn deinit(self: *Input, alloc: Allocator) void { alloc.free(self.url); self.* = .{ .url = &.{} }; }};
pub fn decode(ctx: tool_dispatch.DispatchContext, args_json: []const u8) tool_dispatch.DispatchError!tool_dispatch.DecodeResult { var parsed = std.json.parseFromSlice(std.json.Value, ctx.allocator, args_json, .{}) catch { return .{ .failure = try ctx.allocator.dupe(u8, "web_fetch arguments must be valid JSON") }; }; defer parsed.deinit();
if (parsed.value != .object) { return .{ .failure = try ctx.allocator.dupe(u8, "web_fetch arguments must be an object") }; }
var it = parsed.value.object.iterator(); while (it.next()) |entry| { if (std.mem.eql(u8, entry.key_ptr.*, "url")) continue; return .{ .failure = try std.fmt.allocPrint(ctx.allocator, "web_fetch field \"{s}\" is not allowed", .{entry.key_ptr.*}) }; }
const url_value = parsed.value.object.get("url") orelse { return .{ .failure = try ctx.allocator.dupe(u8, "web_fetch field \"url\" is required") }; }; if (url_value != .string) { return .{ .failure = try ctx.allocator.dupe(u8, "web_fetch field \"url\" must be a string") }; }
const input = try ctx.allocator.create(Input); errdefer ctx.allocator.destroy(input); const url = try ctx.allocator.dupe(u8, url_value.string); errdefer ctx.allocator.free(url); input.* = .{ .url = url, }; errdefer input.deinit(ctx.allocator);
return .{ .input = .{ .ptr = input, .deinit_fn = inputDeinit } };}
pub fn validate(ctx: tool_dispatch.DispatchContext, erased: tool_dispatch.ToolInput) tool_dispatch.DispatchError!?[]u8 { const input = erased.as(Input); const trimmed = std.mem.trim(u8, input.url, whitespace); if (!std.mem.eql(u8, input.url, trimmed)) { const owned = try ctx.allocator.dupe(u8, trimmed); ctx.allocator.free(input.url); input.url = owned; }
var normalized = url_policy.normalize(ctx.allocator, input.url) catch |err| { return try ctx.allocator.dupe(u8, urlValidationMessage(err)); }; defer normalized.deinit(ctx.allocator); if (!std.mem.eql(u8, input.url, normalized.retrieval_url)) { const owned = try ctx.allocator.dupe(u8, normalized.retrieval_url); ctx.allocator.free(input.url); input.url = owned; }
return null;}
pub fn readsOnly(_: tool_dispatch.ToolInput) bool { return true;}
pub fn isIrreversible(_: tool_dispatch.ToolInput) bool { return false;}
fn inputDeinit(ptr: *anyopaque, alloc: Allocator) void { const input: *Input = @ptrCast(@alignCast(ptr)); input.deinit(alloc); alloc.destroy(input);}
fn urlValidationMessage(err: url_policy.Error) []const u8 { return switch (err) { error.EmptyUrl => "web_fetch field \"url\" must not be empty", error.UrlTooLong => "web_fetch field \"url\" must be at most 2000 bytes", error.UnsupportedScheme => "web_fetch url must start with http:// or https://", error.MissingHost => "web_fetch url must include a host", error.CredentialedUrl => "web_fetch refuses credential-bearing URLs", error.NonPublicAddress, error.SingleLabelHost, => "web_fetch only fetches known public HTTP(S) URLs", error.MalformedPercentEncoding, error.PercentEncodedHost, error.UnicodeHost, error.ControlByte, error.InvalidPort, error.InvalidIpv4, error.ScopeIdRejected, error.MalformedHost, error.MalformedLocation, error.PortChanged, error.ProtocolChanged, error.RequestTargetWhitespace, error.OutOfMemory, error.WriteFailed, => "web_fetch url is malformed", };}