diff --git a/README.md b/README.md index 2e88b7a..4f8627a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![CI](https://github.com/SSHDotCodes/auto/actions/workflows/ci.yml/badge.svg)](https://github.com/SSHDotCodes/auto/actions/workflows/ci.yml) [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-22543d.svg)](LICENSE) -![Auto — local permission intelligence](site/assets/mountain.webp) +![Auto — local permission intelligence](site/assets/og.webp) **Local permission intelligence for coding agents.** Works with Claude Code, Codex, Gemini CLI, Cursor, GitHub Copilot CLI, Pi, OpenCode and Hermes, plus the Claude Agent SDK, LangChain and any custom harness. diff --git a/docs/assets.md b/docs/assets.md index 00db1a8..aa18385 100644 --- a/docs/assets.md +++ b/docs/assets.md @@ -1,9 +1,7 @@ # Asset provenance -`site/assets/mountain.png` was generated with OpenAI ImageGen for this project on 2026-09-09. Generation mode: text-to-image. Actual output: 1672×941 PNG. The requested 4K size was not the generated size; no 4K-resolution claim is made. WebP encodings and the 900-pixel responsive version are produced by `scripts/build-site.mjs`. +The page draws its starfield and horizon glow in CSS; it uses no photographs. The link preview `site/assets/og.png` is rendered from HTML by `scripts/render-social.mjs` (`node scripts/render-social.mjs og site/assets/og.png`) and encoded to `og.webp` by `scripts/build-site.mjs`; only the WebP is served. The same script renders the launch post image (`x`). -Prompt: +Typography: Geist and Geist Mono (variable), and Instrument Serif italic, sourced through their Fontsource npm packages. All three are distributed under the SIL Open Font License; copies are included beside the built font files in `site/assets/`. These retain their original licenses. The Auto mark is a project-created SVG. Project code is Apache-2.0. -> Use case: photorealistic-natural. Asset type: full-bleed background for the landing page of Auto, an elegant open-source local AI tool-permission classifier. Create a stunning, extraordinarily detailed, realistic alpine mountain photograph at first light. A monumental sharp snow-covered mountain massif with delicate geological detail, blue shadowed glaciers and rocky ridgelines, glowing warm pale sunlight on the summit, multiple layers of distant mountains and a sea of low mist. Panoramic 16:9, preferably 3840x2160. The mountain is dominant in the center-right with calm dark blue sky and mist extending to the left for white website typography. Deep natural midnight/slate blues, silver snow, subtle warm sunlight. Cinematic atmosphere with photographic restraint, crisp rich detail, grand scale and beautiful natural light. Edge-to-edge image, no border, no text, no lettering, no logo, no people, no buildings, no UI. Designed to remain beautiful when cropped on mobile. This is the actual hero photograph asset, not a website mockup. - -Typography: Manrope variable and Instrument Serif italic, sourced through their Fontsource npm packages. Both are distributed under the SIL Open Font License; copies are included beside the built font files in `site/assets/`. These retain their original licenses. The Auto mark is a project-created SVG. Project code is Apache-2.0; the generated mountain asset is included for reuse with this project, subject to applicable law concerning generated works. +Earlier versions used a generated mountain photograph (OpenAI ImageGen, 2026-09-09); it was removed in favor of the ssh.codes theme. diff --git a/docs/website.md b/docs/website.md index c9450db..cc7905d 100644 --- a/docs/website.md +++ b/docs/website.md @@ -1,9 +1,9 @@ # Website -`site/` is the complete static website. Run `npm ci && npm run build:site` to generate optimized WebP backgrounds, copy local fonts and their licenses, and copy the canonical installer scripts. `python site/serve.py --port 8542` serves only the intended public files on loopback. A reverse proxy provides HTTPS. No model inference or management interface is exposed through the website. +`site/` is the complete static website. Run `npm ci && npm run build:site` to encode the social preview image, copy local fonts and their licenses, and copy the canonical installer scripts. `python site/serve.py --port 8542` serves only the intended public files on loopback. A reverse proxy provides HTTPS. No model inference or management interface is exposed through the website. The production origin is a Raspberry Pi, managed as the `auto` app. Deploy the whole built site tree, set the command to `python3 serve.py --port 8542`, enable autostart and restart-always, and route `auto.ssh.codes` to the loopback origin. `/healthz` returns a minimal JSON readiness response. Before modifying an existing deployment, pull its complete current source and reapply intended changes to a fresh pull immediately before upload. Charts report the v3 benchmark (2,948 held-out tool calls in near-twin pairs) for the v3 models, three hosted guards and auto-0.4b-2. Metric selectors change both bars and accessible labels; all axes start at zero. Probe examples are static and clearly identified. Install controls generate agent/OS-specific commands and support copying or keyboard fallback. The site has no analytics or third-party font requests. -The mountain background was generated for this project using OpenAI ImageGen on September 9, 2026. The original asset is 1672×941; WebP derivatives preserve its composition. It depicts an imagined alpine scene, not an identified real location. See [asset provenance](assets.md) for the prompt and font licenses. +The site follows the ssh.codes theme: a black, CSS-only starfield and horizon glow, Geist and Geist Mono with Instrument Serif italic accents. It loads no images in the page itself. `assets/og.png`, the link preview, is rendered by `scripts/render-social.mjs` and served as WebP. See [asset provenance](assets.md) for fonts and licenses. diff --git a/package-lock.json b/package-lock.json index 5f09181..adb7703 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "name": "auto-local-site-and-adapters", "version": "0.1.0", "devDependencies": { - "@fontsource-variable/manrope": "5.3.0", + "@fontsource-variable/geist": "5.3.0", + "@fontsource-variable/geist-mono": "5.3.0", "@fontsource/instrument-serif": "5.3.0", "@playwright/test": "1.63.0", "prettier": "3.9.6", @@ -26,10 +27,20 @@ "tslib": "^2.4.0" } }, - "node_modules/@fontsource-variable/manrope": { + "node_modules/@fontsource-variable/geist": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@fontsource-variable/manrope/-/manrope-5.3.0.tgz", - "integrity": "sha512-6D5dgokHsWDDMtmXHznKa0hK229NN+1a4BLPmUCLqcO1Pw5EEhWY5RFt0AcXnVRAljFFPfRtLkJePQj6LSsV6g==", + "resolved": "https://registry.npmjs.org/@fontsource-variable/geist/-/geist-5.3.0.tgz", + "integrity": "sha512-j0m+vLQuG5XAYoHtGCVu0spvlGreR3EzpECUVzkFmI1mTVnAO38l/NEPDCFgZ177JxzYJCLSmTQibIiYPilGrA==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/geist-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/geist-mono/-/geist-mono-5.3.0.tgz", + "integrity": "sha512-vBbuwDEo9AkrqADMXOrlAR3DFcJi4/JxeuU43FoiQERnNwsfXNnvxvReZG02cQKmyk4DZkZdBZX3oTDvy2zBAw==", "dev": true, "license": "OFL-1.1", "funding": { diff --git a/package.json b/package.json index 2b6bdac..60df18f 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "format:check": "prettier --check 'site/*.{html,css,js}' 'src/auto_gate/adapters/*.mjs' 'scripts/*.mjs' 'tests/**/*.mjs' 'tests/site/*.js' playwright.config.js" }, "devDependencies": { - "@fontsource-variable/manrope": "5.3.0", + "@fontsource-variable/geist": "5.3.0", + "@fontsource-variable/geist-mono": "5.3.0", "@fontsource/instrument-serif": "5.3.0", "@playwright/test": "1.63.0", "prettier": "3.9.6", diff --git a/scripts/build-site.mjs b/scripts/build-site.mjs index 36d5555..2d657eb 100644 --- a/scripts/build-site.mjs +++ b/scripts/build-site.mjs @@ -13,22 +13,24 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const site = path.join(root, "site"); await mkdir(path.join(site, "assets"), { recursive: true }); await Promise.all([ - sharp(path.join(site, "assets/mountain.png")) - .webp({ quality: 90 }) - .toFile(path.join(site, "assets/mountain.webp")), - sharp(path.join(site, "assets/mountain.png")) - .resize({ width: 900 }) - .webp({ quality: 86 }) - .toFile(path.join(site, "assets/mountain-mobile.webp")), + // Social preview, rendered by scripts/social_images.py; the PNG source is not served. + sharp(path.join(site, "assets/og.png")) + .webp({ quality: 92 }) + .toFile(path.join(site, "assets/og.webp")), ...["install.sh", "install.ps1", "install.py"].map((name) => copyFile(path.join(root, "scripts", name), path.join(site, name)), ), ]); for (const [pkg, match, target] of [ [ - "@fontsource-variable/manrope", - "manrope-latin-wght-normal.woff2", - "manrope.woff2", + "@fontsource-variable/geist", + "geist-latin-wght-normal.woff2", + "geist.woff2", + ], + [ + "@fontsource-variable/geist-mono", + "geist-mono-latin-wght-normal.woff2", + "geist-mono.woff2", ], [ "@fontsource/instrument-serif", @@ -52,4 +54,4 @@ await writeFile( path.join(site, "probes.json"), JSON.stringify(probes, null, 2) + "\n", ); -console.log("Built local image, fonts, installers, and probe fixtures."); +console.log("Built social image, fonts, installers, and probe fixtures."); diff --git a/scripts/render-social.mjs b/scripts/render-social.mjs new file mode 100644 index 0000000..8d73eff --- /dev/null +++ b/scripts/render-social.mjs @@ -0,0 +1,156 @@ +// Social images in the site's theme, rendered with Playwright so they use the real fonts. +// node scripts/render-social.mjs og site/assets/og.png (link preview, 1200×630 at 2×) +// node scripts/render-social.mjs x OUT.png (launch post image, 1600×900 at 2×) +// Numbers: v3 benchmark (2,948 held-out tool calls) and median time per decision from the v0.4.0 release. +import { chromium } from "playwright"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import path from "node:path"; +import { mkdtemp, writeFile } from "node:fs/promises"; +import os from "node:os"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const font = (pkg, file) => + pathToFileURL(path.join(root, "node_modules", pkg, "files", file)).href; +const [kind, out] = process.argv.slice(2); +if (!["og", "x"].includes(kind) || !out) + throw new Error("usage: render-social.mjs og|x OUT.png"); + +const css = ` +@font-face { font-family: Geist; src: url(${font("@fontsource-variable/geist", "geist-latin-wght-normal.woff2")}); font-weight: 100 900; } +@font-face { font-family: "Geist Mono"; src: url(${font("@fontsource-variable/geist-mono", "geist-mono-latin-wght-normal.woff2")}); font-weight: 100 900; } +@font-face { font-family: Instrument; src: url(${font("@fontsource/instrument-serif", "instrument-serif-latin-400-italic.woff2")}); font-style: italic; } +* { box-sizing: border-box; margin: 0; } +body { width: var(--w); height: var(--h); background: #000; color: #f4f6fb; font-family: Geist; overflow: hidden; position: relative; } +body::before { content: ""; position: absolute; inset: 0; opacity: .6; background-image: + radial-gradient(1px 1px at 23px 41px, #ffffffb0, transparent), radial-gradient(1px 1px at 151px 97px, #ffffff70, transparent), + radial-gradient(1px 1px at 87px 171px, #dfeaff80, transparent), radial-gradient(1.5px 1.5px at 211px 23px, #ffffff90, transparent), + radial-gradient(1px 1px at 301px 233px, #ffffff60, transparent), radial-gradient(1.5px 1.5px at 397px 151px, #ffffff80, transparent); + background-size: 233px 233px, 277px 277px, 313px 313px, 389px 389px, 431px 431px, 523px 523px; } +.horizon { position: absolute; left: 50%; width: 3200px; aspect-ratio: 1; border-radius: 50%; transform: translateX(-30%); + background: radial-gradient(circle at 50% 0%, #16335f 0%, #0a1a33 9%, #03070f 22%, #000 34%); + box-shadow: 0 -1px 0 rgba(159,208,255,.55), 0 -18px 60px rgba(110,170,255,.28), 0 -80px 160px rgba(80,130,255,.12); } +.brand { display: flex; align-items: center; gap: 12px; font-weight: 600; letter-spacing: -.02em; } +.orb { width: 24px; height: 24px; border-radius: 50%; background: radial-gradient(circle at 34% 30%, #e2f1ff 0%, #6ea6f4 30%, #1d4c9c 60%, #04101f 85%); + box-shadow: 0 0 0 1px rgba(159,208,255,.4), 0 0 16px rgba(110,170,255,.55); } +.mono { font-family: "Geist Mono"; text-transform: uppercase; letter-spacing: .12em; font-weight: 500; } +h1 { font-weight: 600; letter-spacing: -.05em; line-height: 1; } +em { font-family: Instrument; font-style: italic; font-weight: 400; letter-spacing: -.01em; padding-right: .06em; + background: linear-gradient(100deg, #7597ff 0%, #95b2ff 50%, #b4ceff 100%); -webkit-background-clip: text; background-clip: text; color: transparent; } +.sky { color: #9fd0ff; } +.muted { color: rgba(226,233,247,.68); } +`; + +const rows = [ + ["auto-3b-3", "open weights · 3.08B", 96.0, "21 ms", true], + ["DeepSeek V4.1 Flash", "hosted · max reasoning", 95.6, "1.3 s", false], + ["Codex auto-review", "hosted · medium", 95.5, "–", false], + ["GLM-5.3 Flash", "hosted · high reasoning", 95.4, "1.2 s", false], + ["auto-0.4b-3", "open weights · 396M", 93.1, "7 ms", true], + ["auto-150m-3", "open weights · 150M", 88.8, "5 ms", true], +]; +const lo = 85, + hi = 100; +const pos = (v) => `${((v - lo) / (hi - lo)) * 100}%`; + +const x = ` + +
+
auto
v3 · open-weight tool-call guards
+

As accurate as hosted guards.
At 60× the speed.

+
+
ModelAccuracy · v3 benchmarkPer decision
+ ${rows + .map( + ([ + n, + s, + a, + t, + auto, + ]) => `
${n}${s}
+
${a.toFixed(1)}%
+
${t}
`, + ) + .join("")} +
${[85, 90, 95, 100].map((v) => `${v}%`).join("")}
+
+
2,948 held-out tool calls · Auto on one RTX PRO 6000 · hosted via API · Codex via CLI, not timedhuggingface.co/ProCreations
+`; + +const og = ` + +
+
auto
+
auto.ssh.codes
+
Local permission intelligence for coding agents
+

A little intelligence.
A clearer path.

+
+
96.0%v3 accuracy · auto-3b-3
+
21 msper decision
+
3open-weight models
+
+`; + +const [w, h] = kind === "x" ? [1600, 900] : [1200, 630]; +const browser = await chromium.launch(); +const page = await browser.newPage({ + viewport: { width: w, height: h }, + deviceScaleFactor: 2, +}); +// Loaded from a file so the file:// fonts are same-origin. +const html = path.join( + await mkdtemp(path.join(os.tmpdir(), "auto-social-")), + "card.html", +); +await writeFile( + html, + `${kind === "x" ? x : og}`, +); +await page.goto(pathToFileURL(html).href); +await page.evaluate(() => document.fonts.ready); +const families = await page.evaluate(() => + [...document.fonts].filter((f) => f.status === "loaded").map((f) => f.family), +); +if (families.length < 3) throw new Error(`fonts did not load: ${families}`); +await page.screenshot({ path: out }); +await browser.close(); +console.log(`wrote ${out}`); diff --git a/site/assets/geist-mono.woff2 b/site/assets/geist-mono.woff2 new file mode 100644 index 0000000..0750682 Binary files /dev/null and b/site/assets/geist-mono.woff2 differ diff --git a/site/assets/geist-mono.woff2.LICENSE b/site/assets/geist-mono.woff2.LICENSE new file mode 100644 index 0000000..2e4aef1 --- /dev/null +++ b/site/assets/geist-mono.woff2.LICENSE @@ -0,0 +1,93 @@ +Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git) GeistMono-Italic[wght].ttf: Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/site/assets/geist.woff2 b/site/assets/geist.woff2 new file mode 100644 index 0000000..991445d Binary files /dev/null and b/site/assets/geist.woff2 differ diff --git a/site/assets/manrope.woff2.LICENSE b/site/assets/geist.woff2.LICENSE similarity index 95% rename from site/assets/manrope.woff2.LICENSE rename to site/assets/geist.woff2.LICENSE index 462280f..98835ac 100644 --- a/site/assets/manrope.woff2.LICENSE +++ b/site/assets/geist.woff2.LICENSE @@ -1,4 +1,4 @@ -Copyright 2019 The Manrope Project Authors (https://github.com/sharanda/manrope) +Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font) Geist-Italic[wght].ttf: Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font) This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: diff --git a/site/assets/manrope.woff2 b/site/assets/manrope.woff2 deleted file mode 100644 index 71eb731..0000000 Binary files a/site/assets/manrope.woff2 and /dev/null differ diff --git a/site/assets/mark.svg b/site/assets/mark.svg index 3af2438..4da6196 100644 --- a/site/assets/mark.svg +++ b/site/assets/mark.svg @@ -1 +1 @@ - + diff --git a/site/assets/mountain-mobile.webp b/site/assets/mountain-mobile.webp deleted file mode 100644 index c3ee3a7..0000000 Binary files a/site/assets/mountain-mobile.webp and /dev/null differ diff --git a/site/assets/mountain.png b/site/assets/mountain.png deleted file mode 100644 index 0110831..0000000 Binary files a/site/assets/mountain.png and /dev/null differ diff --git a/site/assets/mountain.webp b/site/assets/mountain.webp deleted file mode 100644 index fe2aea8..0000000 Binary files a/site/assets/mountain.webp and /dev/null differ diff --git a/site/assets/og.png b/site/assets/og.png new file mode 100644 index 0000000..43abef0 Binary files /dev/null and b/site/assets/og.png differ diff --git a/site/assets/og.webp b/site/assets/og.webp new file mode 100644 index 0000000..57f9150 Binary files /dev/null and b/site/assets/og.webp differ diff --git a/site/index.html b/site/index.html index ad426bf..836e03d 100644 --- a/site/index.html +++ b/site/index.html @@ -3,7 +3,7 @@ - + Auto — A little intelligence. A clearer path. - + - Skip to content