diff --git a/README.md b/README.md index 20a7fe3..e42feb8 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ All configuration is via environment variables. Set them in your shell profile ( | Variable | Default | Description | |----------|---------|-------------| | `OPENCODE_ENABLE_TELEMETRY` | *(unset)* | Set to any non-empty value to enable the plugin | -| `OPENCODE_OTLP_ENDPOINT` | `http://localhost:4317` | OTLP collector endpoint. For `grpc`, use the collector host/port. For `http/protobuf` and `http/json`, use the base URL and the plugin will append `/v1/traces`, `/v1/metrics`, and `/v1/logs`. | +| `OPENCODE_OTLP_ENDPOINT` | `http://localhost:4317` | OTLP collector endpoint. Always include a URL scheme. For `grpc`, use the collector URL (for example `http://localhost:4317` or `grpc://collector:4317`). For `http/protobuf` and `http/json`, use the base URL and the plugin will append `/v1/traces`, `/v1/metrics`, and `/v1/logs`. | | `OPENCODE_OTLP_PROTOCOL` | `grpc` | OTLP transport protocol: `grpc`, `http/protobuf`, or `http/json` | | `OPENCODE_OTLP_METRICS_INTERVAL` | `60000` | Metrics export interval in milliseconds | | `OPENCODE_OTLP_LOGS_INTERVAL` | `5000` | Logs export interval in milliseconds | @@ -111,6 +111,8 @@ export OPENCODE_OTLP_PROTOCOL=grpc opencode ``` +Always set `OPENCODE_OTLP_ENDPOINT` to a full URL with a scheme. Scheme-less values like `localhost:4317` are rejected. + For `OPENCODE_OTLP_PROTOCOL=http/protobuf` or `OPENCODE_OTLP_PROTOCOL=http/json`, set `OPENCODE_OTLP_ENDPOINT` to the collector base URL rather than a per-signal path. The plugin expands it to `/v1/traces`, `/v1/metrics`, and `/v1/logs` automatically. ### Headers and resource attributes diff --git a/src/probe.ts b/src/probe.ts index f7e66e5..035f974 100644 --- a/src/probe.ts +++ b/src/probe.ts @@ -10,6 +10,7 @@ export type ProbeResult = { ok: boolean; ms: number; error?: string } export function parseEndpoint(endpoint: string): { host: string; port: number } | null { try { const url = new URL(endpoint) + if (!url.hostname) return null const defaultPort = url.protocol === "http:" ? 80 : url.protocol === "https:" ? 443 : 4317 return { host: url.hostname, port: url.port ? parseInt(url.port, 10) : defaultPort } } catch { diff --git a/tests/probe.test.ts b/tests/probe.test.ts index 66e94cb..4cf0db8 100644 --- a/tests/probe.test.ts +++ b/tests/probe.test.ts @@ -22,12 +22,17 @@ describe("parseEndpoint", () => { test("returns null for invalid URLs", () => { expect(parseEndpoint("not a url")).toBeNull() }) + + test("returns null for URLs without a hostname", () => { + expect(parseEndpoint("localhost:4317")).toBeNull() + }) }) describe("probeEndpoint", () => { test("returns error for malformed URL (no scheme)", async () => { const result = await probeEndpoint("localhost:4317") expect(result.ok).toBe(false) + expect(result.error).toContain("invalid endpoint URL") expect(result.error).toBeDefined() })