mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
Every page that names points reads the cloud's rewards: the board (GET /v1/rewards), the reader's standing, badges and history (/v1/rewards/me), the listing (/v1/rewards/optin) and the rules table (/v1/rewards/rules), snapshotted at build into lib/data/rewards-rules.json and asked again when a page opens. The hard-coded RULES tiles, POINTS, the client-side score and lib/hanzo/board.ts are gone; /beta and /referral draw the same Rules and board and link to /leaderboard. /leaderboard is the home: tabs by source and period, Your standing, badges earned and locked, the rules, token milestones and levels, Link GitHub through the existing per-person connection (the page completes the grant GitHub returns), and Share your rank with a leaderboard card. /bounty prints the contribution kinds and cap, three steps, the open issues labelled bounty or good first issue in Hanzo's orgs (searched at build with a token only), My contributions and the rules. A star now counts for the linked GitHub account, so Earn more offers Link GitHub. The cards, nav, footer, llms.txt and sitemap carry both pages; /founder no longer says points go nowhere. Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
56 lines
1.8 KiB
JavaScript
56 lines
1.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// What `next build` reads, regenerated before it runs.
|
|
//
|
|
// The first group each write files of their own and read none the others
|
|
// write, so they run at once: one after another they were 23 s of every
|
|
// deploy, most of it waiting on the network. The last four read what the
|
|
// first group wrote (the open bounties are searched in the orgs the rewards
|
|
// rules name, the route tree's policy joins the catalog, and keep-stamps
|
|
// compares every snapshot), so they run after it, in order.
|
|
//
|
|
// Every script runs to its end before the build is refused, so one failure
|
|
// does not hide another.
|
|
import { spawn } from 'node:child_process'
|
|
import { existsSync } from 'node:fs'
|
|
|
|
const AT_ONCE = [
|
|
'gen-gui-css',
|
|
'rooms-assets',
|
|
'sync-mark',
|
|
'brands',
|
|
'zen',
|
|
'kai',
|
|
'coding',
|
|
'tools',
|
|
'sync-catalog',
|
|
'sync-models',
|
|
'sync-papers',
|
|
'sync-blog',
|
|
'sync-oss',
|
|
'sync-rewards',
|
|
'sync-releases',
|
|
'sync-menu',
|
|
]
|
|
const AFTER = ['sync-bounties', 'sync-rooms', 'sync-pages', 'keep-stamps']
|
|
|
|
// A script is TypeScript (`.ts`, run by Node's own type stripping) or a `.mjs` not yet converted.
|
|
const run = (name: string) =>
|
|
new Promise<string | null>((done) => {
|
|
spawn(process.execPath, [existsSync(`scripts/${name}.ts`) ? `scripts/${name}.ts` : `scripts/${name}.mjs`], { stdio: 'inherit' })
|
|
.on('error', (err) => done(`${name}: ${err.message}`))
|
|
.on('exit', (code, signal) => done(code === 0 ? null : `${name} exited ${signal ?? code}`))
|
|
})
|
|
|
|
const failed = (await Promise.all(AT_ONCE.map(run))).filter(Boolean)
|
|
if (failed.length) {
|
|
for (const f of failed) console.error(`prebuild: ${f}`)
|
|
process.exit(1)
|
|
}
|
|
for (const name of AFTER) {
|
|
const f = await run(name)
|
|
if (f) {
|
|
console.error(`prebuild: ${f}`)
|
|
process.exit(1)
|
|
}
|
|
}
|