mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-12 01:56:19 -04:00
refactor: unify URL handling and clean up agent logic
This commit is contained in:
@@ -911,11 +911,8 @@ const tool_output_max_bytes: usize = 1 * 1024 * 1024;
|
||||
fn capToolOutput(allocator: std.mem.Allocator, output: []const u8) []const u8 {
|
||||
if (output.len <= tool_output_max_bytes) return output;
|
||||
const prefix = output[0..tool_output_max_bytes];
|
||||
return std.fmt.allocPrint(
|
||||
allocator,
|
||||
"{s}\n...[truncated, original {d} bytes]",
|
||||
.{ prefix, output.len },
|
||||
) catch prefix;
|
||||
const suffix = std.fmt.allocPrint(allocator, "\n...[truncated, original {d} bytes]", .{output.len}) catch return prefix;
|
||||
return std.mem.concat(allocator, u8, &.{ prefix, suffix }) catch prefix;
|
||||
}
|
||||
|
||||
fn handleToolCall(ctx: *anyopaque, allocator: std.mem.Allocator, tool_name: []const u8, arguments: []const u8) zenai.provider.Client.ToolHandler.Result {
|
||||
|
||||
@@ -530,15 +530,7 @@ pub fn fromToolCallValue(tool_name: []const u8, arguments: std.json.Value) ?Comm
|
||||
} },
|
||||
.scroll => blk: {
|
||||
if (obj.get("backendNodeId") != null) break :blk null;
|
||||
const x: i32 = switch (obj.get("x") orelse std.json.Value{ .integer = 0 }) {
|
||||
.integer => |i| @intCast(i),
|
||||
else => 0,
|
||||
};
|
||||
const y: i32 = switch (obj.get("y") orelse std.json.Value{ .integer = 0 }) {
|
||||
.integer => |i| @intCast(i),
|
||||
else => 0,
|
||||
};
|
||||
break :blk .{ .scroll = .{ .x = x, .y = y } };
|
||||
break :blk .{ .scroll = .{ .x = getJsonI32(obj, "x", 0), .y = getJsonI32(obj, "y", 0) } };
|
||||
},
|
||||
else => null,
|
||||
};
|
||||
@@ -551,6 +543,13 @@ fn getJsonString(o: std.json.ObjectMap, key: []const u8) ?[]const u8 {
|
||||
};
|
||||
}
|
||||
|
||||
fn getJsonI32(o: std.json.ObjectMap, key: []const u8, default: i32) i32 {
|
||||
return switch (o.get(key) orelse return default) {
|
||||
.integer => |i| std.math.cast(i32, i) orelse default,
|
||||
else => default,
|
||||
};
|
||||
}
|
||||
|
||||
// --- Tests ---
|
||||
|
||||
test "parse GOTO" {
|
||||
|
||||
@@ -516,8 +516,8 @@ fn formatReplResult(arena: std.mem.Allocator, name: []const u8, result: []const
|
||||
return aw.written();
|
||||
}
|
||||
|
||||
pub fn printError(_: *Self, msg: []const u8) void {
|
||||
std.debug.print("{s}{s}Error: {s}{s}\n", .{ ansi.bold, ansi.red, msg, ansi.reset });
|
||||
pub fn printError(self: *Self, msg: []const u8) void {
|
||||
self.printErrorFmt("{s}", .{msg});
|
||||
}
|
||||
|
||||
pub fn printErrorFmt(_: *Self, comptime fmt: []const u8, args: anytype) void {
|
||||
|
||||
@@ -73,8 +73,7 @@ pub fn schemaAllocator(self: *Self) std.mem.Allocator {
|
||||
}
|
||||
|
||||
pub fn getCurrentUrl(self: *Self) []const u8 {
|
||||
const page = self.session.currentFrame() orelse return "(no page loaded)";
|
||||
return page.url;
|
||||
return browser_tools.currentUrlOrPlaceholder(self.session);
|
||||
}
|
||||
|
||||
/// Run a JavaScript expression and return the full result (text + error flag).
|
||||
|
||||
@@ -948,6 +948,14 @@ fn execGetUrl(session: *lp.Session) ToolError![]const u8 {
|
||||
return page.url;
|
||||
}
|
||||
|
||||
/// URL of the active frame, or a stable placeholder when no page is loaded.
|
||||
/// Use from contexts that just want a string for display/logging; callers
|
||||
/// that need to react to "no page" should check `currentFrame()` directly.
|
||||
pub fn currentUrlOrPlaceholder(session: *lp.Session) []const u8 {
|
||||
const frame = session.currentFrame() orelse return "(no page loaded)";
|
||||
return frame.url;
|
||||
}
|
||||
|
||||
fn execGetCookies(arena: std.mem.Allocator, session: *lp.Session) ToolError![]const u8 {
|
||||
const cookies = session.cookie_jar.cookies.items;
|
||||
if (cookies.len == 0) return "No cookies.";
|
||||
@@ -1030,6 +1038,7 @@ pub fn substituteEnvVars(arena: std.mem.Allocator, input: []const u8) []const u8
|
||||
const first_lp = std.mem.indexOf(u8, input, "$LP_") orelse return input;
|
||||
|
||||
var result: std.ArrayList(u8) = .empty;
|
||||
result.ensureTotalCapacity(arena, input.len) catch return input;
|
||||
var i: usize = first_lp;
|
||||
var last_copy: usize = 0;
|
||||
while (std.mem.indexOfScalarPos(u8, input, i, '$')) |dollar| {
|
||||
|
||||
@@ -288,7 +288,7 @@ fn handleScriptStep(server: *Server, arena: std.mem.Allocator, id: std.json.Valu
|
||||
}
|
||||
|
||||
const result = browser_tools.call(arena, server.session, &server.node_registry, tcv.name, tcv.args) catch |err| {
|
||||
const url = currentUrl(server) catch "";
|
||||
const url = browser_tools.currentUrlOrPlaceholder(server.session);
|
||||
const msg = std.fmt.allocPrint(arena, "{s} failed at line `{s}` (url: {s}): {s}", .{ tcv.name, args.line, url, @errorName(err) }) catch @errorName(err);
|
||||
return sendErrorContent(server, id, msg);
|
||||
};
|
||||
@@ -298,7 +298,7 @@ fn handleScriptStep(server: *Server, arena: std.mem.Allocator, id: std.json.Valu
|
||||
// roundtrip the same way an exec failure does.
|
||||
const verification = server.verifier.verify(arena, cmd);
|
||||
if (verification.result == .failed) {
|
||||
const url = currentUrl(server) catch "";
|
||||
const url = browser_tools.currentUrlOrPlaceholder(server.session);
|
||||
const reason = verification.reason orelse "verification failed";
|
||||
const msg = std.fmt.allocPrint(arena, "{s} executed at line `{s}` but verification failed (url: {s}): {s}", .{ tcv.name, args.line, url, reason }) catch reason;
|
||||
return sendErrorContent(server, id, msg);
|
||||
@@ -381,11 +381,6 @@ fn findLineSpan(content: []const u8, line: []const u8) error{ NotFound, Ambiguou
|
||||
return found orelse error.NotFound;
|
||||
}
|
||||
|
||||
fn currentUrl(server: *Server) ![]const u8 {
|
||||
const frame = server.session.currentFrame() orelse return "(no page loaded)";
|
||||
return frame.url;
|
||||
}
|
||||
|
||||
fn sendErrorContent(server: *Server, id: std.json.Value, msg: []const u8) !void {
|
||||
const content = [_]protocol.TextContent([]const u8){.{ .text = msg }};
|
||||
try server.transport.sendResult(id, protocol.CallToolResult([]const u8){ .content = &content, .isError = true });
|
||||
|
||||
Reference in New Issue
Block a user