ReferenceAPI

Data services

Reference for dd.http fetch against host allowlists, the dd.weather helpers, and dd.claudeUsage.

dd.http

Needs the http permission with the exact hostname allowlisted in the manifest (permissions.http.hosts). All traffic goes through the host's proxy; widgets have no direct network access at all.

dd.http.fetch()

dd.http.fetch(req: {
  url: string;
  method?: string;   // GET (default), POST, PUT, PATCH, DELETE, HEAD
  headers?: Record<string, string | { $secret: string }>;
  /** Extra query params merged into the URL host-side after allowlist
   *  validation. Values may be { $secret: name } like headers. */
  query?: Record<string, string | { $secret: string }>;
  body?: string;
  /** "dataUrl" returns the raw response bytes as a base64 data: URL
   *  (bodyText/json omitted), for images. Default "text". */
  as?: "text" | "dataUrl";
}): Promise<{
  status: number;
  headers: Record<string, string>;
  bodyText?: string;
  json?: unknown;    // parsed when the response is JSON
  dataUrl?: string;
}>

The rules, all enforced host-side:

  • Hosts. The URL's hostname must exactly match an entry in permissions.http.hosts. https only (http allowed for localhost). Redirects are followed up to 4 hops, each hop re-validated against the allowlist.
  • Methods. GET, POST, PUT, PATCH, DELETE, HEAD. Headers that would let a widget impersonate the browser session (host, cookie, origin, referer, connection-control headers) are refused; the response exposes a small safe set (content-type, content-length, date, cache-control, etag, last-modified).
  • Secrets. A header or query value of { "$secret": "settingKey" } names one of this widget's secretRef settings; the host injects the user's stored value after validation. The resolved value (and the merged URL) never appears in widget JS, logs, or error messages, and secret query params are never re-appended across redirects.
  • Sizes and pacing. Request body up to 256 KB; response up to 1 MB; query up to 32 pairs (keys 128 bytes, values 2048 bytes, final URL 8 KB); 30 second timeout; 2 concurrent requests per instance; 30 requests per minute by default, raised by the manifest's http.rateLimit up to 120.
  • Binary. as: "dataUrl" returns the bytes as a base64 data: URL (same 1 MB cap). The widget CSP allows img-src data:, so the result can feed an img or dd-icon directly.
const res = await dd.http.fetch({
  url: "https://api.example.com/v1/status",
  headers: { authorization: { $secret: "apiKey" } },
});
if (res.status === 200) render(res.json);

dd.weather

The shared weather stack, built on Open-Meteo. Convenience code, not a capability: everything routes through this widget's own grants. geocode needs geocoding-api.open-meteo.com in http.hosts, current needs api.open-meteo.com, and the geocode cache uses your storage grant.

dd.weather.describe()

dd.weather.describe(code: number, isDay: number | boolean): {
  label: string;
  glyph: WeatherGlyph;
}

type WeatherGlyph =
  | "clearDay" | "clearNight" | "partlyDay" | "partlyNight" | "cloudy"
  | "fog" | "drizzle" | "rain" | "snow" | "thunder";

Maps a WMO weather code to a human label plus a glyph name for icon().

dd.weather.geocode()

dd.weather.geocode(query: string, opts?: { cacheKey?: string }): Promise<{
  query: string;
  lat: number;
  lon: number;
  label: string;
}>

Resolves a place name, cached in the widget's own storage (one entry per cacheKey; a changed query re-fetches).

dd.weather.current()

dd.weather.current(opts: {
  lat: number;
  lon: number;
  units?: "celsius" | "fahrenheit";
}): Promise<{
  temperature: number;  // in the requested unit
  code: number;         // WMO code; feed it to describe()
  isDay: boolean;
}>

dd.weather.icon()

dd.weather.icon(glyph: WeatherGlyph | string): string

A built-in line-art icon as a data: URI, ready for <dd-icon src=...> (the widget CSP allows img-src data:). Unknown names return "".

dd.claudeUsage

Needs the claudeUsage permission.

dd.claudeUsage.status()

dd.claudeUsage.status(opts: { dirKey: string }): Promise<ClaudeUsage>

Claude Code's own usage as one aggregated snapshot. dirKey names one of this widget's filePath settings; the host resolves the .claude directory the user configured there (blank = the default install), reads the limit bars, today's tokens by model, and recently-active sessions, and refreshes the bars from Anthropic's usage endpoint in the background. The OAuth token, transcript contents, and raw paths never reach the widget. Poll it. 20 calls per rolling 10 seconds.

interface ClaudeUsage {
  /** False when the configured directory is not a readable Claude home:
   *  show a "set Claude home dir" hint. */
  dirOk: boolean;
  claudeVersion: string | null;
  /** Where limits came from: "live" (Anthropic's usage endpoint), "cache"
   *  (Claude Code's own cache), or "none" (nothing available yet). */
  source: "live" | "cache" | "none";
  /** When limits was produced (epoch ms), for an "as of" readout. */
  fetchedAtMs: number | null;
  /** One bar per active limit window; percent is 0..100. */
  limits: {
    kind: string;
    group: string;
    label: string;
    percent: number;
    severity: string;
    resetsAtMs: number | null;
    isActive: boolean;
  }[];
  /** Today's usage summed from the transcripts (local day), by model. */
  today: {
    byModel: {
      model: string;
      input: number;
      output: number;
      cacheRead: number;
      cacheWrite: number;
      messages: number;
    }[];
    totals: {
      input: number;
      output: number;
      cacheRead: number;
      cacheWrite: number;
      messages: number;
    };
  };
  /** Recently-active Claude Code sessions. */
  sessions: {
    name: string | null;
    cwd: string;
    status: string;
    updatedAtMs: number;
  }[];
}