@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/LICENSE +661 -0
- package/README.md +133 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/keepalive.d.ts.map +1 -1
- package/dist/lib/logger.d.ts +4 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/paths.d.ts +2 -0
- package/dist/lib/paths.d.ts.map +1 -1
- package/dist/lib/state.d.ts +1 -1
- package/dist/lib/state.d.ts.map +1 -1
- package/dist/lib/tui/footer.d.ts.map +1 -1
- package/lib/keepalive.ts +17 -5
- package/lib/logger.ts +31 -5
- package/lib/paths.ts +5 -0
- package/lib/state.ts +1 -1
- package/lib/tui/footer.tsx +9 -2
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
17
|
+
this.write("INFO", message, data)
|
|
8
18
|
}
|
|
9
19
|
|
|
10
20
|
warn(message: string, data?: unknown) {
|
|
11
|
-
|
|
21
|
+
this.write("WARN", message, data)
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
error(message: string, data?: unknown) {
|
|
15
|
-
|
|
25
|
+
this.write("ERROR", message, data)
|
|
16
26
|
}
|
|
17
27
|
|
|
18
28
|
dbg(message: string, data?: unknown) {
|
|
19
|
-
if (this.debug)
|
|
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
|
-
/**
|
|
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
|
package/lib/tui/footer.tsx
CHANGED
|
@@ -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
|
-
.
|
|
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.
|
|
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.
|
|
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",
|