app
Chip Fetcher (cf)
Public Made by Adomby adom
Crawls the web on your behalf to gather everything about the chips you care about: datasheets, symbols, footprints, 3D models, stock and pricing, straight from the manufacturer first, into your EDA tool. Say "cf" to your AI.
master
John Lauer
1.3.0: Dock Bar contract (serve --print-url, /api/status LEDs, CORS, say strip), settings modal, taskbar progress, pup vault for logins, adom-bridge + pup_* transport, dashboard overhaul per adom-ui-design, doctor for first run
83b2ae8
1mo ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
//! Vendor logins live in pup's OS-keychain vault (pup-credentials skill);
//! chip-fetcher keeps no password file of its own. This module is a thin
//! proxy over the `credential_*` / `pup_login` / `pup_import_*` verbs so the
//! CLI and the dashboard never spell them out.
use anyhow::Result;
use serde_json::{json, Value};
/// Vendors whose logins are worth importing from the user's real browser.
pub const DEFAULT_VENDOR_HOSTS: &[&str] = &[
"componentsearchengine",
"samacsys",
"snapeda",
"snapmagic",
"ultralibrarian",
"mouser",
"digikey",
"arrow.com",
"ti.com",
"st.com",
"nxp.com",
"microchip",
"nordicsemi",
"lcsc",
];
/// `{ok, credentials:[{host, username, ...}]}` with passwords never present.
pub fn list() -> Result<Value> {
let v = crate::bridge::call("credential_list", &json!({}))?;
if !crate::bridge::succeeded(&v) {
return Ok(json!({
"ok": false,
"error": crate::bridge::error_text(&v),
"_hint": "Vendor logins are stored by pup on your desktop. Open Adom Hydrogen there so the bridge is connected, then reload.",
}));
}
let out = crate::bridge::output(&v);
let mut creds: Vec<Value> = out
.get("credentials")
.and_then(|c| c.as_array())
.cloned()
.unwrap_or_default();
for c in creds.iter_mut() {
if let Some(o) = c.as_object_mut() {
o.remove("password");
}
}
Ok(json!({
"ok": true,
"source": "pup",
"credentials": creds,
"_hint": "Passwords stay in the OS keychain on the desktop. Add one with `adom-chip-fetcher login <source> --user <u> --pass <p>` or import from your browser with `adom-chip-fetcher logins --import`.",
}))
}
pub fn set(host: &str, username: &str, password: &str) -> Result<Value> {
let v = crate::bridge::call("credential_set", &json!({"host": host, "username": username, "password": password}))?;
if crate::bridge::succeeded(&v) {
Ok(json!({"ok": true, "host": host, "username": username}))
} else {
Ok(json!({"ok": false, "error": crate::bridge::error_text(&v)}))
}
}
pub fn delete(host: &str) -> Result<Value> {
let v = crate::bridge::call("credential_delete", &json!({"host": host}))?;
if crate::bridge::succeeded(&v) {
Ok(json!({"ok": true, "removed": true, "host": host}))
} else {
Ok(json!({"ok": false, "error": crate::bridge::error_text(&v)}))
}
}
/// Read-only scan of the browser profiles on the desktop.
pub fn import_scan() -> Result<Value> {
let v = crate::bridge::call("pup_import_scan", &json!({}))?;
Ok(if crate::bridge::succeeded(&v) { crate::bridge::output(&v) } else { json!({"ok": false, "error": crate::bridge::error_text(&v)}) })
}
/// Import the user's saved browser logins (one consent dialog + one UAC on
/// the desktop). Counts only come back; plaintext never leaves pup.
pub fn import_browser_logins(host_filter: &[String]) -> Result<Value> {
let hosts: Vec<&str> = if host_filter.is_empty() { DEFAULT_VENDOR_HOSTS.to_vec() } else { host_filter.iter().map(|s| s.as_str()).collect() };
let v = crate::bridge::call("pup_import_browser_logins", &json!({"hostFilter": hosts}))?;
Ok(if crate::bridge::succeeded(&v) { crate::bridge::output(&v) } else { json!({"ok": false, "error": crate::bridge::error_text(&v)}) })
}
/// Sign into the page currently open in `session` from the vault (or with
/// explicit credentials, saved for next time). Submit is reason-gated by pup.
pub fn login(session: &str, username: Option<&str>, password: Option<&str>, host: Option<&str>, reason: &str) -> Result<Value> {
let mut args = json!({"sessionId": session, "submit": true, "submitReason": reason});
if let (Some(u), Some(p)) = (username, password) {
args["username"] = json!(u);
args["password"] = json!(p);
args["save"] = json!(true);
if let Some(h) = host {
args["host"] = json!(h);
}
}
let v = crate::bridge::call("pup_login", &args)?;
if crate::bridge::succeeded(&v) {
Ok(crate::bridge::output(&v))
} else {
Ok(json!({
"ok": false,
"errorCode": v.get("errorCode").cloned().unwrap_or(Value::Null),
"error": crate::bridge::error_text(&v),
}))
}
}