Files
hanzo.sh-native/scripts/build-polyglot.js
zeekayandHanzo Dev b455fe3138 one installer, three names — generate /install instead of committing it twice
`public/install` was a byte-identical copy of `public/install.sh`, added because
hanzo.ai/install.sh fetches the extensionless path and a 404 there is silent
(curl -f writes nothing and exits 22; sh reading an empty script exits 0; the
documented one-liner reports success and installs nothing). The reason is right.
The mechanism was 328 duplicated lines held together by a sentence in a commit
message saying the two must stay identical.

scripts/build-polyglot.js already reads public/install.sh to build the polyglot,
so it writes dist/install from the same string it already has. One source, three
published faces — `/`, `/install.sh`, `/install` — and the Dockerfile `cmp`s the
last two so "identical" is checked rather than promised.

Also asserted there: dist/install opens with #!/bin/sh and parses under dash. It
is piped into sh by a caller in another repo, so the shell that must accept it is
not the one whoever edits it will test with.

Verified: clean rebuild, all four polyglot asserts, cmp, every served path 200
(/, /install, /install.sh, /cli, /mcp, /dev, /desktop, /bot, /node, /full,
/llms.txt) and a mistyped path still 404s; `curl /install | sh -s -- --help`
prints the installer usage.

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

40 lines
1.9 KiB
JavaScript

#!/usr/bin/env node
/**
* Turn the built page into the polyglot, and emit the installer's second name.
*
* `dist/index.html` becomes a file that is simultaneously a shell script (line 1
* is `#!/bin/sh`, line 2 opens a `<<\EOF` heredoc that swallows the document, and
* the installer follows the closing `EOF`) and an HTML document (`</html>` is
* rewritten to `</html><!--`, so the shell half is a comment). That is what makes
* `curl hanzo.sh | sh` and opening hanzo.sh in a browser the same bytes.
*
* `dist/install` is the installer under the extensionless name — the path
* hanzo.ai/install.sh's one-liner actually fetches. It is GENERATED, not a second
* file in `public/`: two committed copies of an installer is one copy that
* drifts, and the one that drifts is the one nobody reads. `public/install.sh` is
* the only installer in this repo, and all three of its published faces — the
* polyglot, `/install.sh`, `/install` — are read from it here.
*/
import { readFileSync, writeFileSync } from 'fs'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
const rootDir = join(dirname(fileURLToPath(import.meta.url)), '..')
const distDir = join(rootDir, 'dist')
const installer = readFileSync(join(rootDir, 'public', 'install.sh'), 'utf-8')
// The heredoc body is the page; the installer follows it MINUS its own shebang,
// because line 1 of the polyglot is already `#!/bin/sh`.
const html = readFileSync(join(distDir, 'index.html'), 'utf-8').replace('</html>', '</html><!--')
const body = installer.slice(installer.indexOf('\n') + 1)
writeFileSync(join(distDir, 'index.html'), `#!/bin/sh\n<<\\EOF\n${html}\nEOF\n\n${body}\n`)
writeFileSync(join(distDir, 'install'), installer)
console.log('✓ Created polyglot index.html')
console.log(' curl hanzo.sh | sh → runs installer')
console.log(' open hanzo.sh → shows landing page')
console.log('✓ Wrote dist/install — the installer under its extensionless name')