@@ -5,7 +5,9 @@ use json;
55fn map_int(value: map, key: string, fallback: int) -> int {
66 let mut result: int = fallback;
77 if value.has(key) {
8- result = value[key];
8+ if type(value[key]) == "int" {
9+ result = value[key];
10+ }
911 }
1012 result
1113}
@@ -534,6 +536,24 @@ fn push_match(state: map, line: string) -> map {
534536}
535537
536538fn observe_limits(state: map) -> map {
539+ let tick: map = cap::clock_monotonic_ms(types::map_string(state, "token", ""));
540+ if map_bool(tick, "ok", false) == false {
541+ state.fatal = true;
542+ state.fatal_code = types::map_string(tick, "code", "cancelled");
543+ state.fatal_message = types::map_string(tick, "message", "clock failed");
544+ state.stop = true;
545+ } else {
546+ let now: int = map_int(tick, "ms", 0);
547+ let start: int = map_int(state, "start_ms", 0);
548+ let mut elapsed: int = now - start;
549+ if elapsed < 0 {
550+ elapsed = 0;
551+ }
552+ if elapsed >= map_int(state, "max_search_wall_time_ms", 2000) {
553+ state.truncated = true;
554+ state.stop = true;
555+ }
556+ }
537557 if map_int(state, "files_visited", 0) >= map_int(state, "max_search_files", 10000) {
538558 state.truncated = true;
539559 state.stop = true;
@@ -675,7 +695,7 @@ fn shrink_result(result: map, max_output_bytes: int, token: string) -> map {
675695 let data: map = types::map_map(current, "data");
676696 if content.length > 0 {
677697 let payload: bytes = bytes::from_utf8(content);
678- let put: map = cap::artifact_put (token, payload, { purpose: "result" });
698+ let put: map = cap::artifact_put_result (token, payload, {});
679699 if map_bool(put, "ok", false) {
680700 let id: string = types::map_string(put, "id", "");
681701 let len: int = map_int(put, "len", content.length);
@@ -714,55 +734,42 @@ pub fn descriptor() -> map {
714734pub fn validate(arguments: map) -> map {
715735 let mut result: map = { ok: true, code: "", message: "" };
716736 if arguments.has("pattern") == false {
717- result = { ok: false, code: "invalid_arguments", message: "search_files requires a pattern" };
737+ result = { ok: false, code: "invalid_arguments", message: "search_files requires pattern" };
718738 } else {
719739 if type(arguments.pattern) != "string" {
720- result = { ok: false, code: "invalid_arguments", message: "pattern must be a string " };
740+ result = { ok: false, code: "invalid_arguments", message: "search_files requires pattern " };
721741 } else {
722742 let pattern: string = arguments.pattern;
723743 if pattern.length == 0 {
724744 result = { ok: false, code: "invalid_arguments", message: "search_files requires a pattern" };
745+ }
746+ }
747+ }
748+ if map_bool(result, "ok", false) {
749+ if arguments.has("path") {
750+ if type(arguments.path) == "string" {
751+ result = validate_tool_path(arguments.path, true);
752+ }
753+ }
754+ }
755+ if map_bool(result, "ok", false) {
756+ if arguments.has("offset") {
757+ if type(arguments.offset) != "int" {
758+ result = { ok: false, code: "invalid_arguments", message: "numeric argument is invalid" };
725759 } else {
726- if arguments.has("path") {
727- if type(arguments.path) != "string" {
728- result = { ok: false, code: "invalid_arguments", message: "path must be a string" };
729- }
730- }
731- if map_bool(result, "ok", false) {
732- if arguments.has("target") {
733- if type(arguments.target) != "string" {
734- result = { ok: false, code: "invalid_arguments", message: "target must be a string" };
735- }
736- }
760+ if arguments.offset < 0 {
761+ result = { ok: false, code: "invalid_arguments", message: "numeric argument is invalid" };
737762 }
738- if map_bool(result, "ok", false) {
739- if arguments.has("file_glob") {
740- if type(arguments.file_glob) != "string" {
741- result = { ok: false, code: "invalid_arguments", message: "file_glob must be a string" };
742- }
743- }
744- }
745- if map_bool(result, "ok", false) {
746- if arguments.has("offset") {
747- if type(arguments.offset) != "int" {
748- result = { ok: false, code: "invalid_arguments", message: "offset must be an integer" };
749- } else {
750- if arguments.offset < 0 {
751- result = { ok: false, code: "invalid_arguments", message: "offset must be an integer" };
752- }
753- }
754- }
755- }
756- if map_bool(result, "ok", false) {
757- if arguments.has("limit") {
758- if type(arguments.limit) != "int" {
759- result = { ok: false, code: "invalid_arguments", message: "limit must be an integer" };
760- } else {
761- if arguments.limit < 0 {
762- result = { ok: false, code: "invalid_arguments", message: "limit must be an integer" };
763- }
764- }
765- }
763+ }
764+ }
765+ }
766+ if map_bool(result, "ok", false) {
767+ if arguments.has("limit") {
768+ if type(arguments.limit) != "int" {
769+ result = { ok: false, code: "invalid_arguments", message: "numeric argument is invalid" };
770+ } else {
771+ if arguments.limit < 0 {
772+ result = { ok: false, code: "invalid_arguments", message: "numeric argument is invalid" };
766773 }
767774 }
768775 }
@@ -818,6 +825,7 @@ pub fn execute(context: map, arguments: map) -> map {
818825 fatal: false,
819826 fatal_code: "",
820827 fatal_message: "",
828+ start_ms: 0,
821829 max_search_files: map_int(config, "max_search_files", 10000),
822830 max_search_scanned_bytes: map_int(config, "max_search_scanned_bytes", 16777216),
823831 max_search_depth: map_int(config, "max_search_depth", 32),
@@ -826,27 +834,33 @@ pub fn execute(context: map, arguments: map) -> map {
826834 max_search_wall_time_ms: map_int(config, "max_search_wall_time_ms", 2000),
827835 max_read_bytes: map_int(config, "max_read_bytes", 1048576)
828836 };
829- state = walk_search(state, path, 0 );
830- if map_bool(state , "fatal ", false) {
831- result = fail(types::map_string(state, "fatal_code", "internal_error"), types::map_string(state, "fatal_message", ""), {} );
837+ let start_clock: map = cap::clock_monotonic_ms(token );
838+ if map_bool(start_clock , "ok ", false) == false {
839+ result = fail_host(start_clock );
832840 } else {
833- let collected: array = types::map_array(state, "lines");
834- let sorted: array = sort_strings(collected);
835- let selected: array = slice_lines(sorted, offset, limit);
836- result = shrink_result(
837- succeed(
838- join_lines(selected),
839- {
840- match_count: selected.length,
841- files_visited: map_int(state, "files_visited", 0),
842- dirs_visited: map_int(state, "dirs_visited", 0)
843- },
844- map_bool(state, "truncated", false),
845- []
846- ),
847- map_int(config, "max_tool_output_bytes", 65536),
848- token
849- );
841+ state.start_ms = map_int(start_clock, "ms", 0);
842+ state = walk_search(state, path, 0);
843+ if map_bool(state, "fatal", false) {
844+ result = fail(types::map_string(state, "fatal_code", "internal_error"), types::map_string(state, "fatal_message", ""), {});
845+ } else {
846+ let collected: array = types::map_array(state, "lines");
847+ let sorted: array = sort_strings(collected);
848+ let selected: array = slice_lines(sorted, offset, limit);
849+ result = shrink_result(
850+ succeed(
851+ join_lines(selected),
852+ {
853+ match_count: selected.length,
854+ files_visited: map_int(state, "files_visited", 0),
855+ dirs_visited: map_int(state, "dirs_visited", 0)
856+ },
857+ map_bool(state, "truncated", false),
858+ []
859+ ),
860+ map_int(config, "max_tool_output_bytes", 65536),
861+ token
862+ );
863+ }
850864 }
851865 }
852866 }
0 commit comments