opencode-discord 1.8.2 → 1.8.3
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/index.ts +66 -46
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -6,13 +6,15 @@ const RECONNECT_DELAY = 5_000
|
|
|
6
6
|
const MAX_RECONNECT_ATTEMPTS = 5
|
|
7
7
|
|
|
8
8
|
type Activity = "idle" | "thinking" | "editing" | "running" | "reading" | "approving" | "commanding"
|
|
9
|
-
type LogLevel = "debug" | "info" | "error" | "warn"
|
|
10
9
|
|
|
11
10
|
function basename(filePath?: string): string | undefined {
|
|
12
11
|
if (!filePath) return undefined
|
|
13
12
|
const normalized = filePath.replace(/\\/g, "/")
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
return normalized.split("/").pop() || undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function props(event: { properties?: Record<string, unknown> }) {
|
|
17
|
+
return (event.properties ?? {}) as Record<string, any>
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
@@ -26,26 +28,37 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
26
28
|
let sessionCount = 0
|
|
27
29
|
let filesChanged = 0
|
|
28
30
|
let lastCommand: string | undefined
|
|
31
|
+
let modelName: string | undefined
|
|
32
|
+
let sessionTokens = { input: 0, output: 0 }
|
|
29
33
|
let reconnectAttempts = 0
|
|
30
34
|
let reconnectTimer: ReturnType<typeof setTimeout> | undefined
|
|
31
35
|
let pendingPresence = false
|
|
36
|
+
let presenceDirty = false
|
|
32
37
|
let destroyed = false
|
|
33
38
|
|
|
34
|
-
function log(level:
|
|
35
|
-
sdk.app.log({
|
|
36
|
-
body: {
|
|
37
|
-
service: "discord-status",
|
|
38
|
-
level,
|
|
39
|
-
message,
|
|
40
|
-
extra,
|
|
41
|
-
},
|
|
42
|
-
}).catch(() => {})
|
|
39
|
+
function log(level: string, message: string, extra?: Record<string, unknown>) {
|
|
40
|
+
sdk.app.log({ body: { service: "discord-status", level, message, extra } }).catch(() => {})
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
function formatTokens(n: number): string {
|
|
44
|
+
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
|
|
45
|
+
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
|
|
46
|
+
return String(n)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resetSession() {
|
|
50
|
+
lastEditedFile = undefined
|
|
51
|
+
lastCommand = undefined
|
|
52
|
+
filesChanged = 0
|
|
53
|
+
sessionTokens = { input: 0, output: 0 }
|
|
54
|
+
}
|
|
46
55
|
|
|
47
56
|
function updatePresence() {
|
|
48
|
-
if (!connected
|
|
57
|
+
if (!connected) return
|
|
58
|
+
if (pendingPresence) {
|
|
59
|
+
presenceDirty = true
|
|
60
|
+
return
|
|
61
|
+
}
|
|
49
62
|
pendingPresence = true
|
|
50
63
|
|
|
51
64
|
let details: string
|
|
@@ -82,9 +95,12 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
82
95
|
details = "Session active"
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
const stateParts = [currentProject]
|
|
99
|
+
if (filesChanged > 0) stateParts.push(`${filesChanged} file${filesChanged !== 1 ? "s" : ""} changed`)
|
|
100
|
+
const totalTokens = sessionTokens.input + sessionTokens.output
|
|
101
|
+
if (totalTokens > 0) stateParts.push(`${formatTokens(totalTokens)} tokens`)
|
|
102
|
+
state = stateParts.join(" · ")
|
|
103
|
+
|
|
88
104
|
smallImageKey = activity === "thinking" ? "thinking" : activity === "approving" ? "idle" : "active"
|
|
89
105
|
smallImageText = activity === "thinking" ? "Generating" : activity === "approving" ? "Needs approval" : "Active"
|
|
90
106
|
}
|
|
@@ -94,13 +110,21 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
94
110
|
state,
|
|
95
111
|
startTimestamp,
|
|
96
112
|
largeImageKey: "opencode",
|
|
97
|
-
largeImageText:
|
|
113
|
+
largeImageText: modelName
|
|
114
|
+
? `OpenCode · ${modelName}`
|
|
115
|
+
: `OpenCode · ${sessionCount} session${sessionCount !== 1 ? "s" : ""}`,
|
|
98
116
|
smallImageKey,
|
|
99
117
|
smallImageText,
|
|
100
118
|
})
|
|
101
119
|
.then(() => log("debug", "Presence updated", { details, state }))
|
|
102
120
|
.catch((e) => log("error", "Failed to set presence", { error: String(e) }))
|
|
103
|
-
.finally(() => {
|
|
121
|
+
.finally(() => {
|
|
122
|
+
pendingPresence = false
|
|
123
|
+
if (presenceDirty) {
|
|
124
|
+
presenceDirty = false
|
|
125
|
+
updatePresence()
|
|
126
|
+
}
|
|
127
|
+
})
|
|
104
128
|
}
|
|
105
129
|
|
|
106
130
|
function scheduleReconnect() {
|
|
@@ -145,8 +169,8 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
145
169
|
rpc.destroy().catch(() => {})
|
|
146
170
|
})
|
|
147
171
|
|
|
172
|
+
log("info", "Logging in to Discord RPC...", { clientId: CLIENT_ID, project: currentProject })
|
|
148
173
|
try {
|
|
149
|
-
log("info", "Logging in to Discord RPC...")
|
|
150
174
|
await rpc.login()
|
|
151
175
|
reconnectAttempts = 0
|
|
152
176
|
log("info", "Discord RPC login successful")
|
|
@@ -163,9 +187,7 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
163
187
|
sessionActive = true
|
|
164
188
|
sessionCount += 1
|
|
165
189
|
startTimestamp = new Date()
|
|
166
|
-
|
|
167
|
-
lastCommand = undefined
|
|
168
|
-
filesChanged = 0
|
|
190
|
+
resetSession()
|
|
169
191
|
activity = "thinking"
|
|
170
192
|
break
|
|
171
193
|
case "session.updated":
|
|
@@ -173,7 +195,7 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
173
195
|
if (activity === "idle") activity = "thinking"
|
|
174
196
|
break
|
|
175
197
|
case "session.status": {
|
|
176
|
-
const status = (event
|
|
198
|
+
const status = props(event).status
|
|
177
199
|
if (status === "busy" || status === "retry") {
|
|
178
200
|
sessionActive = true
|
|
179
201
|
activity = "thinking"
|
|
@@ -191,29 +213,28 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
191
213
|
break
|
|
192
214
|
case "session.deleted":
|
|
193
215
|
sessionActive = false
|
|
194
|
-
|
|
195
|
-
lastCommand = undefined
|
|
196
|
-
filesChanged = 0
|
|
216
|
+
resetSession()
|
|
197
217
|
activity = "idle"
|
|
198
|
-
if (connected) {
|
|
199
|
-
rpc.user?.clearActivity().catch(() => {})
|
|
200
|
-
}
|
|
218
|
+
if (connected) rpc.user?.clearActivity().catch(() => {})
|
|
201
219
|
return
|
|
202
220
|
case "session.compacted":
|
|
203
221
|
activity = "thinking"
|
|
204
222
|
break
|
|
205
|
-
case "file.edited":
|
|
223
|
+
case "file.edited":
|
|
206
224
|
activity = "editing"
|
|
207
|
-
|
|
208
|
-
lastEditedFile = basename(editedFile) ?? lastEditedFile
|
|
225
|
+
lastEditedFile = basename(props(event).file) ?? lastEditedFile
|
|
209
226
|
break
|
|
210
|
-
}
|
|
211
227
|
case "session.diff": {
|
|
212
|
-
const diffs = (event
|
|
213
|
-
if (diffs) filesChanged = diffs.length
|
|
228
|
+
const diffs = props(event).diff
|
|
229
|
+
if (Array.isArray(diffs)) filesChanged = diffs.length
|
|
214
230
|
break
|
|
215
231
|
}
|
|
216
232
|
case "message.part.updated": {
|
|
233
|
+
const part = props(event).part
|
|
234
|
+
if (part?.type === "step-finish" && part.tokens) {
|
|
235
|
+
sessionTokens.input += part.tokens.input ?? 0
|
|
236
|
+
sessionTokens.output += part.tokens.output ?? 0
|
|
237
|
+
}
|
|
217
238
|
if (sessionActive && activity !== "editing" && activity !== "running") {
|
|
218
239
|
activity = "thinking"
|
|
219
240
|
}
|
|
@@ -231,17 +252,20 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
231
252
|
case "permission.replied":
|
|
232
253
|
activity = sessionActive ? "thinking" : "idle"
|
|
233
254
|
break
|
|
234
|
-
case "command.executed":
|
|
235
|
-
|
|
236
|
-
lastCommand = cmdName
|
|
255
|
+
case "command.executed":
|
|
256
|
+
lastCommand = props(event).name
|
|
237
257
|
activity = "commanding"
|
|
238
258
|
break
|
|
239
|
-
}
|
|
240
259
|
}
|
|
241
260
|
|
|
242
261
|
updatePresence()
|
|
243
262
|
},
|
|
244
263
|
|
|
264
|
+
"chat.params": async (input) => {
|
|
265
|
+
const name = input.model?.name ?? input.model?.id
|
|
266
|
+
if (name) modelName = name
|
|
267
|
+
},
|
|
268
|
+
|
|
245
269
|
"tool.execute.before": async (input, output) => {
|
|
246
270
|
const args = output.args as { filePath?: string } | undefined
|
|
247
271
|
|
|
@@ -259,15 +283,11 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
259
283
|
},
|
|
260
284
|
|
|
261
285
|
"tool.execute.after": async (input) => {
|
|
262
|
-
if (input.tool === "
|
|
263
|
-
activity = "editing"
|
|
264
|
-
const filePath = input.args?.filePath as string | undefined
|
|
265
|
-
lastEditedFile = basename(filePath) ?? lastEditedFile
|
|
266
|
-
} else if (input.tool === "bash") {
|
|
286
|
+
if (input.tool === "bash") {
|
|
267
287
|
activity = sessionActive ? "thinking" : "idle"
|
|
268
288
|
}
|
|
269
289
|
|
|
270
290
|
updatePresence()
|
|
271
291
|
},
|
|
272
292
|
}
|
|
273
|
-
}
|
|
293
|
+
}
|