@xiaoqiong0v0/opencode-plugin-logger 1.0.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.
Files changed (2) hide show
  1. package/logger.js +96 -0
  2. package/package.json +13 -0
package/logger.js ADDED
@@ -0,0 +1,96 @@
1
+ import { appendFileSync, mkdirSync, existsSync, readdirSync, unlinkSync, statSync, readFileSync, writeFileSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import { homedir } from "node:os"
4
+
5
+ const pad = (n, w = 2) => String(n).padStart(w, "0")
6
+
7
+ const GLOBAL_DIR = join(homedir(), ".config", "opencode")
8
+ const CONFIG_NAME = "plugin-logger.jsonc"
9
+ const CONFIG_PATH = join(GLOBAL_DIR, CONFIG_NAME)
10
+
11
+ const defaults = {
12
+ dir: join(homedir(), ".opencode", "plugins-log"),
13
+ timeFormat: "yyyy-MM-dd HH:mm:ss",
14
+ retentionDays: 7,
15
+ enabled: false,
16
+ }
17
+
18
+ const SAMPLE_CFG = `{
19
+ // 是否启用日志输出(默认 false)
20
+ "enabled": false,
21
+ // 日志文件输出目录
22
+ "dir": "${defaults.dir.replace(/\\/g, "/")}",
23
+ // 时间格式,SSS 为毫秒
24
+ "timeFormat": "${defaults.timeFormat}.SSS",
25
+ // 日志保留天数,0 为永不过期
26
+ "retentionDays": ${defaults.retentionDays}
27
+ }
28
+ `
29
+
30
+ function readJsonc(path) {
31
+ try {
32
+ const raw = readFileSync(path, "utf-8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "")
33
+ return JSON.parse(raw)
34
+ } catch { return {} }
35
+ }
36
+
37
+ function resolveCfg(opts) {
38
+ if (!existsSync(CONFIG_PATH)) {
39
+ try { writeFileSync(CONFIG_PATH, SAMPLE_CFG, "utf-8") } catch {}
40
+ }
41
+ const cfg = existsSync(CONFIG_PATH) ? readJsonc(CONFIG_PATH) : {}
42
+ return { ...defaults, ...cfg, ...(opts || {}) }
43
+ }
44
+
45
+ export default function createLogger(name, opts) {
46
+ const cfg = resolveCfg(opts)
47
+ if (!cfg.enabled) return { loaded() {}, info() {}, error() {}, hook() {}, tool() {} }
48
+
49
+ const logDir = cfg.dir
50
+ if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true })
51
+
52
+ const formatTime = (ts) => {
53
+ const d = new Date(ts)
54
+ let s = cfg.timeFormat
55
+ s = s.replace("yyyy", d.getFullYear())
56
+ s = s.replace("MM", pad(d.getMonth() + 1))
57
+ s = s.replace("dd", pad(d.getDate()))
58
+ s = s.replace("HH", pad(d.getHours()))
59
+ s = s.replace("mm", pad(d.getMinutes()))
60
+ s = s.replace("ss", pad(d.getSeconds()))
61
+ s = s.replace("SSS", pad(d.getMilliseconds(), 3))
62
+ return s
63
+ }
64
+
65
+ const cleanOld = () => {
66
+ if (!cfg.retentionDays) return
67
+ const maxAge = cfg.retentionDays * 86400000
68
+ const now = Date.now()
69
+ try {
70
+ for (const f of readdirSync(logDir)) {
71
+ if (!f.match(/^\d{4}-\d{2}-\d{2}\.log$/)) continue
72
+ try { if (now - statSync(join(logDir, f)).mtimeMs > maxAge) unlinkSync(join(logDir, f)) } catch {}
73
+ }
74
+ } catch {}
75
+ }
76
+
77
+ let currentDay = formatTime(Date.now()).slice(0, 10)
78
+ let logFile = join(logDir, currentDay.replace(/-/g, "") + ".log")
79
+
80
+ const write = (level, msg) => {
81
+ const now = Date.now()
82
+ const day = formatTime(now).slice(0, 10)
83
+ if (day !== currentDay) { currentDay = day; logFile = join(logDir, day.replace(/-/g, "") + ".log"); cleanOld() }
84
+ try { appendFileSync(logFile, `[${formatTime(now)}] [${level}] ${name} ${msg}\n`) } catch {}
85
+ }
86
+
87
+ cleanOld()
88
+
89
+ return {
90
+ loaded: () => write("INFO", "loaded"),
91
+ info: (msg) => write("INFO", msg),
92
+ error: (msg, err) => write("ERROR", err ? `${msg} — ${err.message || err}` : msg),
93
+ hook: (h, d) => write("HOOK", `${h}${d ? " → " + d : ""}`),
94
+ tool: (t, a) => write("TOOL", `${t}(${JSON.stringify(a).slice(0, 200)})`),
95
+ }
96
+ }
package/package.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "@xiaoqiong0v0/opencode-plugin-logger",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "description": "Configurable file-based logger for OpenCode plugins",
6
+ "exports": {
7
+ ".": "./logger.js"
8
+ },
9
+ "files": [
10
+ "logger.js"
11
+ ],
12
+ "license": "MIT"
13
+ }