mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-22 18:25:32 -04:00
43ecf3ff1b
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: Julian Coy <julian@ex-machina.co> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: James Long <longster@gmail.com> Co-authored-by: Simon Klee <hello@simonklee.dk> Co-authored-by: Jay <air@live.ca> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: James Long <jlongster@users.noreply.github.com> Co-authored-by: 冯基魁 <56265583+fengjikui@users.noreply.github.com> Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com> Co-authored-by: Victor Navarro <vn4varro@gmail.com>
51 lines
2.1 KiB
TypeScript
51 lines
2.1 KiB
TypeScript
import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
|
|
import { Athena } from "@opencode-ai/stats-core/athena"
|
|
import { ModelStatRepo } from "@opencode-ai/stats-core/domain/model"
|
|
import { layer as statsLayer } from "@opencode-ai/stats-core/runtime"
|
|
import { syncStats } from "@opencode-ai/stats-core/stat-sync"
|
|
import { Cause, Duration, Effect, Layer, Schedule } from "effect"
|
|
|
|
const SYNC_INTERVAL = "1 hour"
|
|
const SYNC_INTERVAL_MS = 3_600_000
|
|
|
|
const runtimeLayer = Layer.mergeAll(statsLayer, Athena.layer)
|
|
|
|
const daemon = Effect.gen(function* () {
|
|
yield* Effect.logInfo("stats sync daemon started")
|
|
yield* initialDelay()
|
|
|
|
// One full pass per UTC day (including the first pass after boot) refreshes the
|
|
// whole display window; every other pass only recomputes the current ISO week.
|
|
let lastFullDay = ""
|
|
const pass = Effect.gen(function* () {
|
|
const today = new Date().toISOString().slice(0, 10)
|
|
const full = lastFullDay !== today
|
|
yield* syncStats({ full })
|
|
if (full) lastFullDay = today
|
|
}).pipe(
|
|
Effect.catchCause((cause) =>
|
|
Effect.logWarning(`stats sync failed ${JSON.stringify({ cause: Cause.pretty(cause) })}`),
|
|
),
|
|
)
|
|
yield* pass.pipe(Effect.repeat(Schedule.fixed(SYNC_INTERVAL)))
|
|
}).pipe(Effect.forkScoped)
|
|
|
|
// A restarted daemon must not immediately re-run the expensive Athena pass; resume
|
|
// the hourly cadence from the last completed sync instead. This caps the Athena
|
|
// spend of a crash loop at one pass per interval.
|
|
const initialDelay = Effect.fnUntraced(function* () {
|
|
const modelStats = yield* ModelStatRepo
|
|
const lastSynced = yield* modelStats.lastSyncedAt().pipe(Effect.catchCause(() => Effect.succeed(null)))
|
|
if (!lastSynced) return
|
|
const delayMs = Math.min(SYNC_INTERVAL_MS - (Date.now() - lastSynced.getTime()), SYNC_INTERVAL_MS)
|
|
if (delayMs <= 0) return
|
|
yield* Effect.logInfo(
|
|
`stats sync delaying first pass ${JSON.stringify({ lastSyncedAt: lastSynced.toISOString(), delayMs })}`,
|
|
)
|
|
yield* Effect.sleep(Duration.millis(delayMs))
|
|
})
|
|
|
|
NodeRuntime.runMain(Layer.launch(Layer.effectDiscard(daemon).pipe(Layer.provide(runtimeLayer))), {
|
|
disableErrorReporting: true,
|
|
})
|