+17





![opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)

![opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)
![opencode-agent[bot]](/assets/img/avatar_default.png)
opencode-agent[bot]
opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
LukeParkerDev
opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Aiden Cline
Brendan Allan
Aarav Sareen
Julian Coy
Brendan Allan
usrnk1
opencode
Vladimir Glafirov
Adam
Frank
Jay
Dustin Deus
Kit Langton
James Long
Simon Klee
Jay
Jack
David Hill
Aiden Cline
James Long
冯基魁
Aiden Cline
Victor Navarro
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,
|
|
})
|