@vikrant82/opencode-cache-keepalive 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/logger.ts CHANGED
@@ -1,21 +1,47 @@
1
+ import { appendFileSync, mkdirSync } from "node:fs"
2
+ import { dirname } from "node:path"
3
+
1
4
  const PREFIX = "[cache-keepalive]"
2
5
 
3
6
  export class Logger {
4
- constructor(private readonly debug: boolean) {}
7
+ private readonly logPath: string
8
+ private readonly debug: boolean
9
+ private initialized = false
10
+
11
+ constructor(debug: boolean, logPath: string) {
12
+ this.debug = debug
13
+ this.logPath = logPath
14
+ }
5
15
 
6
16
  info(message: string, data?: unknown) {
7
- if (this.debug) console.log(`${PREFIX} ${message}`, data ?? "")
17
+ this.write("INFO", message, data)
8
18
  }
9
19
 
10
20
  warn(message: string, data?: unknown) {
11
- console.warn(`${PREFIX} ${message}`, data ?? "")
21
+ this.write("WARN", message, data)
12
22
  }
13
23
 
14
24
  error(message: string, data?: unknown) {
15
- console.error(`${PREFIX} ${message}`, data ?? "")
25
+ this.write("ERROR", message, data)
16
26
  }
17
27
 
18
28
  dbg(message: string, data?: unknown) {
19
- if (this.debug) console.log(`${PREFIX}:debug ${message}`, data ?? "")
29
+ if (this.debug) this.write("DEBUG", message, data)
30
+ }
31
+
32
+ private write(level: string, message: string, data?: unknown): void {
33
+ const ts = new Date().toISOString()
34
+ const extra =
35
+ data !== undefined ? ` ${typeof data === "string" ? data : JSON.stringify(data)}` : ""
36
+ const line = `${ts} ${level} ${PREFIX} ${message}${extra}\n`
37
+ try {
38
+ if (!this.initialized) {
39
+ mkdirSync(dirname(this.logPath), { recursive: true })
40
+ this.initialized = true
41
+ }
42
+ appendFileSync(this.logPath, line, "utf8")
43
+ } catch {
44
+ // Best-effort logging — never crash the plugin.
45
+ }
20
46
  }
21
47
  }
package/lib/paths.ts CHANGED
@@ -29,3 +29,8 @@ export function stateDirectoryPath(): string {
29
29
  export function controlFilePath(): string {
30
30
  return join(stateDirectoryPath(), "control.json")
31
31
  }
32
+
33
+ /** Append-only log file for the server plugin — never stdout, which corrupts the TUI. */
34
+ export function logFilePath(): string {
35
+ return join(stateDirectoryPath(), "server.log")
36
+ }
package/lib/state.ts CHANGED
@@ -26,7 +26,7 @@ export type SessionKeepalive = {
26
26
  windowEndsAt: number
27
27
  /** Epoch ms the next ping is due. */
28
28
  nextPingAt: number
29
- /** Pings sent during the current idle stretch. */
29
+ /** Number of scheduled ping requests attempted during this warm window. */
30
30
  pingsSent: number
31
31
  /** A real turn is currently running. */
32
32
  busy: boolean
@@ -38,7 +38,11 @@ export function KeepaliveFooter(props: { theme: Theme; directory: string; sessio
38
38
  const match = readdirSync(stateDirectoryPath())
39
39
  .filter((name) => name.startsWith("state-") && name.endsWith(".json"))
40
40
  .map((name) => readState(join(stateDirectoryPath(), name)))
41
- .find((snapshot) => snapshot?.sessions?.[sessionID])
41
+ .filter(
42
+ (snapshot): snapshot is PersistedState =>
43
+ snapshot?.sessions?.[sessionID] !== undefined,
44
+ )
45
+ .sort((a, b) => b.updatedAt - a.updatedAt)[0]
42
46
  setState(match)
43
47
  } catch {
44
48
  setState(undefined)
@@ -63,8 +67,11 @@ export function KeepaliveFooter(props: { theme: Theme; directory: string; sessio
63
67
 
64
68
  const interval = session.intervalMs || snapshot.intervalMs || 270_000
65
69
  const idleMs = Math.max(0, now() - (session.lastResponseAt || now()))
70
+ // `nextPingAt` is the scheduler's source of truth and includes the
71
+ // per-ping jitter. Counting from it keeps the two counters in step.
72
+ const nextPingAt = Math.max(session.nextPingAt || now(), now())
66
73
  const left = session.active
67
- ? Math.max(0, Math.floor((session.windowEndsAt - now()) / interval))
74
+ ? Math.max(0, Math.ceil((session.windowEndsAt - nextPingAt) / interval))
68
75
  : 0
69
76
  const status: Status =
70
77
  !snapshot.enabled || !session.eligible
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@vikrant82/opencode-cache-keepalive",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "type": "module",
6
6
  "description": "OpenCode plugin that keeps provider prompt caches warm by sending invisible keepalive pings, with a live TUI readout",
7
7
  "main": "./dist/index.js",