diff --git a/README.md b/README.md index 8bf3edc5..f8fa9cdb 100644 --- a/README.md +++ b/README.md @@ -1481,7 +1481,7 @@ Automated authentication for web services. The `run` command orchestrates the fu - `--show-content` - Print the extracted content text for each result (implies `--content`) - `--content-source ` - Retrieval source: `auto`, `provider`, or `browser` - `--content-format ` - Extracted content format: `markdown` or `text` - - `--content-max-chars ` - Per-result Unicode character limit after extraction + - `--content-max-chars ` - Per-result Unicode character limit after extraction (100–100000; default 10000) - `--content-max-age-hours ` - Maximum acceptable age of cached page content; `0` forces a live fetch - `--content-timeout-ms ` - Per-result retrieval deadline - `--content-browser-id ` - Retrieve through an existing browser session (requires `--content-source browser`) diff --git a/cmd/search.go b/cmd/search.go index 6184282b..1e6b15d2 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -62,13 +62,17 @@ func newSearchCommand() *cobra.Command { contents.Flags().Int("timeout-ms", 0, "Overall deadline across all selected results in milliseconds (1000–120000; default 60000)") contents.Flags().String("content-source", "", "Content source: auto, provider, or browser (default auto)") contents.Flags().String("content-format", "", "Content format: markdown or text") - contents.Flags().Int("content-max-chars", 0, "Per-result Unicode character limit after extraction") + contents.Flags().Int("content-max-chars", 0, "Per-result Unicode character limit after extraction (100–100000; default 10000)") contents.Flags().Int("content-max-age-hours", 0, "For source auto, maximum age of retained provider content; 0 fetches every result through a browser") contents.Flags().Int("content-timeout-ms", 0, "Per-result deadline in milliseconds, including capacity acquisition, retrieval, and extraction") contents.Flags().String("content-browser-id", "", "Existing browser session to retrieve through (requires source auto or browser)") contents.Flags().String("content-browser-mode", "", "Browser retrieval mode: curl or render") contents.MarkFlagsMutuallyExclusive("result-ids", "limit") contents.MarkFlagsOneRequired("result-ids", "limit") + addJSONOutputFlag(cmd) + addJSONOutputFlag(get) + addJSONOutputFlag(providers) + addJSONOutputFlag(contents) cmd.AddCommand(get, providers, contents) return cmd } @@ -211,6 +215,10 @@ func validateSearchQuery(query string) error { } func executeSearchRequest(cmd *cobra.Command, method, path string, body json.RawMessage) error { + output, _ := cmd.Flags().GetString("output") + if err := validateJSONOutput(output); err != nil { + return err + } client := getKernelClient(cmd) var response json.RawMessage var opts []option.RequestOption diff --git a/cmd/search_test.go b/cmd/search_test.go index c6dc7dd9..c9aae4f1 100644 --- a/cmd/search_test.go +++ b/cmd/search_test.go @@ -74,6 +74,8 @@ func TestSearchReadCommands(t *testing.T) { {[]string{"get", "srch_test"}, "/search/srch_test", "", `{"id":"srch_test","results":[]}`}, {[]string{"providers"}, "/search/providers", "", `[]`}, {[]string{"providers", "--slug", "exa&other=1"}, "/search/providers", "slug=exa%26other%3D1", `[]`}, + {[]string{"get", "srch_test", "-o", "json"}, "/search/srch_test", "", `{"id":"srch_test","results":[]}`}, + {[]string{"providers", "-o", "json"}, "/search/providers", "", `[]`}, } { t.Run(strings.Join(tc.args, " "), func(t *testing.T) { stdout, err := executeSearchCommand(t, func(w http.ResponseWriter, r *http.Request) { @@ -99,6 +101,7 @@ func TestSearchContents(t *testing.T) { want string }{ {"limit", []string{"--limit", "3"}, `{"limit":3}`}, + {"limit with json output", []string{"--limit", "3", "-o", "json"}, `{"limit":3}`}, {"result ids", []string{"--result-ids", "res_2,res_1", "--timeout-ms", "30000"}, `{"result_ids":["res_2","res_1"],"timeout_ms":30000}`}, {"content options", []string{"--limit", "1", "--content-source", "browser", "--content-format", "text", "--content-max-chars", "500", "--content-max-age-hours", "0", "--content-timeout-ms", "5000", "--content-browser-id", "br_1", "--content-browser-mode", "render"}, `{"limit":1,"content":{"source":"browser","format":"text","max_chars":500,"max_age_hours":0,"timeout_ms":5000,"browser":{"browser_id":"br_1","mode":"render"}}}`}, @@ -127,6 +130,7 @@ func TestSearchInvalidInput(t *testing.T) { for _, args := range [][]string{ {}, {" "}, {strings.Repeat("x", 2049)}, {"a", "b"}, {"test", "--provider", ""}, {"test", "--max-results", "0"}, {"test", "--max-results", "101"}, + {"test", "-o", "yaml"}, {"get", "srch_test", "-o", "yaml"}, {"contents", "srch_test", "--limit", "1", "-o", "yaml"}, {"--request", "null"}, {"--request", "[]"}, {"--request", "{"}, {"--request", `{}`}, {"--request", `{"query":1}`}, {"test", "--request", `{"query":"test"}`}, {"--request", `{}`, "--provider", "exa"}, {"--request", `{}`, "--max-results", "5"}, {"--request-file", "request.json"},