opencode-glm-quota 1.3.1 → 1.3.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/README.md +17 -0
- package/bin/install.js +109 -0
- package/dist/index.js +101 -21
- package/integration/command/glm_quota.md +1 -1
- package/integration/opencode.jsonc +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,6 +35,23 @@ npx opencode-glm-quota install
|
|
|
35
35
|
- Merges agent configuration into `~/.config/opencode/opencode.json`
|
|
36
36
|
- Supports `--force` flag to overwrite existing files
|
|
37
37
|
|
|
38
|
+
### Uninstall
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Remove OpenCode integration files and config
|
|
42
|
+
npx opencode-glm-quota uninstall
|
|
43
|
+
|
|
44
|
+
# If installed globally
|
|
45
|
+
npx opencode-glm-quota uninstall --global
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**What the uninstaller does:**
|
|
49
|
+
- Removes `/glm_quota` command
|
|
50
|
+
- Deletes `skills/glm-quota/SKILL.md`
|
|
51
|
+
- Removes plugin entry from OpenCode config
|
|
52
|
+
- Removes `glm-quota-exec` agent config
|
|
53
|
+
- Runs `npm remove opencode-glm-quota` (or `--global`)
|
|
54
|
+
|
|
38
55
|
### Option 2: From GitHub
|
|
39
56
|
|
|
40
57
|
```bash
|
package/bin/install.js
CHANGED
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
* Usage:
|
|
10
10
|
* node bin/install.js # Interactive install (ask before overwriting)
|
|
11
11
|
* node bin/install.js --force # Force overwrite existing files
|
|
12
|
+
* node bin/install.js uninstall # Remove integration files and config
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import * as fs from 'fs'
|
|
15
16
|
import * as path from 'path'
|
|
16
17
|
import * as os from 'os'
|
|
18
|
+
import { spawnSync } from 'child_process'
|
|
17
19
|
import { parse as parseJsonc } from 'jsonc-parser'
|
|
18
20
|
|
|
19
21
|
// ==========================================
|
|
@@ -72,6 +74,30 @@ function copyFile(source, destination) {
|
|
|
72
74
|
fs.copyFileSync(source, destination)
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Remove file if it exists
|
|
79
|
+
*/
|
|
80
|
+
function removeFile(filePath, label) {
|
|
81
|
+
if (fileExists(filePath)) {
|
|
82
|
+
fs.unlinkSync(filePath)
|
|
83
|
+
console.log(` ✓ Removed ${label}`)
|
|
84
|
+
} else {
|
|
85
|
+
console.log(` ⊙ Not found ${label}`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Remove directory if it exists
|
|
91
|
+
*/
|
|
92
|
+
function removeDirectory(dirPath, label) {
|
|
93
|
+
if (fileExists(dirPath)) {
|
|
94
|
+
fs.rmSync(dirPath, { recursive: true, force: true })
|
|
95
|
+
console.log(` ✓ Removed ${label}`)
|
|
96
|
+
} else {
|
|
97
|
+
console.log(` ⊙ Not found ${label}`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
75
101
|
/**
|
|
76
102
|
* Parse JSON or JSONC file
|
|
77
103
|
*/
|
|
@@ -208,6 +234,79 @@ function mergeConfig() {
|
|
|
208
234
|
console.log(` ✓ Merged configuration into ${path.basename(TARGET_CONFIG)}`)
|
|
209
235
|
}
|
|
210
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Remove plugin configuration and agent configuration
|
|
239
|
+
*/
|
|
240
|
+
function removeConfig() {
|
|
241
|
+
if (!fileExists(TARGET_CONFIG)) {
|
|
242
|
+
console.log(` ⊙ Config not found: ${TARGET_CONFIG}`)
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const PLUGIN_NAME = 'opencode-glm-quota'
|
|
247
|
+
const existingConfig = parseConfig(TARGET_CONFIG)
|
|
248
|
+
let changed = false
|
|
249
|
+
|
|
250
|
+
if (Array.isArray(existingConfig.plugin)) {
|
|
251
|
+
const next = existingConfig.plugin.filter((name) => name !== PLUGIN_NAME)
|
|
252
|
+
if (next.length !== existingConfig.plugin.length) {
|
|
253
|
+
existingConfig.plugin = next
|
|
254
|
+
changed = true
|
|
255
|
+
console.log(' ✓ Removed plugin from plugin array')
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (Array.isArray(existingConfig.plugins)) {
|
|
260
|
+
const next = existingConfig.plugins.filter((name) => name !== PLUGIN_NAME)
|
|
261
|
+
if (next.length !== existingConfig.plugins.length) {
|
|
262
|
+
existingConfig.plugins = next
|
|
263
|
+
changed = true
|
|
264
|
+
console.log(' ✓ Removed plugin from plugins array')
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (existingConfig.agent && existingConfig.agent['glm-quota-exec']) {
|
|
269
|
+
delete existingConfig.agent['glm-quota-exec']
|
|
270
|
+
if (Object.keys(existingConfig.agent).length === 0) {
|
|
271
|
+
delete existingConfig.agent
|
|
272
|
+
}
|
|
273
|
+
changed = true
|
|
274
|
+
console.log(' ✓ Removed glm-quota-exec agent config')
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (changed) {
|
|
278
|
+
writeConfig(TARGET_CONFIG, existingConfig)
|
|
279
|
+
console.log(` ✓ Updated ${path.basename(TARGET_CONFIG)}`)
|
|
280
|
+
} else {
|
|
281
|
+
console.log(' ⊙ No config changes needed')
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Remove npm package
|
|
287
|
+
*/
|
|
288
|
+
function removePackage(globalFlag) {
|
|
289
|
+
const args = ['remove', 'opencode-glm-quota']
|
|
290
|
+
if (globalFlag) {
|
|
291
|
+
args.push('--global')
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const result = spawnSync('npm', args, { stdio: 'inherit' })
|
|
295
|
+
if (result.status !== 0) {
|
|
296
|
+
console.log(' ⊙ npm remove failed, remove manually if needed')
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Uninstall integration files and configuration
|
|
302
|
+
*/
|
|
303
|
+
function uninstall(globalFlag) {
|
|
304
|
+
removeFile(TARGET_COMMAND, TARGET_COMMAND)
|
|
305
|
+
removeDirectory(path.dirname(TARGET_SKILL), path.dirname(TARGET_SKILL))
|
|
306
|
+
removeConfig()
|
|
307
|
+
removePackage(globalFlag)
|
|
308
|
+
}
|
|
309
|
+
|
|
211
310
|
// ==========================================
|
|
212
311
|
// MAIN INSTALLATION FUNCTION
|
|
213
312
|
// ==========================================
|
|
@@ -219,7 +318,17 @@ function main() {
|
|
|
219
318
|
try {
|
|
220
319
|
// Parse command line arguments
|
|
221
320
|
const args = process.argv.slice(2)
|
|
321
|
+
const isUninstall = args.includes('uninstall')
|
|
222
322
|
const forceFlag = args.includes('--force')
|
|
323
|
+
const globalFlag = args.includes('--global') || args.includes('-g')
|
|
324
|
+
|
|
325
|
+
if (isUninstall) {
|
|
326
|
+
console.log('✓ Uninstalling GLM Quota Plugin...\n')
|
|
327
|
+
uninstall(globalFlag)
|
|
328
|
+
console.log()
|
|
329
|
+
console.log('✓ Uninstall complete!')
|
|
330
|
+
return
|
|
331
|
+
}
|
|
223
332
|
|
|
224
333
|
console.log('✓ Installing GLM Quota Plugin...\n')
|
|
225
334
|
|
package/dist/index.js
CHANGED
|
@@ -249,7 +249,87 @@ function formatToolUsage(data, quotaData) {
|
|
|
249
249
|
* @returns Formatted line with box characters
|
|
250
250
|
*/
|
|
251
251
|
function formatBoxLine(content, lineIndent) {
|
|
252
|
-
|
|
252
|
+
const trimmed = trimToDisplayWidth(content, lineIndent);
|
|
253
|
+
const padding = Math.max(lineIndent - getDisplayWidth(trimmed), 0);
|
|
254
|
+
return '║ ' + trimmed + ' '.repeat(padding) + '║';
|
|
255
|
+
}
|
|
256
|
+
function getDisplayWidth(text) {
|
|
257
|
+
let width = 0;
|
|
258
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
259
|
+
const codePoint = text.codePointAt(i);
|
|
260
|
+
if (codePoint === undefined) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (codePoint > 0xffff) {
|
|
264
|
+
i += 1;
|
|
265
|
+
}
|
|
266
|
+
if (isControlCodePoint(codePoint) || isZeroWidthCodePoint(codePoint)) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
width += isEmojiCodePoint(codePoint) || isFullWidthCodePoint(codePoint) ? 2 : 1;
|
|
270
|
+
}
|
|
271
|
+
return width;
|
|
272
|
+
}
|
|
273
|
+
function trimToDisplayWidth(text, maxWidth) {
|
|
274
|
+
let width = 0;
|
|
275
|
+
let result = '';
|
|
276
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
277
|
+
const codePoint = text.codePointAt(i);
|
|
278
|
+
if (codePoint === undefined) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (codePoint > 0xffff) {
|
|
282
|
+
i += 1;
|
|
283
|
+
}
|
|
284
|
+
if (isControlCodePoint(codePoint) || isZeroWidthCodePoint(codePoint)) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const charWidth = isEmojiCodePoint(codePoint) || isFullWidthCodePoint(codePoint) ? 2 : 1;
|
|
288
|
+
if (width + charWidth > maxWidth) {
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
result += String.fromCodePoint(codePoint);
|
|
292
|
+
width += charWidth;
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
function isControlCodePoint(codePoint) {
|
|
297
|
+
return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);
|
|
298
|
+
}
|
|
299
|
+
function isZeroWidthCodePoint(codePoint) {
|
|
300
|
+
return (codePoint === 0x200d ||
|
|
301
|
+
codePoint === 0xfe0f ||
|
|
302
|
+
(codePoint >= 0xfe00 && codePoint <= 0xfe0f));
|
|
303
|
+
}
|
|
304
|
+
function isEmojiCodePoint(codePoint) {
|
|
305
|
+
return ((codePoint >= 0x1f300 && codePoint <= 0x1f5ff) ||
|
|
306
|
+
(codePoint >= 0x1f600 && codePoint <= 0x1f64f) ||
|
|
307
|
+
(codePoint >= 0x1f680 && codePoint <= 0x1f6ff) ||
|
|
308
|
+
(codePoint >= 0x1f700 && codePoint <= 0x1f77f) ||
|
|
309
|
+
(codePoint >= 0x1f780 && codePoint <= 0x1f7ff) ||
|
|
310
|
+
(codePoint >= 0x1f800 && codePoint <= 0x1f8ff) ||
|
|
311
|
+
(codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
|
|
312
|
+
(codePoint >= 0x1fa00 && codePoint <= 0x1faff) ||
|
|
313
|
+
(codePoint >= 0x2600 && codePoint <= 0x26ff) ||
|
|
314
|
+
(codePoint >= 0x2700 && codePoint <= 0x27bf));
|
|
315
|
+
}
|
|
316
|
+
function isFullWidthCodePoint(codePoint) {
|
|
317
|
+
return (codePoint >= 0x1100 && (codePoint <= 0x115f ||
|
|
318
|
+
codePoint === 0x2329 ||
|
|
319
|
+
codePoint === 0x232a ||
|
|
320
|
+
(codePoint >= 0x2e80 && codePoint <= 0x3247 && codePoint !== 0x303f) ||
|
|
321
|
+
(codePoint >= 0x3250 && codePoint <= 0x4dbf) ||
|
|
322
|
+
(codePoint >= 0x4e00 && codePoint <= 0xa4c6) ||
|
|
323
|
+
(codePoint >= 0xa960 && codePoint <= 0xa97c) ||
|
|
324
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
325
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
326
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
327
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6b) ||
|
|
328
|
+
(codePoint >= 0xff01 && codePoint <= 0xff60) ||
|
|
329
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
|
330
|
+
(codePoint >= 0x1b000 && codePoint <= 0x1b001) ||
|
|
331
|
+
(codePoint >= 0x1f200 && codePoint <= 0x1f251) ||
|
|
332
|
+
(codePoint >= 0x20000 && codePoint <= 0x3fffd)));
|
|
253
333
|
}
|
|
254
334
|
/**
|
|
255
335
|
* Format header section
|
|
@@ -260,16 +340,16 @@ function formatBoxLine(content, lineIndent) {
|
|
|
260
340
|
*/
|
|
261
341
|
function formatHeader(platformName, startTime, endTime) {
|
|
262
342
|
const lines = [];
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
lines.push('
|
|
266
|
-
lines.push('║' + '
|
|
267
|
-
lines.push('║' + ' '.
|
|
268
|
-
lines.push('
|
|
269
|
-
lines.push('
|
|
270
|
-
|
|
271
|
-
lines.push(
|
|
272
|
-
lines.push('╠' + '═'.repeat(
|
|
343
|
+
const LINE_CONTENT = 58;
|
|
344
|
+
const LINE_INDENT = 56;
|
|
345
|
+
lines.push('╔' + '═'.repeat(LINE_CONTENT) + '╗');
|
|
346
|
+
lines.push('║' + ' '.repeat(LINE_CONTENT) + '║');
|
|
347
|
+
lines.push('║' + ' Z.ai GLM Coding Plan Usage Statistics '.padStart(35).padEnd(LINE_CONTENT) + '║');
|
|
348
|
+
lines.push('║' + ' '.repeat(LINE_CONTENT) + '║');
|
|
349
|
+
lines.push('╠' + '═'.repeat(LINE_CONTENT) + '╣');
|
|
350
|
+
lines.push(formatBoxLine(`Platform: ${platformName}`, LINE_INDENT));
|
|
351
|
+
lines.push(formatBoxLine(`Period: ${startTime} → ${endTime}`, LINE_INDENT));
|
|
352
|
+
lines.push('╠' + '═'.repeat(LINE_CONTENT) + '╣');
|
|
273
353
|
return lines;
|
|
274
354
|
}
|
|
275
355
|
/**
|
|
@@ -281,8 +361,8 @@ function formatQuotaLimits(quotaData) {
|
|
|
281
361
|
const lines = [];
|
|
282
362
|
const LINE_CONTENT = 58;
|
|
283
363
|
const LINE_INDENT = 56;
|
|
284
|
-
lines.push('
|
|
285
|
-
lines.push('╟' + '─'.repeat(
|
|
364
|
+
lines.push(formatBoxLine('QUOTA LIMITS', LINE_INDENT));
|
|
365
|
+
lines.push('╟' + '─'.repeat(LINE_CONTENT) + '╢');
|
|
286
366
|
const limits = quotaData?.limits;
|
|
287
367
|
if (limits && Array.isArray(limits)) {
|
|
288
368
|
for (const limit of limits) {
|
|
@@ -302,9 +382,9 @@ function formatQuotaLimits(quotaData) {
|
|
|
302
382
|
}
|
|
303
383
|
}
|
|
304
384
|
else {
|
|
305
|
-
lines.push('
|
|
385
|
+
lines.push(formatBoxLine('No quota data available', LINE_INDENT));
|
|
306
386
|
}
|
|
307
|
-
lines.push('╠' + '═'.repeat(
|
|
387
|
+
lines.push('╠' + '═'.repeat(LINE_CONTENT) + '╣');
|
|
308
388
|
return lines;
|
|
309
389
|
}
|
|
310
390
|
/**
|
|
@@ -319,8 +399,8 @@ function formatQuotaLimits(quotaData) {
|
|
|
319
399
|
function formatDataSection(title, data, formatter, quotaData, noDataMessage, LINE_INDENT) {
|
|
320
400
|
const lines = [];
|
|
321
401
|
const LINE_CONTENT = 58;
|
|
322
|
-
lines.push(
|
|
323
|
-
lines.push('╟' + '─'.repeat(
|
|
402
|
+
lines.push(formatBoxLine(title, LINE_INDENT));
|
|
403
|
+
lines.push('╟' + '─'.repeat(LINE_CONTENT) + '╢');
|
|
324
404
|
if (data) {
|
|
325
405
|
const formattedLines = formatter(data, quotaData);
|
|
326
406
|
for (const line of formattedLines) {
|
|
@@ -328,9 +408,9 @@ function formatDataSection(title, data, formatter, quotaData, noDataMessage, LIN
|
|
|
328
408
|
}
|
|
329
409
|
}
|
|
330
410
|
else {
|
|
331
|
-
lines.push(
|
|
411
|
+
lines.push(formatBoxLine(noDataMessage, LINE_INDENT));
|
|
332
412
|
}
|
|
333
|
-
lines.push('╠' + '═'.repeat(
|
|
413
|
+
lines.push('╠' + '═'.repeat(LINE_CONTENT) + '╣');
|
|
334
414
|
return lines;
|
|
335
415
|
}
|
|
336
416
|
/**
|
|
@@ -356,8 +436,8 @@ function formatOutput(platform, startTime, endTime, quotaData, modelData, toolDa
|
|
|
356
436
|
const LINE_INDENT = 56;
|
|
357
437
|
lines.push(...formatHeader(platformName, startTime, endTime));
|
|
358
438
|
lines.push(...formatQuotaLimits(quotaData));
|
|
359
|
-
lines.push(...formatDataSection('
|
|
360
|
-
lines.push(...formatDataSection('
|
|
439
|
+
lines.push(...formatDataSection('MODEL USAGE (24h)', modelData, formatModelUsage, quotaData, 'No model usage data available', LINE_INDENT));
|
|
440
|
+
lines.push(...formatDataSection('TOOL/MCP USAGE (24h)', toolData, formatToolUsage, quotaData, 'No tool usage data available', LINE_INDENT));
|
|
361
441
|
lines.push(...formatFooter());
|
|
362
442
|
return lines.join('\n');
|
|
363
443
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"agent": {
|
|
4
4
|
"glm-quota-exec": {
|
|
5
5
|
"mode": "subagent",
|
|
6
|
-
"system": "
|
|
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
7
|
"permission": {}
|
|
8
8
|
}
|
|
9
9
|
}
|
package/package.json
CHANGED