← Commit history
README.md+11−5
@@ -1,12 +1,18 @@ # adom-mouser -Mouser Electronics component search for the Adom ecosystem — one Rust binary, three modes:+Mouser Electronics component search for the Adom ecosystem, one Rust binary, three modes: -- **CLI verbs** — `adom-mouser search|part|health` for quick JSON-returning shell use.-- **Hydrogen webview app** — `adom-mouser app` opens a searchable UI as a first-class Hydrogen tab.-- **Backend HTTP service** — `adom-mouser serve` runs the shared proxy (port 8775) used by the `service-mouser` container.+- **CLI verbs**: `adom-mouser search|part|health` for quick JSON-returning shell use.+- **Hydrogen webview app**: `adom-mouser app` opens a searchable UI as a first-class Hydrogen tab.+- **Backend HTTP service**: `adom-mouser serve` runs the shared proxy (port 8775) used by the `service-mouser` container. -Replaces the former `gallia/services/mouser/server.js` + `gallia/mouser/mcp/` setup. No MCP server, no Adom Viewer dependency.+Replaces the earlier Node service and MCP server. No MCP server, no Adom Viewer dependency.++## Identity and access++**You never need a Mouser API key.** Any vendor credential is company-owned and lives only inside Adom's service container. The CLI reaches Adom's hosted Mouser backend automatically, with nothing to configure, and never calls the vendor directly.++What a search needs is your **Adom identity**, so it is attributed to a real Adom user: in an Adom container it is read automatically from `/var/run/adom/api-key`; elsewhere set `ADOM_API_KEY` to your Adom session token. An error reading *"this needs your ADOM identity, not a vendor API key"* means exactly that.  ## Quick start 
SKILL.md+34−18
@@ -7,9 +7,25 @@ description: Search Mouser Electronics for components, pricing, stock, and part  Search Mouser's catalog of millions of electronic components from thousands of manufacturers. Real-time pricing, stock, lead times, and product links. +## Identity and access (read this first)++**You never need a Mouser API key, and you must never ask a user for one.** Adom's Mouser key is a company-owned credential that lives only inside Adom's service container. The CLI reaches Adom's hosted Mouser backend automatically; there is nothing to configure.++What every search DOES need is an **Adom identity**, because Adom attributes each search to a real Adom user so shared vendor quota can be accounted for. It is automatic:++- In an Adom container the CLI reads `/var/run/adom/api-key` (your Adom session token) by itself. Nothing to set up.+- Anywhere else, set `ADOM_API_KEY` to the user's Adom session token.+- The backend validates that token against Adom's identity service, so the identity is proven, not self-reported.++Backend resolution, in order: `--api` / `$MOUSER_API` if set, then a local `adom-mouser serve` on `127.0.0.1`, then **Adom's hosted backend at `https://mouser-xr8t4f5d01bt.adom.cloud`**. It never falls back to calling Mouser directly.++If a search fails with **"this needs your ADOM identity, not a vendor API key"**, the machine presented no valid Adom token. The fix is signing in to Adom or setting `ADOM_API_KEY`. It is never a vendor key. If it fails with **"could not verify your identity right now"**, that is an Adom-side outage, not the user's credential: retry shortly.++`GET /health` and `GET /usage` on the backend stay open without a credential so monitoring keeps working. `GET /usage?day=YYYY-MM-DD` returns that day's searches by user (`searches_by_owner`), separate from monitoring traffic.+ ## What you have -A single CLI — `adom-mouser` — with three faces:+A single CLI, `adom-mouser`: with three faces:  1. **One-shot search from the shell**, for when the AI just needs the data:    ```bash@@ -43,7 +59,7 @@ Search by keyword, MPN, or Mouser PN. |---|---|---| | `--limit N` | 10 | Max results (1–50) | | `--in-stock-only` | false | Only parts currently in stock |-| `--api URL` / `$MOUSER_API` | auto | Backend URL. If unset, the CLI tries `http://127.0.0.1:8775`, and if that's also down it falls back to calling Mouser directly (requires `MOUSER_API_KEY`) |+| `--api URL` / `$MOUSER_API` | auto | Backend URL. If unset, the CLI tries a local backend on `http://127.0.0.1:8775`, then Adom's hosted backend. It never calls the vendor directly and never needs a vendor key |  Examples: ```bash@@ -112,7 +128,7 @@ Run the backend HTTP proxy. This is what the `service-mouser` container runs. Requires `MOUSER_API_KEY`. Endpoints:  ```-GET  /health                                  — health + live API ping+GET  /health, health + live API ping GET  /search?keyword=...&limit=10&inStockOnly=false GET  /part?partNumber=... POST /              {"action":"search"|"part"|"health", ...}@@ -124,7 +140,7 @@ GET  /              (text/html landing page) Check reachability.  ```bash-adom-mouser health                 # checks backend (or falls back to Mouser direct)+adom-mouser health                 # checks backend (local, then Adom's hosted backend) adom-mouser health --api http://127.0.0.1:8775 ``` @@ -132,21 +148,21 @@ adom-mouser health --api http://127.0.0.1:8775  Each component in `.components[]` has: -- `mouser_pn`    — Mouser part number, use this for BOM-- `mpn`          — manufacturer part number, use this for schematic/footprint-- `manufacturer` — manufacturer name+- `mouser_pn`: Mouser part number, use this for BOM+- `mpn`: manufacturer part number, use this for schematic/footprint+- `manufacturer`: manufacturer name - `description`, `category`-- `stock`        — integer count in Mouser's warehouse (live, 10-min cache on backend)-- `stock_text`   — raw Mouser text ("In Stock", "Non-Stocked", etc.)-- `popularity`   — `low` / `medium` / `high` heuristic from stock-- `lead_time`    — factory lead time if ordering more-- `unit_price`   — price at qty 1-- `price_tiers`  — array of `{qty, price}` for quantity price breaks-- `min_order`, `mult` — minimum order + order multiple-- `rohs`         — compliance flag-- `lifecycle`    — New Product / Active / NRND / Obsolete+- `stock`: integer count in Mouser's warehouse (live, 10-min cache on backend)+- `stock_text`: raw Mouser text ("In Stock", "Non-Stocked", etc.)+- `popularity`: `low` / `medium` / `high` heuristic from stock+- `lead_time`: factory lead time if ordering more+- `unit_price`: price at qty 1+- `price_tiers`: array of `{qty, price}` for quantity price breaks+- `min_order`, `mult`: minimum order + order multiple+- `rohs`: compliance flag+- `lifecycle`: New Product / Active / NRND / Obsolete - `datasheet_url`, `product_url`, `image_url`-- `attributes`   — array of `{name, value}` from the product-attribute table+- `attributes`: array of `{name, value}` from the product-attribute table  ## Typical workflows @@ -188,7 +204,7 @@ adom-mouser search "LM317" adom-mouser app        # UI reads $MOUSER_API automatically ``` -Gallia users don't need to set `MOUSER_API` explicitly — `adom-mouser` is a Tier A app (see `adom-app-model`), so it's installed by every `gallia/install.mjs` run from the wiki binary. The CLI autodetects a local `adom-mouser serve` on 127.0.0.1:8775 and falls back to calling Mouser directly if `MOUSER_API_KEY` is set locally.+Nobody needs to set `MOUSER_API`. Install with `adom-wiki pkg install adom/adom-mouser` (it also arrives with `adom/core`), and the CLI finds a backend by itself: a local `serve` on 127.0.0.1 if one is running, otherwise Adom's hosted backend. It never calls the vendor directly.  ### Local-only (quick testing) 
bin/adom-mouser
⋯ 1 unchanged line ⋯
package.json+2−4
@@ -1,6 +1,6 @@ {   "slug": "adom-mouser",-  "version": "1.0.8",+  "version": "1.0.9",   "type": "app",   "description": "Mouser component search in your workspace \u2014 stock, quantity price breaks, datasheets and lifecycle. CLI verbs, a Hydrogen app, and a shared backend service.",   "dependencies": {},@@ -16,11 +16,9 @@     "install.sh",     "uninstall.sh",     "package.json",-    "page.json",     "README.md",     "SKILL.md",-    "BUILD-SKILL.md",-    "screenshots/**"+    "BUILD-SKILL.md"   ],   "tags": [     "mouser",
page.json+2−4
@@ -4,7 +4,7 @@   "slug": "adom-mouser",   "title": "Mouser Electronics Search",   "brief": "Mouser component search in your workspace — stock, quantity price breaks, datasheets and lifecycle. CLI verbs, a Hydrogen app, and a shared backend service.",-  "version": "1.0.8",+  "version": "1.0.9",   "install": {     "binary_name": "adom-mouser",     "install_dir": "",@@ -77,11 +77,9 @@     "install.sh",     "uninstall.sh",     "package.json",-    "page.json",     "README.md",     "SKILL.md",-    "BUILD-SKILL.md",-    "screenshots/**"+    "BUILD-SKILL.md"   ],   "org": "adom" }\ No newline at end of file
skills/adom-mouser-build/SKILL.md+10−10
@@ -10,7 +10,7 @@ A release syncs five places to the same version X.Y.Z:  ## Steps -1. **Bump** — decide X.Y.Z (semver). Then:+1. **Bump**: decide X.Y.Z (semver). Then:    ```bash    echo "X.Y.Z" > VERSION    sed -i 's/^version = .*/version = "X.Y.Z"/' Cargo.toml@@ -39,15 +39,15 @@ A release syncs five places to the same version X.Y.Z:    ```    Runs: git commit + tag `vX.Y.Z`, `git push origin main --tags`, `gh release create`, `adom-wiki asset upload` (replaces the prior `docker_binary`), `adom-wiki page update`, and a final curl of the wiki binary to check `--version` returns X.Y.Z. -6. **Service container picks it up automatically** — `service/watchdog.sh` runs every 2 min, `git fetch`es origin/main, and rebuilds + restarts when HEAD moves. Verify within ~5 min:+6. **Service container picks it up automatically**: `service/watchdog.sh` runs every 2 min, `git fetch`es origin/main, and rebuilds + restarts when HEAD moves. Verify within ~5 min:    ```bash    curl -s https://mouser-<slug>.adom.cloud/health | jq '.version'   # → "X.Y.Z"    ``` -7. **Verify on a fresh user container** (optional, but catches gallia regressions):+7. **Verify on a fresh user container** (optional, but catches install regressions):    ```bash    # on a fresh container:-   node ~/gallia/install.mjs                 # Tier A block fetches from wiki+   adom-wiki pkg install adom/core          # pulls the latest CLIs from the wiki    adom-mouser --version                     # → "adom-mouser X.Y.Z"    ``` @@ -57,19 +57,19 @@ A release syncs five places to the same version X.Y.Z: - [ ] Wiki page at `apps/adom-mouser` renders the new `install_hint` one-liner - [ ] User has seen a working screenshot of the webview - [ ] No console errors via `GET /console`-- [ ] `gallia/install.mjs` Tier A block still works (pulls + installs the new version)-- [ ] **Brand audit — in-app icons are monochrome white.** Run:+- [ ] `adom-wiki pkg install adom/core` still pulls and installs the new version+- [ ] **Brand audit, in-app icons are monochrome white.** Run:       ```       grep -nE 'fill="#00b8b0"|stroke="#00b8b0"|color: var\(--accent\)' src/app/*.html src/app/*.svg 2>/dev/null       ```       Expected result: empty, OR only the text-color rules (`.mouser-pn`,       `<a>`, focus-rings) on non-SVG elements. Any match on an `<svg>` or-      a `.logo` / `.icon` class CSS rule is a bug — the brand skill+      a `.logo` / `.icon` class CSS rule is a bug, the brand skill       requires monochrome white for in-app icons. See       `app-creator` §1b and §Checklist.  ## Notes -- **Never skip the ralph loop** — a green `cargo build` is not proof the UI works. Fonts, search, card rendering must be seen working.-- **Wiki upload is idempotent** — `adom-wiki asset upload --asset-type docker_binary` replaces the prior binary for this app automatically.-- **Version discipline is load-bearing** — `refresh-wiki-catalog.mjs` uses semver comparison to surface stale installs; inconsistent versions silently break that.+- **Never skip the ralph loop**: a green `cargo build` is not proof the UI works. Fonts, search, card rendering must be seen working.+- **Wiki upload is idempotent**: `adom-wiki asset upload --asset-type docker_binary` replaces the prior binary for this app automatically.+- **Version discipline is load-bearing**: `refresh-wiki-catalog.mjs` uses semver comparison to surface stale installs; inconsistent versions silently break that.
skills/adom-mouser/SKILL.md+34−18
@@ -7,9 +7,25 @@ description: Search Mouser Electronics for components, pricing, stock, and part  Search Mouser's catalog of millions of electronic components from thousands of manufacturers. Real-time pricing, stock, lead times, and product links. +## Identity and access (read this first)++**You never need a Mouser API key, and you must never ask a user for one.** Adom's Mouser key is a company-owned credential that lives only inside Adom's service container. The CLI reaches Adom's hosted Mouser backend automatically; there is nothing to configure.++What every search DOES need is an **Adom identity**, because Adom attributes each search to a real Adom user so shared vendor quota can be accounted for. It is automatic:++- In an Adom container the CLI reads `/var/run/adom/api-key` (your Adom session token) by itself. Nothing to set up.+- Anywhere else, set `ADOM_API_KEY` to the user's Adom session token.+- The backend validates that token against Adom's identity service, so the identity is proven, not self-reported.++Backend resolution, in order: `--api` / `$MOUSER_API` if set, then a local `adom-mouser serve` on `127.0.0.1`, then **Adom's hosted backend at `https://mouser-xr8t4f5d01bt.adom.cloud`**. It never falls back to calling Mouser directly.++If a search fails with **"this needs your ADOM identity, not a vendor API key"**, the machine presented no valid Adom token. The fix is signing in to Adom or setting `ADOM_API_KEY`. It is never a vendor key. If it fails with **"could not verify your identity right now"**, that is an Adom-side outage, not the user's credential: retry shortly.++`GET /health` and `GET /usage` on the backend stay open without a credential so monitoring keeps working. `GET /usage?day=YYYY-MM-DD` returns that day's searches by user (`searches_by_owner`), separate from monitoring traffic.+ ## What you have -A single CLI — `adom-mouser` — with three faces:+A single CLI, `adom-mouser`: with three faces:  1. **One-shot search from the shell**, for when the AI just needs the data:    ```bash@@ -43,7 +59,7 @@ Search by keyword, MPN, or Mouser PN. |---|---|---| | `--limit N` | 10 | Max results (1–50) | | `--in-stock-only` | false | Only parts currently in stock |-| `--api URL` / `$MOUSER_API` | auto | Backend URL. If unset, the CLI tries `http://127.0.0.1:8775`, and if that's also down it falls back to calling Mouser directly (requires `MOUSER_API_KEY`) |+| `--api URL` / `$MOUSER_API` | auto | Backend URL. If unset, the CLI tries a local backend on `http://127.0.0.1:8775`, then Adom's hosted backend. It never calls the vendor directly and never needs a vendor key |  Examples: ```bash@@ -112,7 +128,7 @@ Run the backend HTTP proxy. This is what the `service-mouser` container runs. Requires `MOUSER_API_KEY`. Endpoints:  ```-GET  /health                                  — health + live API ping+GET  /health, health + live API ping GET  /search?keyword=...&limit=10&inStockOnly=false GET  /part?partNumber=... POST /              {"action":"search"|"part"|"health", ...}@@ -124,7 +140,7 @@ GET  /              (text/html landing page) Check reachability.  ```bash-adom-mouser health                 # checks backend (or falls back to Mouser direct)+adom-mouser health                 # checks backend (local, then Adom's hosted backend) adom-mouser health --api http://127.0.0.1:8775 ``` @@ -132,21 +148,21 @@ adom-mouser health --api http://127.0.0.1:8775  Each component in `.components[]` has: -- `mouser_pn`    — Mouser part number, use this for BOM-- `mpn`          — manufacturer part number, use this for schematic/footprint-- `manufacturer` — manufacturer name+- `mouser_pn`: Mouser part number, use this for BOM+- `mpn`: manufacturer part number, use this for schematic/footprint+- `manufacturer`: manufacturer name - `description`, `category`-- `stock`        — integer count in Mouser's warehouse (live, 10-min cache on backend)-- `stock_text`   — raw Mouser text ("In Stock", "Non-Stocked", etc.)-- `popularity`   — `low` / `medium` / `high` heuristic from stock-- `lead_time`    — factory lead time if ordering more-- `unit_price`   — price at qty 1-- `price_tiers`  — array of `{qty, price}` for quantity price breaks-- `min_order`, `mult` — minimum order + order multiple-- `rohs`         — compliance flag-- `lifecycle`    — New Product / Active / NRND / Obsolete+- `stock`: integer count in Mouser's warehouse (live, 10-min cache on backend)+- `stock_text`: raw Mouser text ("In Stock", "Non-Stocked", etc.)+- `popularity`: `low` / `medium` / `high` heuristic from stock+- `lead_time`: factory lead time if ordering more+- `unit_price`: price at qty 1+- `price_tiers`: array of `{qty, price}` for quantity price breaks+- `min_order`, `mult`: minimum order + order multiple+- `rohs`: compliance flag+- `lifecycle`: New Product / Active / NRND / Obsolete - `datasheet_url`, `product_url`, `image_url`-- `attributes`   — array of `{name, value}` from the product-attribute table+- `attributes`: array of `{name, value}` from the product-attribute table  ## Typical workflows @@ -188,7 +204,7 @@ adom-mouser search "LM317" adom-mouser app        # UI reads $MOUSER_API automatically ``` -Gallia users don't need to set `MOUSER_API` explicitly — `adom-mouser` is a Tier A app (see `adom-app-model`), so it's installed by every `gallia/install.mjs` run from the wiki binary. The CLI autodetects a local `adom-mouser serve` on 127.0.0.1:8775 and falls back to calling Mouser directly if `MOUSER_API_KEY` is set locally.+Nobody needs to set `MOUSER_API`. Install with `adom-wiki pkg install adom/adom-mouser` (it also arrives with `adom/core`), and the CLI finds a backend by itself: a local `serve` on 127.0.0.1 if one is running, otherwise Adom's hosted backend. It never calls the vendor directly.  ### Local-only (quick testing)