mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-08 01:43:07 +00:00
feat(teams,dsh): overview dashboard, lead task auth, and DSH launch tokens
- teams: cross-team overview read model + route/query/page; enforce member target visibility and restrict member task creation to the Lead - dsh: accept 0.2.x launch tokens from the printed URL, HAPI_DSH_TOKEN, or HAPI_DSH_TOKEN_FILE; bundled wrapper captures the printed token securely; per-session agent availability and remote event/auth handling - usage: support the 1d range and per-session totals in summaries - cli/web: team prompt/client plumbing, availability gating, locale and route wiring for the overview page Co-Authored-By: Claude Code <noreply@anthropic.com> Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { chmodSync, mkdirSync, renameSync, unlinkSync, writeFileSync } from 'node:fs'
|
||||
import { dirname } from 'node:path'
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
const dshBin = process.env.DSH_BIN?.trim()
|
||||
const tokenFile = process.env.DSH_TOKEN_FILE?.trim()
|
||||
if (!dshBin || !tokenFile) {
|
||||
console.error('dsh web wrapper requires DSH_BIN and DSH_TOKEN_FILE')
|
||||
process.exit(64)
|
||||
}
|
||||
|
||||
mkdirSync(dirname(tokenFile), { recursive: true, mode: 0o700 })
|
||||
try { unlinkSync(tokenFile) } catch (error) {
|
||||
if (error?.code !== 'ENOENT') throw error
|
||||
}
|
||||
|
||||
const child = spawn(process.env.DSH_NODE?.trim() || process.execPath, [dshBin, ...process.argv.slice(2)], {
|
||||
env: process.env,
|
||||
stdio: ['inherit', 'pipe', 'inherit']
|
||||
})
|
||||
|
||||
let pending = ''
|
||||
let tokenSaved = false
|
||||
const consume = (chunk) => {
|
||||
const text = String(chunk)
|
||||
process.stdout.write(text)
|
||||
pending += text
|
||||
const lines = pending.split(/\r?\n/u)
|
||||
pending = lines.pop() ?? ''
|
||||
for (const line of lines) {
|
||||
const match = /dsh web:\s*https?:\/\/[^\s?]+(?:\?[^\s]*)?token=([A-Za-z0-9_-]+)/u.exec(line)
|
||||
if (!match || tokenSaved) continue
|
||||
const temporary = `${tokenFile}.tmp-${process.pid}`
|
||||
writeFileSync(temporary, `${match[1]}\n`, { mode: 0o600 })
|
||||
chmodSync(temporary, 0o600)
|
||||
renameSync(temporary, tokenFile)
|
||||
tokenSaved = true
|
||||
}
|
||||
}
|
||||
child.stdout.on('data', consume)
|
||||
child.on('error', (error) => {
|
||||
console.error(`dsh web wrapper failed to start: ${error.message}`)
|
||||
})
|
||||
for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP']) {
|
||||
process.on(signal, () => child.kill(signal))
|
||||
}
|
||||
child.on('exit', (code, signal) => {
|
||||
if (pending) consume('')
|
||||
process.exit(signal ? 1 : (code ?? 1))
|
||||
})
|
||||
Reference in New Issue
Block a user