Files
zooqueenandHanzo Dev f717dd89ad keys: the key a user pastes is sk-, not the retired hk-
28 apps told the user to paste an `hk-` key — input placeholders, settings
help text, READMEs, the error thrown when the field is empty — and five of
them validated that paste against /^hk-/ before sending it as a bearer.
Nothing mints that prefix any more: cloud accepts pk- and sk-, IAM mints
sk-. So the guard admitted a credential that authenticates nothing, and the
panel failed later at the gateway with nothing on screen to explain it.

The three leak detectors keep hk- and gain sk-. Salesforce's source scan
and the two Zendesk tests exist to catch a key committed to source or
echoed back through a request; retired keys are still in circulation and
leak exactly as badly, while a detector that only knew hk- would have gone
blind to every key minted from here on.

`sk-` used to be the example of the foreign key the guard screened out — an
OpenAI key. It cannot mean that now, and shape alone cannot tell one
vendor's sk- from another's, so those comments say what the check really
proves and point at the /v1/models call that proves the rest. The three
tests that pinned "sk- is rejected" now pin "hk- is rejected", which is the
invariant that survived.

Verified: 2029 tests pass across the 28 packages; typecheck clean on 25 of
them (hubspot, jupyter and teams fail identically at HEAD on JSX and ESM
type conflicts in files this does not touch); salesforce validate-source
green.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 18:07:16 -07:00

65 lines
2.4 KiB
JavaScript

// Build the desktop app's FRONTEND into dist/: bundle app.ts (ESM) and copy the
// static shell (index.html, styles.css). Tauri loads dist/ as its `frontendDist`
// (see src-tauri/tauri.conf.json). No framework — esbuild + Node stdlib only,
// mirroring @hanzo/pdf's and @hanzo/canva's build. Every model call goes to
// api.hanzo.ai via @hanzo/ai; the app holds no secret — the user pastes their own
// sk- key into the settings pane, stored only in the webview's localStorage.
//
// node build.js → production build (dist/index.html, app.js, styles.css)
// node build.js --watch → rebuild on change (Tauri dev serves dist/)
//
// This is the ONLY step that runs on a developer machine. Building the native
// installers (.dmg / .msi / .AppImage) is `tauri build`, which needs the Rust
// toolchain and code-signing — a CI job, never run locally.
import esbuild from "esbuild";
import { rmSync, mkdirSync, copyFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const watch = process.argv.includes("--watch");
const src = join(__dirname, "src");
const dist = join(__dirname, "dist");
async function build() {
rmSync(dist, { recursive: true, force: true });
mkdirSync(dist, { recursive: true });
// Bundle app.ts → dist/app.js as ESM for the Tauri webview (a modern WebView2 /
// WKWebView / WebKitGTK). @hanzo/ai and the @tauri-apps/* JS APIs are bundled
// in — the page is served standalone from dist/, no import map.
const ctx = await esbuild.context({
entryPoints: [join(src, "app.ts")],
outfile: join(dist, "app.js"),
bundle: true,
format: "esm",
platform: "browser",
target: ["chrome100", "edge100", "firefox100", "safari15"],
define: { "process.env.NODE_ENV": JSON.stringify(watch ? "development" : "production") },
sourcemap: true,
minify: !watch,
logLevel: "info",
});
await ctx.rebuild();
// Copy the static shell.
copyFileSync(join(src, "index.html"), join(dist, "index.html"));
copyFileSync(join(src, "styles.css"), join(dist, "styles.css"));
console.log("Hanzo desktop frontend built -> dist/");
console.log(" Tauri loads dist/index.html as its frontendDist.");
if (watch) {
await ctx.watch();
console.log("watching...");
} else {
await ctx.dispose();
}
}
build().catch((e) => {
console.error(e);
process.exit(1);
});