← back to Ventura Claw
server/connectors/colorapi.js
54 lines
// The Color API — no key. https://www.thecolorapi.com/docs
const BASE = "https://www.thecolorapi.com";
async function call(path) {
const r = await fetch(`${BASE}${path}`, {
headers: { Accept: "application/json", "User-Agent": "VenturaClaw/1.0" },
signal: AbortSignal.timeout(12_000),
});
if (!r.ok) throw new Error(`colorapi ${r.status}`);
return r.json();
}
function normHex(h) {
if (!h) return null;
let s = String(h).trim().replace(/^#/, "");
if (s.length === 3) s = s.split("").map(c => c + c).join("");
if (!/^[0-9a-fA-F]{6}$/.test(s)) return null;
return s.toLowerCase();
}
module.exports = {
meta: { id: "colorapi", name: "Color API", category: "creative", docsUrl: "https://www.thecolorapi.com/docs", auth: "none", realImpl: true },
fields: [],
configured() { return true; },
async health() {
try {
const d = await call("/id?hex=d4a04a&format=json");
return { ok: true, label: `live · sample: ${d.name?.value || "named"}` };
} catch (e) { return { ok: false, reason: e.message }; }
},
actions: {
async "identify"({ hex, rgb }) {
const h = normHex(hex);
if (!h && !rgb) throw new Error("hex or rgb required");
const qs = h ? `hex=${h}` : `rgb=${encodeURIComponent(rgb)}`;
const d = await call(`/id?${qs}&format=json`);
return {
hex: d.hex?.value, name: d.name?.value, rgb: d.rgb?.value,
hsl: d.hsl?.value, cmyk: d.cmyk?.value,
contrast_text: d.contrast?.value,
nearest_named: d.name?.exact_match_name === false ? d.name?.closest_named_hex : null,
};
},
async "scheme"({ hex, mode, count }) {
const h = normHex(hex);
if (!h) throw new Error("hex required");
const qs = new URLSearchParams({ hex: h, mode: mode || "analogic", count: String(Math.min(count || 5, 10)), format: "json" });
const d = await call(`/scheme?${qs}`);
return {
seed: d.seed?.hex?.value, mode: d.mode,
colors: (d.colors || []).map(c => ({ hex: c.hex?.value, name: c.name?.value, rgb: c.rgb?.value })),
scheme_image: d.image?.bare,
};
},
},
};