opencode-glm-quota 1.3.3 → 1.4.0
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/README.md +9 -3
- package/bin/install.js +35 -25
- package/dist/index.js +12 -4
- package/dist/utils/progress-bar.js +2 -2
- package/integration/agents/glm-quota-exec.md +20 -0
- package/package.json +1 -1
- package/integration/opencode.jsonc +0 -10
package/README.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/opencode-glm-quota)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://github.com/guyinwonder168/opencode-glm-quota/actions)
|
|
6
|
+
[](https://sonarcloud.io/summary/new_code?id=guyinwonder168_opencode-glm-quota)
|
|
7
|
+
---
|
|
8
|
+
[](https://sonarcloud.io/summary/new_code?id=guyinwonder168_opencode-glm-quota)
|
|
9
|
+
[](https://sonarcloud.io/summary/new_code?id=guyinwonder168_opencode-glm-quota)
|
|
10
|
+
[](https://sonarcloud.io/summary/new_code?id=guyinwonder168_opencode-glm-quota)
|
|
11
|
+
[](https://sonarcloud.io/summary/new_code?id=guyinwonder168_opencode-glm-quota)
|
|
6
12
|
|
|
7
13
|
OpenCode plugin to query Z.ai GLM Coding Plan usage statistics with real-time quota monitoring, model usage tracking, and MCP tool usage.
|
|
8
14
|
|
|
@@ -32,7 +38,7 @@ npx opencode-glm-quota install
|
|
|
32
38
|
- Copies `/glm_quota` command to `~/.config/opencode/command/glm_quota.md`
|
|
33
39
|
- Copies skill documentation to `~/.config/opencode/skills/glm-quota/SKILL.md`
|
|
34
40
|
- Automatically adds plugin to your OpenCode config
|
|
35
|
-
-
|
|
41
|
+
- Copies agent to `~/.config/opencode/agents/`
|
|
36
42
|
- Supports `--force` flag to overwrite existing files
|
|
37
43
|
|
|
38
44
|
### Uninstall
|
|
@@ -49,7 +55,7 @@ npx opencode-glm-quota uninstall --global
|
|
|
49
55
|
- Removes `/glm_quota` command
|
|
50
56
|
- Deletes `skills/glm-quota/SKILL.md`
|
|
51
57
|
- Removes plugin entry from OpenCode config
|
|
52
|
-
- Removes
|
|
58
|
+
- Removes agent file and legacy config
|
|
53
59
|
- Runs `npm remove opencode-glm-quota` (or `--global`)
|
|
54
60
|
|
|
55
61
|
### Option 2: From GitHub
|
|
@@ -252,9 +258,9 @@ src/
|
|
|
252
258
|
progress-bar.ts # ASCII progress bar rendering
|
|
253
259
|
time-window.ts # Rolling window calculation
|
|
254
260
|
integration/
|
|
261
|
+
agents/glm-quota-exec.md # Minimal executor agent (Markdown)
|
|
255
262
|
command/glm_quota.md # /glm_quota slash command
|
|
256
263
|
skills/glm-quota/SKILL.md # Skill documentation
|
|
257
|
-
opencode.jsonc # Agent configuration (JSONC)
|
|
258
264
|
bin/
|
|
259
265
|
install.js # Installation script
|
|
260
266
|
dist/ # Compiled JavaScript (generated)
|
package/bin/install.js
CHANGED
|
@@ -27,11 +27,12 @@ const __dirname = path.dirname(__filename)
|
|
|
27
27
|
const SOURCE_DIR = path.join(__dirname, '..', 'integration')
|
|
28
28
|
const COMMAND_FILE = path.join(SOURCE_DIR, 'command', 'glm_quota.md')
|
|
29
29
|
const SKILL_FILE = path.join(SOURCE_DIR, 'skills', 'glm-quota', 'SKILL.md')
|
|
30
|
-
const
|
|
30
|
+
const AGENT_FILE = path.join(SOURCE_DIR, 'agents', 'glm-quota-exec.md')
|
|
31
31
|
|
|
32
32
|
const CONFIG_DIR = path.join(os.homedir(), '.config', 'opencode')
|
|
33
33
|
const TARGET_COMMAND = path.join(CONFIG_DIR, 'command', 'glm_quota.md')
|
|
34
34
|
const TARGET_SKILL = path.join(CONFIG_DIR, 'skills', 'glm-quota', 'SKILL.md')
|
|
35
|
+
const TARGET_AGENT = path.join(CONFIG_DIR, 'agents', 'glm-quota-exec.md')
|
|
35
36
|
|
|
36
37
|
// Check which config file exists (opencode.json or opencode.jsonc)
|
|
37
38
|
const TARGET_CONFIG_JSON = path.join(CONFIG_DIR, 'opencode.json')
|
|
@@ -181,25 +182,42 @@ function installSkill(force) {
|
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
/**
|
|
184
|
-
*
|
|
185
|
+
* Install agent file
|
|
185
186
|
*/
|
|
186
|
-
function
|
|
187
|
-
|
|
187
|
+
function installAgent(force) {
|
|
188
|
+
if (fileExists(TARGET_AGENT) && !force) {
|
|
189
|
+
if (!promptConfirm(`Agent file exists: ${TARGET_AGENT}\nOverwrite?`)) {
|
|
190
|
+
console.log(` ⊘ Skipped ${TARGET_AGENT}`)
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
copyFile(AGENT_FILE, TARGET_AGENT)
|
|
196
|
+
console.log(` ✓ Created ${TARGET_AGENT}`)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Update plugin configuration and cleanup old JSON agent
|
|
201
|
+
*/
|
|
202
|
+
function updatePluginConfig() {
|
|
203
|
+
// Parse existing config if it exists
|
|
188
204
|
let existingConfig = {}
|
|
189
205
|
if (fileExists(TARGET_CONFIG)) {
|
|
190
206
|
existingConfig = parseConfig(TARGET_CONFIG)
|
|
191
|
-
// REMOVE old 'options' field to prevent verbose agent output
|
|
192
|
-
if (existingConfig.agent?.['glm-quota-exec']?.options) {
|
|
193
|
-
delete existingConfig.agent['glm-quota-exec'].options
|
|
194
|
-
}
|
|
195
207
|
}
|
|
196
208
|
|
|
197
|
-
// Parse new agent config from integration
|
|
198
|
-
const newConfig = parseConfig(AGENT_CONFIG)
|
|
199
|
-
|
|
200
209
|
const PLUGIN_NAME = 'opencode-glm-quota'
|
|
201
210
|
|
|
202
|
-
//
|
|
211
|
+
// CLEANUP: Remove old JSON agent config if it exists (migration from v1.3.x)
|
|
212
|
+
if (existingConfig.agent && existingConfig.agent['glm-quota-exec']) {
|
|
213
|
+
delete existingConfig.agent['glm-quota-exec']
|
|
214
|
+
if (Object.keys(existingConfig.agent).length === 0) {
|
|
215
|
+
delete existingConfig.agent
|
|
216
|
+
}
|
|
217
|
+
console.log(' ✓ Removed old JSON agent config (migrated to Markdown)')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Handle both "plugin" array and "plugins" array
|
|
203
221
|
// Check for "plugin" array first (user's config uses this)
|
|
204
222
|
const pluginArrayName = existingConfig.plugin ? 'plugin' : 'plugins'
|
|
205
223
|
|
|
@@ -210,16 +228,6 @@ function mergeConfig() {
|
|
|
210
228
|
|
|
211
229
|
const plugins = Array.isArray(existingConfig[pluginArrayName]) ? existingConfig[pluginArrayName] : []
|
|
212
230
|
|
|
213
|
-
// REPLACE entire glm-quota-exec agent (not merge, to remove old redundant fields)
|
|
214
|
-
if (newConfig.agent && newConfig.agent['glm-quota-exec']) {
|
|
215
|
-
if (!existingConfig.agent) {
|
|
216
|
-
existingConfig.agent = {}
|
|
217
|
-
}
|
|
218
|
-
existingConfig.agent['glm-quota-exec'] = newConfig.agent['glm-quota-exec']
|
|
219
|
-
} else if (!existingConfig.agent && newConfig.agent) {
|
|
220
|
-
existingConfig.agent = newConfig.agent
|
|
221
|
-
}
|
|
222
|
-
|
|
223
231
|
// Only add if not already present
|
|
224
232
|
if (!plugins.includes(PLUGIN_NAME)) {
|
|
225
233
|
plugins.push(PLUGIN_NAME)
|
|
@@ -229,9 +237,9 @@ function mergeConfig() {
|
|
|
229
237
|
console.log(` ⊙ Plugin ${PLUGIN_NAME} already in ${pluginArrayName} array`)
|
|
230
238
|
}
|
|
231
239
|
|
|
232
|
-
// Write
|
|
240
|
+
// Write config back to same file (opencode.json or opencode.jsonc)
|
|
233
241
|
writeConfig(TARGET_CONFIG, existingConfig)
|
|
234
|
-
console.log(` ✓
|
|
242
|
+
console.log(` ✓ Updated ${path.basename(TARGET_CONFIG)}`)
|
|
235
243
|
}
|
|
236
244
|
|
|
237
245
|
/**
|
|
@@ -303,6 +311,7 @@ function removePackage(globalFlag) {
|
|
|
303
311
|
function uninstall(globalFlag) {
|
|
304
312
|
removeFile(TARGET_COMMAND, TARGET_COMMAND)
|
|
305
313
|
removeDirectory(path.dirname(TARGET_SKILL), path.dirname(TARGET_SKILL))
|
|
314
|
+
removeFile(TARGET_AGENT, TARGET_AGENT)
|
|
306
315
|
removeConfig()
|
|
307
316
|
removePackage(globalFlag)
|
|
308
317
|
}
|
|
@@ -335,7 +344,8 @@ function main() {
|
|
|
335
344
|
// Install integration files
|
|
336
345
|
installCommand(forceFlag)
|
|
337
346
|
installSkill(forceFlag)
|
|
338
|
-
|
|
347
|
+
installAgent(forceFlag)
|
|
348
|
+
updatePluginConfig()
|
|
339
349
|
|
|
340
350
|
console.log()
|
|
341
351
|
console.log('✓ Installation complete!')
|
package/dist/index.js
CHANGED
|
@@ -249,10 +249,17 @@ function formatToolUsage(data, quotaData) {
|
|
|
249
249
|
* @returns Formatted line with box characters
|
|
250
250
|
*/
|
|
251
251
|
function formatBoxLine(content, lineIndent) {
|
|
252
|
-
const trimmed = trimToDisplayWidth(content, lineIndent);
|
|
252
|
+
const trimmed = trimToDisplayWidth(content, lineIndent, 0);
|
|
253
253
|
const padding = Math.max(lineIndent - getDisplayWidth(trimmed), 0);
|
|
254
254
|
return '║ ' + trimmed + ' '.repeat(padding) + '║';
|
|
255
255
|
}
|
|
256
|
+
function formatProgressBoxLine(content, lineIndent) {
|
|
257
|
+
const gap = 2;
|
|
258
|
+
const contentWidth = Math.max(lineIndent - gap, 0);
|
|
259
|
+
const trimmed = trimToDisplayWidth(content, contentWidth, 0);
|
|
260
|
+
const padding = Math.max(contentWidth - getDisplayWidth(trimmed), 0);
|
|
261
|
+
return '║ ' + trimmed + ' '.repeat(padding + gap) + '║';
|
|
262
|
+
}
|
|
256
263
|
function getDisplayWidth(text) {
|
|
257
264
|
let width = 0;
|
|
258
265
|
for (let i = 0; i < text.length; i += 1) {
|
|
@@ -270,9 +277,10 @@ function getDisplayWidth(text) {
|
|
|
270
277
|
}
|
|
271
278
|
return width;
|
|
272
279
|
}
|
|
273
|
-
function trimToDisplayWidth(text, maxWidth) {
|
|
280
|
+
function trimToDisplayWidth(text, maxWidth, reserveRightPadding) {
|
|
274
281
|
let width = 0;
|
|
275
282
|
let result = '';
|
|
283
|
+
const allowedWidth = Math.max(maxWidth - reserveRightPadding, 0);
|
|
276
284
|
for (let i = 0; i < text.length; i += 1) {
|
|
277
285
|
const codePoint = text.codePointAt(i);
|
|
278
286
|
if (codePoint === undefined) {
|
|
@@ -285,7 +293,7 @@ function trimToDisplayWidth(text, maxWidth) {
|
|
|
285
293
|
continue;
|
|
286
294
|
}
|
|
287
295
|
const charWidth = isEmojiCodePoint(codePoint) || isFullWidthCodePoint(codePoint) ? 2 : 1;
|
|
288
|
-
if (width + charWidth >
|
|
296
|
+
if (width + charWidth > allowedWidth) {
|
|
289
297
|
break;
|
|
290
298
|
}
|
|
291
299
|
result += String.fromCodePoint(codePoint);
|
|
@@ -368,7 +376,7 @@ function formatQuotaLimits(quotaData) {
|
|
|
368
376
|
for (const limit of limits) {
|
|
369
377
|
const pct = typeof limit.percentage === 'number' ? limit.percentage : 0;
|
|
370
378
|
const line = formatProgressLine(limit.type || 'Unknown', pct);
|
|
371
|
-
lines.push(
|
|
379
|
+
lines.push(formatProgressBoxLine(line, LINE_INDENT));
|
|
372
380
|
if (limit.nextResetTime !== undefined) {
|
|
373
381
|
const resetMsg = formatTimeUntilReset(limit.nextResetTime);
|
|
374
382
|
if (resetMsg) {
|
|
@@ -34,8 +34,8 @@ export function formatPercentage(percentage, decimals = 1) {
|
|
|
34
34
|
* @returns Formatted progress bar line
|
|
35
35
|
*/
|
|
36
36
|
export function formatProgressLine(label, percentage) {
|
|
37
|
-
const bar = createProgressBar(percentage, { width:
|
|
37
|
+
const bar = createProgressBar(percentage, { width: 12 });
|
|
38
38
|
const pctStr = formatPercentage(percentage).padStart(6);
|
|
39
39
|
const labelStr = label.slice(0, 20).padEnd(20);
|
|
40
|
-
return `${labelStr}
|
|
40
|
+
return `${labelStr} [${bar}] ${pctStr}`;
|
|
41
41
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Minimal executor for GLM quota tool
|
|
3
|
+
mode: subagent
|
|
4
|
+
hidden: true
|
|
5
|
+
permission:
|
|
6
|
+
edit: deny
|
|
7
|
+
bash: deny
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
CRITICAL INSTRUCTION: When the glm_quota tool returns output, you MUST return it EXACTLY as received, character-for-character, with NO modifications.
|
|
11
|
+
|
|
12
|
+
- Do NOT add introductory text
|
|
13
|
+
- Do NOT summarize
|
|
14
|
+
- Do NOT reformat
|
|
15
|
+
- Do NOT explain anything
|
|
16
|
+
- Do NOT add any commentary
|
|
17
|
+
- Return ONLY the raw string from the tool
|
|
18
|
+
|
|
19
|
+
Example of WRONG behavior: 'Here is your usage: [summary]'
|
|
20
|
+
Example of CORRECT behavior: [paste entire tool output verbatim]
|
package/package.json
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://opencode.ai/config.json",
|
|
3
|
-
"agent": {
|
|
4
|
-
"glm-quota-exec": {
|
|
5
|
-
"mode": "subagent",
|
|
6
|
-
"system": "CRITICAL INSTRUCTION: When the glm_quota tool returns output, you MUST return it EXACTLY as received, character-for-character, with NO modifications.\n\n- Do NOT add introductory text\n- Do NOT summarize\n- Do NOT reformat\n- Do NOT explain anything\n- Do NOT add any commentary\n- Return ONLY the raw string from the tool\n\nExample of WRONG behavior: 'Here is your usage: [summary]'\nExample of CORRECT behavior: [paste entire tool output verbatim]",
|
|
7
|
-
"permission": {}
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
}
|