RAG-1300: Add web_search binding kind support#13955
Conversation
🦋 Changeset detectedLatest commit: 9c09fcf The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
ffac1d2 to
344d2a1
Compare
create-cloudflare
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
Cloudflare Web Search is a new zero-config Workers binding. Declared
in wrangler.jsonc as a single object (there is exactly one shared web
corpus, so no namespace/instance/setting is required):
{ "web_search": { "binding": "WEBSEARCH" } }
At runtime the binding exposes a single `search()` method that
returns URLs and catalog metadata. The binding is always-remote --
Miniflare proxies to the production Web Search service via the
remote-bindings transport.
Changes in this PR:
@cloudflare/workers-utils
* environment.ts: `web_search` field (single-object shape)
* config.ts: default value
* validation.ts: `ConfigBindingFieldName` entry, friendly-name maps,
`notInheritable` registration using `validateNamedSimpleBinding`,
`safeBindings` entry
* worker.ts: `CfWebSearch` interface
* types.ts: `web_search` entries in `WorkerMetadataBinding` and
`Binding` discriminated unions
* map-worker-metadata-bindings.ts: `case "web_search":` arm
* tests: validation tests for the new field
wrangler
* deployment-bundle/create-worker-upload-form.ts: extract +
serialise into the upload metadata
* api/startDevWorker/utils.ts: config-to-bindings + metadata-to-bindings
switches
* api/remoteBindings/index.ts: marked always-remote
* deploy/config-diffs.ts: `reorderableBindings` and
`singleBindingFields` entries
* deploy/check-remote-secrets-override.ts: switch case
* dev/miniflare/index.ts: `WorkerOptionsBindings` pick, extract,
`warnOrError`, pass to miniflare options
* type-generation/index.ts: emit `WebSearch` runtime type in both
type-generation entry points
* user/user.ts: add `websearch:read` OAuth scope
* utils/print-bindings.ts: extract + render in startup banner
miniflare
* plugins/web-search/index.ts: new always-remote proxy plugin
* plugins/index.ts: register the plugin in PLUGINS, intersect
WorkerOptions, re-export
Companion changes land in workerd (Web Search type definitions) and
edgeworker-config-service (binding kind + pipeline stage).
* Run oxfmt: fix changeset paragraph wrapping + plugin import order (caught by check:format) * Update inline snapshots in user.test.ts, whoami.test.ts, and deploy/core.test.ts to include the new websearch:run OAuth scope in the login URL and whoami scope listing (11 snapshots) * Rename the OAuth scope from 'websearch:read' to 'websearch:run'. The scope is already registered backend-side as 'websearch:run', so using anything else would break the auth-scopes E2E test that validates scope acceptance against the live Cloudflare backend.
Adds a new top-level 'websearch' namespace with a single 'search'
subcommand:
npx wrangler websearch search <query>
npx wrangler websearch search <query> --limit 5
npx wrangler websearch search <query> --json
Both --limit and --json are optional. limit defaults to 10 server-side
and is capped at 20. Without --json the results render as a pretty
table (#, title, url, description, lastModified); with --json the raw
response body is printed verbatim.
The command POSTs to /accounts/{accountId}/websearch/search with body
{ query, limit? } and uses requireAuth(config) for credentials, same
pattern as 'wrangler ai-search search'.
* src/websearch/types.ts: response shape (items + metadata)
* src/websearch/client.ts: fetchResult wrapper
* src/websearch/index.ts: namespace metadata
* src/websearch/search.ts: command definition
* src/index.ts: registry.define + registerNamespace
* src/core/teams.d.ts: add 'Product: Web Search' to the Teams enum
* src/utils/add-created-resource-config.ts: exclude 'web_search' from
ValidKeys (singleton bindings can't go through the createdResource
config helper; same exclusion as 'ai' and 'browser')
* src/__tests__/index.test.ts: update top-level help snapshot to
include the new namespace
* .changeset: document the new command
The normalize-and-validate-config test asserts the default Config object satisfies the Config type. Adding web_search as a required Environment field (alongside ai/browser) means the expected defaults literal must include 'web_search: undefined' as well.
The scope registered in production is literally 'websearch.run' (with a dot, not a colon), differing from the more common '<resource>:<verb>' pattern used by most other scopes. Switching to the registered name lets the auth-scopes E2E test pass against the live Cloudflare backend. Updates the scope key in user.ts, the inline snapshots in user.test.ts / whoami.test.ts / deploy/core.test.ts, and the changeset.
d17a336 to
9c09fcf
Compare
| if (web_search.length > 0) { | ||
| output.push( | ||
| ...web_search.map(({ binding, remote }) => ({ | ||
| name: binding, | ||
| type: getBindingTypeFriendlyName("web_search"), | ||
| value: undefined, | ||
| mode: getMode({ | ||
| isSimulatedLocally: context.remoteBindingsDisabled || !remote, | ||
| }), | ||
| })) | ||
| ); | ||
| } |
There was a problem hiding this comment.
🟡 web_search print-bindings mode incorrectly shows as local instead of always-remote
The web_search binding is always remote — confirmed by pickRemoteBindings (packages/wrangler/src/api/remoteBindings/index.ts:51-54) which unconditionally returns true for web_search, and the warnOrError("web_search", ws.remote, "always-remote") call in packages/wrangler/src/dev/miniflare/index.ts:636-638. However, print-bindings.ts:364 computes the mode as isSimulatedLocally: context.remoteBindingsDisabled || !remote. Since remote is an optional field on CfWebSearch (packages/workers-utils/src/worker.ts:247) and users won't set it (it's always remote), !remote evaluates to !undefined === true, causing the binding to display as "simulated locally" rather than "remote". The other always-remote bindings (ai_search_namespaces at line 341 and ai_search at line 352) correctly use isSimulatedLocally: false unconditionally.
| if (web_search.length > 0) { | |
| output.push( | |
| ...web_search.map(({ binding, remote }) => ({ | |
| name: binding, | |
| type: getBindingTypeFriendlyName("web_search"), | |
| value: undefined, | |
| mode: getMode({ | |
| isSimulatedLocally: context.remoteBindingsDisabled || !remote, | |
| }), | |
| })) | |
| ); | |
| } | |
| if (web_search.length > 0) { | |
| output.push( | |
| ...web_search.map(({ binding }) => ({ | |
| name: binding, | |
| type: getBindingTypeFriendlyName("web_search"), | |
| value: undefined, | |
| mode: getMode({ isSimulatedLocally: false }), | |
| })) | |
| ); | |
| } |
Was this helpful? React with 👍 or 👎 to provide feedback.
Adds wrangler / miniflare / workers-utils support for the new
web_searchWorkers binding plus awrangler websearch searchcommand.includes:
Binding
Declared as a single object in
wrangler.jsonc(one shared corpus, no namespace/instance):{ "web_search": { "binding": "WEBSEARCH" } }At runtime the binding exposes a single
search()method returning{ items, metadata }. Always-remote in local dev: Miniflare proxies to the production service via the remote-bindings transport.CLI
--limitand--jsonare optional. Default output is a pretty table;--jsonprints the raw response.