diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3e9af1b..fbd9082 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.4.0" + ".": "1.5.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 74e73bc..97db86d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 38 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-155e4761d62255841349c0f8a01b0a9c463ea1d1f2d6c4fd8d1a75c8bef6f226.yml -openapi_spec_hash: ab91f77e7c9d992400cbc7fc8a9e76c1 -config_hash: bff282047fafdad771fb7ec685f56944 +configured_endpoints: 40 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-a0dda03bbb600917cfb9add468cc4c8c84351a8dbbf61644dbc353263ca1748f.yml +openapi_spec_hash: c24264f32a46d9317aac5af9d6a396f7 +config_hash: 920678668dd2da6f8966fbf1b8fde4e2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8156589..89cdc76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.5.0 (2026-08-07) + +Full Changelog: [v1.4.0...v1.5.0](https://github.com/context-dot-dev/context-dev-cli/compare/v1.4.0...v1.5.0) + +### Features + +* **api:** api update ([e21a8ff](https://github.com/context-dot-dev/context-dev-cli/commit/e21a8ff46d495a59b9c751965bb9d980a0765c78)) +* **api:** api update ([48a68cb](https://github.com/context-dot-dev/context-dev-cli/commit/48a68cb9ad422157ee55deef66773a161e320f44)) +* **api:** api update ([ad50292](https://github.com/context-dot-dev/context-dev-cli/commit/ad502925051676385440177ba5a4113631ff2dfa)) +* **api:** api update ([5650e87](https://github.com/context-dot-dev/context-dev-cli/commit/5650e87248bd4d1dabd6027007f510650684d511)) +* **api:** api update ([4ca42b8](https://github.com/context-dot-dev/context-dev-cli/commit/4ca42b8b8bdbe6750a50db1bc39b4ff098346f65)) +* **api:** api update ([92be0b6](https://github.com/context-dot-dev/context-dev-cli/commit/92be0b69f74d40bddb2748939d27c5b9f2660f46)) +* **api:** manual updates ([35e256a](https://github.com/context-dot-dev/context-dev-cli/commit/35e256a85572f24f11f89dbc452a7fa81640cef3)) + ## 1.4.0 (2026-08-05) Full Changelog: [v1.3.0...v1.4.0](https://github.com/context-dot-dev/context-dev-cli/compare/v1.3.0...v1.4.0) diff --git a/go.mod b/go.mod index 349bddd..a5e14f2 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 github.com/charmbracelet/x/term v0.2.1 - github.com/context-dot-dev/context-go-sdk/v2 v2.7.0 + github.com/context-dot-dev/context-go-sdk/v2 v2.8.0 github.com/goccy/go-yaml v1.18.0 github.com/itchyny/json2yaml v0.1.4 github.com/muesli/reflow v0.3.0 diff --git a/go.sum b/go.sum index b30c4e4..738ff27 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payR github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/context-dot-dev/context-go-sdk/v2 v2.7.0 h1:zc4xevss1ms1jge3wTn0qgynanmORhvFJav8eLfaiIY= -github.com/context-dot-dev/context-go-sdk/v2 v2.7.0/go.mod h1:CvRANWLP0FHj5XyUwzEVX79ujdwr/2Jy375UVzKNQm8= +github.com/context-dot-dev/context-go-sdk/v2 v2.8.0 h1:K7a/NQIXa4C3VLiF+1cCDyAMGIEnsva2CdkKaYqzC5Q= +github.com/context-dot-dev/context-go-sdk/v2 v2.8.0/go.mod h1:CvRANWLP0FHj5XyUwzEVX79ujdwr/2Jy375UVzKNQm8= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/pkg/cmd/batch.go b/pkg/cmd/batch.go index 74c4a5b..1eff5c6 100644 --- a/pkg/cmd/batch.go +++ b/pkg/cmd/batch.go @@ -70,6 +70,22 @@ var batchList = cli.Command{ HideHelpCommand: true, } +var batchDelete = cli.Command{ + Name: "delete", + Usage: "Permanently delete a finished batch and its stored results. Active batches must\nsettle first.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "batch-id", + Usage: "ID of the batch to retrieve or cancel.", + Required: true, + PathParam: "batch_id", + }, + }, + Action: handleBatchDelete, + HideHelpCommand: true, +} + var batchCancel = cli.Command{ Name: "cancel", Usage: "Stop a batch from starting new pages. In-progress pages finish, and unused\ncredits are refunded.", @@ -112,39 +128,36 @@ var batchGetResults = cli.Command{ HideHelpCommand: true, } -var batchSubmit = requestflag.WithInnerFlags(cli.Command{ +var batchSubmit = cli.Command{ Name: "submit", - Usage: "Retrieve and normalize a person profile from identifiers.", + Usage: "Scrape 25K URLs or crawl large websites asynchronously.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[map[string]any]{ - Name: "identifiers", - Usage: "Known identifiers for the person. At least one identifier is required.", + Name: "input", + Usage: "Choose a URL list or a site crawl.", Required: true, - BodyPath: "identifiers", + BodyPath: "input", }, &requestflag.Flag[[]string]{ Name: "tag", - Usage: "Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters.", + Usage: "Tags stored on the batch. Filter the batch list by them later.", BodyPath: "tags", }, - &requestflag.Flag[int64]{ - Name: "timeout-ms", - Usage: "Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).", - BodyPath: "timeoutMS", + &requestflag.Flag[string]{ + Name: "webhook-url", + Usage: "URL notified when the batch finishes.", + BodyPath: "webhookUrl", + }, + &requestflag.Flag[string]{ + Name: "idempotency-key", + Usage: "Any string unique to this submission. Retries with the same key return the original batch.", + HeaderPath: "Idempotency-Key", }, }, Action: handleBatchSubmit, HideHelpCommand: true, -}, map[string][]requestflag.HasOuterFlag{ - "identifiers": { - &requestflag.InnerFlag[string]{ - Name: "identifiers.linkedin-url", - Usage: "LinkedIn profile URL, e.g. https://www.linkedin.com/in/yahia-bakour/.", - InnerField: "linkedinUrl", - }, - }, -}) +} func handleBatchRetrieve(ctx context.Context, cmd *cli.Command) error { client := contextdev.NewClient(getDefaultRequestOptions(cmd)...) @@ -229,6 +242,48 @@ func handleBatchList(ctx context.Context, cmd *cli.Command) error { }) } +func handleBatchDelete(ctx context.Context, cmd *cli.Command) error { + client := contextdev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("batch-id") && len(unusedArgs) > 0 { + cmd.Set("batch-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Batch.Delete(ctx, cmd.Value("batch-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "batch delete", + Transform: transform, + }) +} + func handleBatchCancel(ctx context.Context, cmd *cli.Command) error { client := contextdev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/batch_test.go b/pkg/cmd/batch_test.go index a14073e..123f80d 100644 --- a/pkg/cmd/batch_test.go +++ b/pkg/cmd/batch_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/context-dot-dev/context-dev-cli/internal/mocktest" - "github.com/context-dot-dev/context-dev-cli/internal/requestflag" ) func TestBatchRetrieve(t *testing.T) { @@ -38,6 +37,18 @@ func TestBatchList(t *testing.T) { }) } +func TestBatchDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "batch", "delete", + "--batch-id", "batch_9f2c8a", + ) + }) +} + func TestBatchCancel(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { @@ -71,42 +82,57 @@ func TestBatchSubmit(t *testing.T) { t, "--api-key", "string", "batch", "submit", - "--identifiers", "{linkedinUrl: https://www.linkedin.com/in/yahia-bakour/}", - "--tag", "production", - "--tag", "team-alpha", - "--timeout-ms", "1000", - ) - }) - - t.Run("inner flags", func(t *testing.T) { - // Check that inner flags have been set up correctly - requestflag.CheckInnerFlags(batchSubmit) - - // Alternative argument passing style using inner flags - mocktest.TestRunMockTestWithFlags( - t, - "--api-key", "string", - "batch", "submit", - "--identifiers.linkedin-url", "https://www.linkedin.com/in/yahia-bakour/", - "--tag", "production", - "--tag", "team-alpha", - "--timeout-ms", "1000", + "--input", "{data: {format: markdown, urls: [{url: https://example.com/products/anvil, itemId: sku-1, meta: {category: bar}}, {url: https://example.com/products/hammer, itemId: sku-2, meta: {foo: bar}}], options: {country: de, excludeSelectors: [x], includeImages: true, includeLinks: true, includeSelectors: [x], maxAgeMs: 0, pdf: {end: 1, ocr: 'true', shouldParse: 'true', start: 1}, settleAnimations: true, shortenBase64Images: true, useMainContentOnly: true, waitForMs: 0}}, mode: scrape}", + "--tag", "docs", + "--tag", "competitor", + "--webhook-url", "webhookUrl", + "--idempotency-key", "Idempotency-Key", ) }) t.Run("piping data", func(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + - "identifiers:\n" + - " linkedinUrl: https://www.linkedin.com/in/yahia-bakour/\n" + + "input:\n" + + " data:\n" + + " format: markdown\n" + + " urls:\n" + + " - url: https://example.com/products/anvil\n" + + " itemId: sku-1\n" + + " meta:\n" + + " category: bar\n" + + " - url: https://example.com/products/hammer\n" + + " itemId: sku-2\n" + + " meta:\n" + + " foo: bar\n" + + " options:\n" + + " country: de\n" + + " excludeSelectors:\n" + + " - x\n" + + " includeImages: true\n" + + " includeLinks: true\n" + + " includeSelectors:\n" + + " - x\n" + + " maxAgeMs: 0\n" + + " pdf:\n" + + " end: 1\n" + + " ocr: 'true'\n" + + " shouldParse: 'true'\n" + + " start: 1\n" + + " settleAnimations: true\n" + + " shortenBase64Images: true\n" + + " useMainContentOnly: true\n" + + " waitForMs: 0\n" + + " mode: scrape\n" + "tags:\n" + - " - production\n" + - " - team-alpha\n" + - "timeoutMS: 1000\n") + " - docs\n" + + " - competitor\n" + + "webhookUrl: webhookUrl\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, "--api-key", "string", "batch", "submit", + "--idempotency-key", "Idempotency-Key", ) }) } diff --git a/pkg/cmd/brand.go b/pkg/cmd/brand.go index 742bfe7..ba05fd9 100644 --- a/pkg/cmd/brand.go +++ b/pkg/cmd/brand.go @@ -154,7 +154,7 @@ var brandRetrieveSimplified = cli.Command{ var brandSearch = cli.Command{ Name: "search", - Usage: "Search brands by name or domain and get back up to 10 lightweight matches\n(domain, name, logo), most popular first: by Tranco rank, then market cap for\nbrands outside the Tranco list, with text relevance breaking ties. Matching is\nprefix-based with no typo tolerance, so it is suited to autocomplete. Only\nbrands already in the Context.dev index are returned — use /brand/retrieve to\nfetch (and index) a specific domain. Free on Pro and Scale plans; costs 1 credit\nper request on the Free and Starter plans.", + Usage: "Search brands by name or domain and get back up to 10 lightweight matches\n(domain, name, logo). Name matches rank ahead of domain matches; within each\ngroup the most popular brands come first: by Tranco rank, then market cap for\nbrands outside the Tranco list, with text relevance breaking ties. Matching is\nprefix-based with no typo tolerance, so it is suited to autocomplete. Only\nbrands already in the Context.dev index are returned — use /brand/retrieve to\nfetch (and index) a specific domain. Free on Pro and Scale plans; costs 1 credit\nper request on the Free and Starter plans.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 4db4f54..c2f399f 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -173,11 +173,20 @@ func init() { Commands: []*cli.Command{ &batchRetrieve, &batchList, + &batchDelete, &batchCancel, &batchGetResults, &batchSubmit, }, }, + { + Name: "people", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &peopleEnrich, + }, + }, { Name: "@manpages", Usage: "Generate documentation for 'man'", diff --git a/pkg/cmd/parse.go b/pkg/cmd/parse.go index 5403297..c047893 100644 --- a/pkg/cmd/parse.go +++ b/pkg/cmd/parse.go @@ -51,7 +51,7 @@ var parseHandle = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.Flag[any]{ Name: "ocr", - Usage: "When true for PDF inputs, detect and OCR images embedded in the selected pages, inserting recognized text at each image's position in page reading order while preserving the PDF text layer. pdf.start/pdf.end limit the inclusive page range. When false, all OCR is disabled, including the automatic scanned-PDF fallback.", + Usage: "When true for PDF inputs, OCR the selected pages that have no usable text layer (scans), replacing each recovered page's text with the OCR result while pages with a real text layer keep it. pdf.start/pdf.end limit the inclusive page range. Billed at 1 credit per page OCR actually recovered, on top of the base request cost. When false, no OCR runs.", Default: false, QueryPath: "ocr", }, diff --git a/pkg/cmd/person.go b/pkg/cmd/person.go new file mode 100644 index 0000000..efc6e30 --- /dev/null +++ b/pkg/cmd/person.go @@ -0,0 +1,153 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/context-dot-dev/context-dev-cli/internal/apiquery" + "github.com/context-dot-dev/context-dev-cli/internal/requestflag" + "github.com/context-dot-dev/context-go-sdk/v2" + "github.com/context-dot-dev/context-go-sdk/v2/option" + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" +) + +var peopleEnrich = requestflag.WithInnerFlags(cli.Command{ + Name: "enrich", + Usage: "Finds and normalizes the best available person candidate from additive identity\nclues, then assigns an identity match score from 0 to 100. Available on all paid\nplans. Successful requests cost 20 credits. Disposable and free email addresses\n(like gmail.com, yahoo.com) will throw a 422 error.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[map[string]any]{ + Name: "company", + BodyPath: "company", + }, + &requestflag.Flag[[]map[string]any]{ + Name: "education", + BodyPath: "education", + }, + &requestflag.Flag[string]{ + Name: "email", + BodyPath: "email", + }, + &requestflag.Flag[map[string]any]{ + Name: "location", + BodyPath: "location", + }, + &requestflag.Flag[map[string]any]{ + Name: "name", + BodyPath: "name", + }, + &requestflag.Flag[[]string]{ + Name: "social-url", + BodyPath: "social_urls", + }, + &requestflag.Flag[[]string]{ + Name: "tag", + Usage: "Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters.", + BodyPath: "tags", + }, + &requestflag.Flag[int64]{ + Name: "timeout-ms", + Usage: "Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).", + BodyPath: "timeoutMS", + }, + }, + Action: handlePeopleEnrich, + HideHelpCommand: true, +}, map[string][]requestflag.HasOuterFlag{ + "company": { + &requestflag.InnerFlag[string]{ + Name: "company.domain", + InnerField: "domain", + }, + &requestflag.InnerFlag[string]{ + Name: "company.name", + InnerField: "name", + }, + }, + "education": { + &requestflag.InnerFlag[string]{ + Name: "education.degree", + InnerField: "degree", + }, + &requestflag.InnerFlag[string]{ + Name: "education.field-of-study", + InnerField: "field_of_study", + }, + &requestflag.InnerFlag[int64]{ + Name: "education.graduation-year", + InnerField: "graduation_year", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "education.institution", + InnerField: "institution", + }, + }, + "location": { + &requestflag.InnerFlag[string]{ + Name: "location.city", + InnerField: "city", + }, + &requestflag.InnerFlag[string]{ + Name: "location.country", + InnerField: "country", + }, + &requestflag.InnerFlag[string]{ + Name: "location.region", + InnerField: "region", + }, + }, + "name": { + &requestflag.InnerFlag[string]{ + Name: "name.first", + InnerField: "first", + }, + &requestflag.InnerFlag[string]{ + Name: "name.last", + InnerField: "last", + }, + }, +}) + +func handlePeopleEnrich(ctx context.Context, cmd *cli.Command) error { + client := contextdev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := contextdev.PersonEnrichParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.People.Enrich(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "people enrich", + Transform: transform, + }) +} diff --git a/pkg/cmd/person_test.go b/pkg/cmd/person_test.go new file mode 100644 index 0000000..2a1a4b1 --- /dev/null +++ b/pkg/cmd/person_test.go @@ -0,0 +1,92 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/context-dot-dev/context-dev-cli/internal/mocktest" + "github.com/context-dot-dev/context-dev-cli/internal/requestflag" +) + +func TestPeopleEnrich(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "people", "enrich", + "--company", "{domain: analyticalengines.example, name: Analytical Engines}", + "--education", "{degree: x, field_of_study: x, graduation_year: 1900, institution: {domain: x, name: x}}", + "--email", "dev@stainless.com", + "--location", "{city: x, country: x, region: x}", + "--name", "{first: Ada, last: Lovelace}", + "--social-url", "https://www.linkedin.com/in/ada-lovelace/", + "--tag", "production", + "--tag", "team-alpha", + "--timeout-ms", "1000", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(peopleEnrich) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "people", "enrich", + "--company.domain", "analyticalengines.example", + "--company.name", "Analytical Engines", + "--education.degree", "x", + "--education.field-of-study", "x", + "--education.graduation-year", "1900", + "--education.institution", "{domain: x, name: x}", + "--email", "dev@stainless.com", + "--location.city", "x", + "--location.country", "x", + "--location.region", "x", + "--name.first", "Ada", + "--name.last", "Lovelace", + "--social-url", "https://www.linkedin.com/in/ada-lovelace/", + "--tag", "production", + "--tag", "team-alpha", + "--timeout-ms", "1000", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "company:\n" + + " domain: analyticalengines.example\n" + + " name: Analytical Engines\n" + + "education:\n" + + " - degree: x\n" + + " field_of_study: x\n" + + " graduation_year: 1900\n" + + " institution:\n" + + " domain: x\n" + + " name: x\n" + + "email: dev@stainless.com\n" + + "location:\n" + + " city: x\n" + + " country: x\n" + + " region: x\n" + + "name:\n" + + " first: Ada\n" + + " last: Lovelace\n" + + "social_urls:\n" + + " - https://www.linkedin.com/in/ada-lovelace/\n" + + "tags:\n" + + " - production\n" + + " - team-alpha\n" + + "timeoutMS: 1000\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "people", "enrich", + ) + }) +} diff --git a/pkg/cmd/version.go b/pkg/cmd/version.go index a2e6c7d..cf77cd0 100644 --- a/pkg/cmd/version.go +++ b/pkg/cmd/version.go @@ -2,4 +2,4 @@ package cmd -const Version = "1.4.0" // x-release-please-version +const Version = "1.5.0" // x-release-please-version diff --git a/pkg/cmd/web.go b/pkg/cmd/web.go index 9f5b105..04af894 100644 --- a/pkg/cmd/web.go +++ b/pkg/cmd/web.go @@ -583,7 +583,7 @@ var webWebCrawlMd = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[bool]{ Name: "pdf.ocr", - Usage: "When true, detect and OCR images embedded in the selected PDF pages, inserting recognized text at each image's position in page reading order while preserving the PDF text layer. This is separate from automatic scanned-PDF OCR fallback.", + Usage: "When true, OCR the selected PDF pages that have no usable text layer (scans), replacing each recovered page's text with the OCR result while pages with a real text layer keep it. Billed at 1 credit per page OCR actually recovered, on top of the base request cost.", InnerField: "ocr", }, &requestflag.InnerFlag[bool]{ @@ -698,12 +698,12 @@ var webWebScrapeHTML = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[any]{ Name: "pdf.ocr", - Usage: "When true, detect and OCR images embedded in the selected PDF pages, inserting recognized text at each image's position in page reading order while preserving the PDF text layer. This is separate from automatic scanned-PDF OCR fallback.", + Usage: "When true, OCR the selected PDF pages that have no usable text layer (scans), replacing each recovered page's text with the OCR result while pages with a real text layer keep it. Billed at 1 credit per page OCR actually recovered, on top of the base request cost. When false, no OCR runs.", InnerField: "ocr", }, &requestflag.InnerFlag[any]{ Name: "pdf.should-parse", - Usage: "When true, PDF URLs are fetched and parsed. When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned.", + Usage: "When true, PDF URLs are fetched and parsed. When false, PDF URLs are skipped and a 400 PDF_SKIPPED is returned.", InnerField: "shouldParse", }, &requestflag.InnerFlag[int64]{ @@ -912,12 +912,12 @@ var webWebScrapeMd = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[any]{ Name: "pdf.ocr", - Usage: "When true, detect and OCR images embedded in the selected PDF pages, inserting recognized text at each image's position in page reading order while preserving the PDF text layer. This is separate from automatic scanned-PDF OCR fallback.", + Usage: "When true, OCR the selected PDF pages that have no usable text layer (scans), replacing each recovered page's text with the OCR result while pages with a real text layer keep it. Billed at 1 credit per page OCR actually recovered, on top of the base request cost. When false, no OCR runs.", InnerField: "ocr", }, &requestflag.InnerFlag[any]{ Name: "pdf.should-parse", - Usage: "When true, PDF URLs are fetched and parsed. When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned.", + Usage: "When true, PDF URLs are fetched and parsed. When false, PDF URLs are skipped and a 400 PDF_SKIPPED is returned.", InnerField: "shouldParse", }, &requestflag.InnerFlag[int64]{ @@ -930,7 +930,7 @@ var webWebScrapeMd = requestflag.WithInnerFlags(cli.Command{ var webWebScrapeSitemap = cli.Command{ Name: "web-scrape-sitemap", - Usage: "Crawl an entire website's sitemap and return all discovered page URLs.", + Usage: "Crawl an entire website's sitemap and return all discovered page URLs. Pass\n`search` to have the crawled sitemap filtered down to the pages about a phrase\n(for example `pricing and plans` or `api authentication docs`), most relevant\nfirst — a searched crawl scans the whole sitemap and costs 2 credits instead\nof 1.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -950,6 +950,11 @@ var webWebScrapeSitemap = cli.Command{ Default: 10000, QueryPath: "maxLinks", }, + &requestflag.Flag[string]{ + Name: "search", + Usage: "Optional search phrase. When provided, the crawled sitemap is filtered to the pages whose URLs are about that phrase, most relevant first, and the request costs 2 credits instead of 1.", + QueryPath: "search", + }, &requestflag.Flag[string]{ Name: "sitemap-url", Usage: "Optional explicit sitemap URL. When provided, exactly this sitemap is crawled instead of discovering the domain's sitemaps.", diff --git a/pkg/cmd/web_test.go b/pkg/cmd/web_test.go index 8eda947..3505637 100644 --- a/pkg/cmd/web_test.go +++ b/pkg/cmd/web_test.go @@ -583,6 +583,7 @@ func TestWebWebScrapeSitemap(t *testing.T) { "--domain", "xxx", "--headers", "{foo: J!}", "--max-links", "1", + "--search", "help center and troubleshooting articles", "--sitemap-url", "https://example.com", "--tag", "production", "--tag", "team-alpha",