webfox 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -174,7 +174,7 @@ or, on Windows, `APPDATA`. Override it with `WEBFOX_CONFIG` or `--config <path>`
174
174
  For example:
175
175
 
176
176
  ```yaml
177
- $schema: https://unpkg.com/webfox@4.0.1/dist/config.schema.json
177
+ $schema: https://unpkg.com/webfox@latest/dist/config.schema.json
178
178
  defaults:
179
179
  search:
180
180
  provider: brave
@@ -252,6 +252,9 @@ configuration, progress, and errors.
252
252
  `web config default <capability> <provider>`.
253
253
  - **Missing credentials:** Run `web providers <id>` to check the required key
254
254
  names, or see the [provider guide](./docs/provider.md).
255
+ - **Invalid configuration in pi:** Pi continues to start, but Webfox registers
256
+ no tools and reports the error (on stderr in print and JSON modes). Fix the
257
+ configuration, then restart Pi or run `/reload` to load the extension again.
255
258
  - **No pi tools:** Select default providers in the shared configuration and
256
259
  restart pi. Installing the extension or setting keys alone isn't enough.
257
260
 
@@ -8,7 +8,7 @@ import {
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "webfox",
11
- version: "4.1.0",
11
+ version: "4.2.0",
12
12
  description: "Search the web, extract pages, get grounded answers, and run deep research with the web CLI, a TypeScript library, and a pi extension. Bring your own providers and API keys.",
13
13
  type: "module",
14
14
  files: [
@@ -138,7 +138,7 @@ var package_default = {
138
138
  // src/package-metadata.ts
139
139
  var PACKAGE_NAME = package_default.name;
140
140
  var PACKAGE_VERSION = package_default.version;
141
- var CONFIG_SCHEMA_URL = `https://unpkg.com/${PACKAGE_NAME}@${PACKAGE_VERSION}/dist/config.schema.json`;
141
+ var CONFIG_SCHEMA_URL = `https://unpkg.com/${PACKAGE_NAME}@latest/dist/config.schema.json`;
142
142
 
143
143
  // src/configuration/planning.ts
144
144
  import { Check, Errors } from "typebox/value";
@@ -11,7 +11,7 @@ import {
11
11
  providers,
12
12
  selectProvider,
13
13
  validateConfiguredOptions
14
- } from "./chunk-7BXAXZPA.js";
14
+ } from "./chunk-CIZXLW6C.js";
15
15
  import {
16
16
  cancelProcessGroup,
17
17
  killProcessGroup
@@ -69,7 +69,13 @@ function readFailure(error, options, path) {
69
69
  if (error instanceof WebfoxError) throw error;
70
70
  throw new WebfoxError(
71
71
  "INVALID_CONFIG",
72
- `Could not read configuration: ${path}. Check the path and file permissions.`
72
+ `Could not read configuration: ${path}. Check the path and file permissions.`,
73
+ {
74
+ configuration: {
75
+ source: path,
76
+ issues: ["Could not read file. Check the path and permissions."]
77
+ }
78
+ }
73
79
  );
74
80
  }
75
81
  function parseConfigDocument(text, source = "config.yaml") {
@@ -91,7 +97,15 @@ function parseConfigDocument(text, source = "config.yaml") {
91
97
  } catch {
92
98
  throw new WebfoxError(
93
99
  "INVALID_CONFIG",
94
- `Invalid YAML in ${source}. Use one YAML 1.2 document with unique keys, with string keys and finite numbers, without aliases or explicit tags.`
100
+ `Invalid YAML in ${source}. Use one YAML 1.2 document with unique keys, with string keys and finite numbers, without aliases or explicit tags.`,
101
+ {
102
+ configuration: {
103
+ source,
104
+ issues: [
105
+ "Invalid YAML. Use YAML 1.2 without duplicate keys, aliases, or explicit tags."
106
+ ]
107
+ }
108
+ }
95
109
  );
96
110
  }
97
111
  }
@@ -105,14 +119,30 @@ function parseConfig(text, source = "config.yaml") {
105
119
  function validateConfig(value, source = "configuration") {
106
120
  const schema = configurationSchema;
107
121
  if (!Check(schema, value)) {
108
- const detail = Errors(schema, value).slice(0, 3).map((error) => `${error.instancePath || "/"}: ${error.message}`).join("; ");
122
+ const errors = Errors(schema, value);
123
+ const detail = errors.slice(0, 3).map((error) => `${error.instancePath || "/"}: ${error.message}`).join("; ");
109
124
  throw new WebfoxError(
110
125
  "INVALID_CONFIG",
111
- `Invalid ${source}: ${detail}. Provider options belong under providers.<id>.options.<capability>.`
126
+ `Invalid ${source}: ${detail}. Provider options belong under providers.<id>.options.<capability>.`,
127
+ { configuration: { source, issues: configurationIssues(errors) } }
112
128
  );
113
129
  }
114
130
  return structuredClone(value);
115
131
  }
132
+ function configurationIssues(errors) {
133
+ const issues = /* @__PURE__ */ new Map();
134
+ for (const error of errors) {
135
+ if (error.keyword === "additionalProperties") continue;
136
+ const path = error.instancePath || "/";
137
+ if (issues.has(path)) continue;
138
+ const unknownKey = error.keyword === "boolean" && error.schemaPath.endsWith("/additionalProperties");
139
+ const key = path.slice(1).split("/").map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~")).join(".");
140
+ const message = unknownKey ? `Unknown key: ${key}` : error.keyword === "enum" || error.keyword === "const" ? `${path}: unsupported value` : `${path}: ${error.message}`;
141
+ issues.set(path, message);
142
+ if (issues.size === 3) break;
143
+ }
144
+ return issues.size ? [...issues.values()] : ["Invalid configuration structure."];
145
+ }
116
146
  function redactConfig(config) {
117
147
  const visit2 = (entry) => {
118
148
  if (Array.isArray(entry)) return entry.map(visit2);
@@ -645,26 +675,29 @@ function createWebfox(options = {}) {
645
675
  }
646
676
  };
647
677
  const definition = selectProvider(config, capability, selected);
648
- return outward.value({
678
+ const defaults = outward.value({
679
+ options: effectiveOptions(
680
+ config,
681
+ definition,
682
+ capability,
683
+ {},
684
+ "defaults"
685
+ ),
686
+ ...capability === "search" ? { maxResults: config.defaults?.search?.maxResults ?? 5 } : {}
687
+ });
688
+ return {
649
689
  capability,
650
690
  provider: selected,
651
691
  configured: configured(definition, capability),
692
+ // Static provider schemas contain no credentials. Redacting property
693
+ // names such as maximum_number_of_tokens would invalidate the schema.
652
694
  optionSchema: optionSchema(
653
695
  definition,
654
696
  capability,
655
697
  "defaults"
656
698
  ),
657
- defaults: {
658
- options: effectiveOptions(
659
- config,
660
- definition,
661
- capability,
662
- {},
663
- "defaults"
664
- ),
665
- ...capability === "search" ? { maxResults: config.defaults?.search?.maxResults ?? 5 } : {}
666
- }
667
- });
699
+ defaults
700
+ };
668
701
  }
669
702
  };
670
703
  }
package/dist/cli.js CHANGED
@@ -11,12 +11,12 @@ import {
11
11
  redactConfig,
12
12
  resolveConfigPath,
13
13
  setCapabilityDefault
14
- } from "./chunk-2DWEBTPX.js";
14
+ } from "./chunk-KC6K6LCQ.js";
15
15
  import "./chunk-WCTOYZVB.js";
16
16
  import {
17
17
  PACKAGE_VERSION,
18
18
  validateConfiguredOptions
19
- } from "./chunk-7BXAXZPA.js";
19
+ } from "./chunk-CIZXLW6C.js";
20
20
  import "./chunk-ZVTVERW5.js";
21
21
  import {
22
22
  CAPABILITIES,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://unpkg.com/webfox@4.1.0/dist/config.schema.json",
3
+ "$id": "https://unpkg.com/webfox@latest/dist/config.schema.json",
4
4
  "title": "webfox configuration",
5
5
  "type": "object",
6
6
  "additionalProperties": false,
package/dist/errors.d.ts CHANGED
@@ -4,11 +4,19 @@ export declare class WebfoxError extends Error {
4
4
  readonly options: {
5
5
  cause?: unknown;
6
6
  retryable?: boolean;
7
+ configuration?: {
8
+ source: string;
9
+ issues: string[];
10
+ };
7
11
  };
8
12
  readonly name = "WebfoxError";
9
13
  constructor(code: WebfoxErrorCode, message: string, options?: {
10
14
  cause?: unknown;
11
15
  retryable?: boolean;
16
+ configuration?: {
17
+ source: string;
18
+ issues: string[];
19
+ };
12
20
  });
13
21
  toJSON(): SerializedError;
14
22
  }
package/dist/index.js CHANGED
@@ -6,12 +6,12 @@ import {
6
6
  resolveConfigPath,
7
7
  setCapabilityDefault,
8
8
  validateConfig
9
- } from "./chunk-2DWEBTPX.js";
9
+ } from "./chunk-KC6K6LCQ.js";
10
10
  import "./chunk-WCTOYZVB.js";
11
11
  import {
12
12
  CONFIG_SCHEMA_URL,
13
13
  validateConfiguredOptions
14
- } from "./chunk-7BXAXZPA.js";
14
+ } from "./chunk-CIZXLW6C.js";
15
15
  import "./chunk-ZVTVERW5.js";
16
16
  import {
17
17
  CAPABILITIES,
@@ -0,0 +1,2 @@
1
+ import type { WebfoxError } from "./errors.js";
2
+ export declare function configurationDiagnostic(error: WebfoxError): string;
package/dist/pi.js CHANGED
@@ -2,15 +2,18 @@ import {
2
2
  renderTextDocument
3
3
  } from "./chunk-QRHDXM2L.js";
4
4
  import {
5
- createWebfox
6
- } from "./chunk-2DWEBTPX.js";
5
+ createWebfox,
6
+ resolveConfigPath
7
+ } from "./chunk-KC6K6LCQ.js";
7
8
  import "./chunk-WCTOYZVB.js";
8
- import "./chunk-7BXAXZPA.js";
9
+ import "./chunk-CIZXLW6C.js";
9
10
  import "./chunk-ZVTVERW5.js";
10
11
  import {
11
12
  CAPABILITIES
12
13
  } from "./chunk-WVHMWLKU.js";
13
- import "./chunk-U4BLULLV.js";
14
+ import {
15
+ WebfoxError
16
+ } from "./chunk-U4BLULLV.js";
14
17
 
15
18
  // src/pi.ts
16
19
  import { mkdtemp, writeFile } from "node:fs/promises";
@@ -417,6 +420,24 @@ function prepareToolArguments(schema, args) {
417
420
  return args;
418
421
  }
419
422
 
423
+ // src/pi-diagnostics.ts
424
+ import { homedir } from "node:os";
425
+ import { sep } from "node:path";
426
+ function configurationDiagnostic(error) {
427
+ const diagnostic = error.options.configuration;
428
+ const source = diagnostic?.source ?? resolveConfigPath();
429
+ const home = homedir() + sep;
430
+ const path = source.startsWith(home) ? `~/${source.slice(home.length)}` : source;
431
+ const issues = diagnostic?.issues ?? [error.message];
432
+ return [
433
+ "Webfox disabled \u2014 invalid configuration",
434
+ ` ${path}`,
435
+ ...issues.map((issue) => ` ${issue}`),
436
+ "",
437
+ "Fix the configuration, then /reload."
438
+ ].join("\n");
439
+ }
440
+
420
441
  // src/pi.ts
421
442
  function webExtension(pi) {
422
443
  const clients = /* @__PURE__ */ new Map();
@@ -428,10 +449,24 @@ function webExtension(pi) {
428
449
  }
429
450
  return client;
430
451
  };
431
- const initial = clientFor(process.cwd());
432
- const selected = CAPABILITIES.filter(
433
- (capability) => initial.inspectCapability(capability).provider
434
- );
452
+ let selected;
453
+ try {
454
+ const initial = clientFor(process.cwd());
455
+ selected = CAPABILITIES.map(
456
+ (capability) => initial.inspectCapability(capability)
457
+ ).filter((inspection) => inspection.provider);
458
+ } catch (error) {
459
+ if (!(error instanceof WebfoxError) || !["INVALID_CONFIG", "INVALID_INPUT", "PROVIDER_UNAVAILABLE"].includes(
460
+ error.code
461
+ ))
462
+ throw error;
463
+ const message = configurationDiagnostic(error);
464
+ pi.on("session_start", (_event, context) => {
465
+ if (context.hasUI) context.ui.notify(message, "error");
466
+ else console.error(message);
467
+ });
468
+ return;
469
+ }
435
470
  if (!selected.length)
436
471
  pi.on("session_start", (_event, context) => {
437
472
  if (context.hasUI)
@@ -440,8 +475,8 @@ function webExtension(pi) {
440
475
  "warning"
441
476
  );
442
477
  });
443
- for (const capability of selected) {
444
- const inspection = initial.inspectCapability(capability);
478
+ for (const inspection of selected) {
479
+ const capability = inspection.capability;
445
480
  const provider = inspection.provider;
446
481
  const fields = capability === "contents" ? { urls: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }) } : capability === "research" ? { input: Type.String({ minLength: 1 }) } : {
447
482
  queries: Type.Array(Type.String({ minLength: 1 }), {
@@ -1,4 +1,4 @@
1
- $schema: https://unpkg.com/webfox@4.0.1/dist/config.schema.json
1
+ $schema: https://unpkg.com/webfox@latest/dist/config.schema.json
2
2
  defaults:
3
3
  search:
4
4
  provider: brave
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webfox",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Search the web, extract pages, get grounded answers, and run deep research with the web CLI, a TypeScript library, and a pi extension. Bring your own providers and API keys.",
5
5
  "type": "module",
6
6
  "files": [