opencode-glm-quota 1.2.0 → 1.3.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 +22 -3
- package/bin/install.js +229 -0
- package/integration/command/glm_quota.md +5 -0
- package/integration/opencode.jsonc +17 -0
- package/integration/skill/glm-quota-skill.md +10 -0
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -21,10 +21,21 @@ OpenCode plugin to query Z.ai GLM Coding Plan usage statistics with real-time qu
|
|
|
21
21
|
### Option 1: npm (Recommended)
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
|
|
24
|
+
# Install the plugin
|
|
25
|
+
npm install @opencode-glm-quota/plugin
|
|
26
|
+
|
|
27
|
+
# Run the installer to configure OpenCode
|
|
28
|
+
npx @opencode-glm-quota/plugin install
|
|
29
|
+
|
|
30
|
+
# Add to your OpenCode config (~/.config/opencode/opencode.json)
|
|
31
|
+
echo '"@opencode-glm-quota/plugin"' >> ~/.config/opencode/opencode.json
|
|
25
32
|
```
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
**What the installer does:**
|
|
35
|
+
- Copies `/glm_quota` command to `~/.config/opencode/command/glm_quota.md`
|
|
36
|
+
- Copies skill documentation to `~/.config/opencode/skill/glm-quota-skill.md`
|
|
37
|
+
- Merges agent configuration into `~/.config/opencode/opencode.json`
|
|
38
|
+
- Supports `--force` flag to overwrite existing files
|
|
28
39
|
|
|
29
40
|
### Option 2: From GitHub
|
|
30
41
|
|
|
@@ -43,6 +54,9 @@ npm run build
|
|
|
43
54
|
|
|
44
55
|
# Link for local testing
|
|
45
56
|
npm link
|
|
57
|
+
|
|
58
|
+
# Run the installer for local testing
|
|
59
|
+
node bin/install.js
|
|
46
60
|
```
|
|
47
61
|
|
|
48
62
|
## Quick Start
|
|
@@ -222,9 +236,14 @@ src/
|
|
|
222
236
|
date-formatter.ts # Date/time formatting utilities
|
|
223
237
|
progress-bar.ts # ASCII progress bar rendering
|
|
224
238
|
time-window.ts # Rolling window calculation
|
|
239
|
+
integration/
|
|
240
|
+
command/glm_quota.md # /glm_quota slash command
|
|
241
|
+
skill/glm-quota-skill.md # Skill documentation
|
|
242
|
+
opencode.jsonc # Agent configuration (JSONC)
|
|
243
|
+
bin/
|
|
244
|
+
install.js # Installation script
|
|
225
245
|
dist/ # Compiled JavaScript (generated)
|
|
226
246
|
tests/ # Test suite
|
|
227
|
-
.opencode/ # OpenCode integration files
|
|
228
247
|
package.json # Dependencies and scripts
|
|
229
248
|
tsconfig.json # TypeScript configuration
|
|
230
249
|
```
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GLM Quota Plugin Installer
|
|
5
|
+
*
|
|
6
|
+
* This script installs the GLM Quota Plugin integration files into the user's
|
|
7
|
+
* OpenCode configuration directory (~/.config/opencode/).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node bin/install.js # Interactive install (ask before overwriting)
|
|
11
|
+
* node bin/install.js --force # Force overwrite existing files
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as fs from 'fs'
|
|
15
|
+
import * as path from 'path'
|
|
16
|
+
import * as os from 'os'
|
|
17
|
+
import { parse as parseJsonc } from 'jsonc-parser'
|
|
18
|
+
|
|
19
|
+
// ==========================================
|
|
20
|
+
// CONSTANTS
|
|
21
|
+
// ==========================================
|
|
22
|
+
|
|
23
|
+
const __filename = decodeURIComponent(new URL(import.meta.url).pathname)
|
|
24
|
+
const __dirname = path.dirname(__filename)
|
|
25
|
+
const SOURCE_DIR = path.join(__dirname, '..', 'integration')
|
|
26
|
+
const COMMAND_FILE = path.join(SOURCE_DIR, 'command', 'glm_quota.md')
|
|
27
|
+
const SKILL_FILE = path.join(SOURCE_DIR, 'skill', 'glm-quota-skill.md')
|
|
28
|
+
const AGENT_CONFIG = path.join(SOURCE_DIR, 'opencode.jsonc')
|
|
29
|
+
|
|
30
|
+
const CONFIG_DIR = path.join(os.homedir(), '.config', 'opencode')
|
|
31
|
+
const TARGET_COMMAND = path.join(CONFIG_DIR, 'command', 'glm_quota.md')
|
|
32
|
+
const TARGET_SKILL = path.join(CONFIG_DIR, 'skill', 'glm-quota-skill.md')
|
|
33
|
+
|
|
34
|
+
// Check which config file exists (opencode.json or opencode.jsonc)
|
|
35
|
+
const TARGET_CONFIG_JSON = path.join(CONFIG_DIR, 'opencode.json')
|
|
36
|
+
const TARGET_CONFIG_JSONC = path.join(CONFIG_DIR, 'opencode.jsonc')
|
|
37
|
+
let TARGET_CONFIG = null
|
|
38
|
+
if (fileExists(TARGET_CONFIG_JSON)) {
|
|
39
|
+
TARGET_CONFIG = TARGET_CONFIG_JSON
|
|
40
|
+
} else if (fileExists(TARGET_CONFIG_JSONC)) {
|
|
41
|
+
TARGET_CONFIG = TARGET_CONFIG_JSONC
|
|
42
|
+
} else {
|
|
43
|
+
// Default to opencode.json if neither exists
|
|
44
|
+
TARGET_CONFIG = TARGET_CONFIG_JSON
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ==========================================
|
|
48
|
+
// UTILITY FUNCTIONS
|
|
49
|
+
// ==========================================
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Ensure directory exists, create if missing
|
|
53
|
+
*/
|
|
54
|
+
function ensureDirectory(dirPath) {
|
|
55
|
+
if (!fs.existsSync(dirPath)) {
|
|
56
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check if file exists
|
|
62
|
+
*/
|
|
63
|
+
function fileExists(filePath) {
|
|
64
|
+
return fs.existsSync(filePath)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Copy file from source to destination
|
|
69
|
+
*/
|
|
70
|
+
function copyFile(source, destination) {
|
|
71
|
+
ensureDirectory(path.dirname(destination))
|
|
72
|
+
fs.copyFileSync(source, destination)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parse JSON or JSONC file
|
|
77
|
+
*/
|
|
78
|
+
function parseConfig(filePath) {
|
|
79
|
+
try {
|
|
80
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
81
|
+
return parseJsonc(content)
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw new Error(`Failed to parse ${filePath}: ${error instanceof Error ? error.message : String(error)}`)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Write JSON file
|
|
89
|
+
*/
|
|
90
|
+
function writeConfig(filePath, data) {
|
|
91
|
+
ensureDirectory(path.dirname(filePath))
|
|
92
|
+
const json = JSON.stringify(data, null, 2) + '\n'
|
|
93
|
+
fs.writeFileSync(filePath, json)
|
|
94
|
+
console.log(` ✓ Wrote ${filePath} (${json.length} bytes)`)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Deep merge objects
|
|
99
|
+
*/
|
|
100
|
+
function deepMerge(target, source) {
|
|
101
|
+
const result = { ...target }
|
|
102
|
+
|
|
103
|
+
for (const key of Object.keys(source)) {
|
|
104
|
+
if (source[key] instanceof Object && key in result && result[key] instanceof Object) {
|
|
105
|
+
result[key] = deepMerge(result[key], source[key])
|
|
106
|
+
} else {
|
|
107
|
+
result[key] = source[key]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return result
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Prompt user for confirmation
|
|
116
|
+
*/
|
|
117
|
+
function promptConfirm(message) {
|
|
118
|
+
process.stdout.write(`${message} (y/N) `)
|
|
119
|
+
const response = process.stdin.read()
|
|
120
|
+
return response?.trim().toLowerCase() === 'y'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ==========================================
|
|
124
|
+
// INSTALLATION FUNCTIONS
|
|
125
|
+
// ==========================================
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Install command file
|
|
129
|
+
*/
|
|
130
|
+
function installCommand(force) {
|
|
131
|
+
if (fileExists(TARGET_COMMAND) && !force) {
|
|
132
|
+
if (!promptConfirm(`Command file exists: ${TARGET_COMMAND}\nOverwrite?`)) {
|
|
133
|
+
console.log(` ⊘ Skipped ${TARGET_COMMAND}`)
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
copyFile(COMMAND_FILE, TARGET_COMMAND)
|
|
139
|
+
console.log(` ✓ Created ${TARGET_COMMAND}`)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Install skill file
|
|
144
|
+
*/
|
|
145
|
+
function installSkill(force) {
|
|
146
|
+
if (fileExists(TARGET_SKILL) && !force) {
|
|
147
|
+
if (!promptConfirm(`Skill file exists: ${TARGET_SKILL}\nOverwrite?`)) {
|
|
148
|
+
console.log(` ⊘ Skipped ${TARGET_SKILL}`)
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
copyFile(SKILL_FILE, TARGET_SKILL)
|
|
154
|
+
console.log(` ✓ Created ${TARGET_SKILL}`)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Merge agent configuration and add plugin to plugins array
|
|
159
|
+
*/
|
|
160
|
+
function mergeConfig() {
|
|
161
|
+
// Parse existing config if it exists (same file type will be written)
|
|
162
|
+
let existingConfig = {}
|
|
163
|
+
if (fileExists(TARGET_CONFIG)) {
|
|
164
|
+
existingConfig = parseConfig(TARGET_CONFIG)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Parse new agent config from integration
|
|
168
|
+
const newConfig = parseConfig(AGENT_CONFIG)
|
|
169
|
+
|
|
170
|
+
// Merge agent definitions first
|
|
171
|
+
const mergedConfig = deepMerge(existingConfig, newConfig)
|
|
172
|
+
|
|
173
|
+
// Ensure plugins array exists and add our plugin
|
|
174
|
+
if (!mergedConfig.plugins) {
|
|
175
|
+
mergedConfig.plugins = []
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const PLUGIN_NAME = '@opencode-glm-quota/plugin'
|
|
179
|
+
const plugins = Array.isArray(mergedConfig.plugins) ? mergedConfig.plugins : []
|
|
180
|
+
|
|
181
|
+
// Only add if not already present
|
|
182
|
+
if (!plugins.includes(PLUGIN_NAME)) {
|
|
183
|
+
plugins.push(PLUGIN_NAME)
|
|
184
|
+
mergedConfig.plugins = plugins
|
|
185
|
+
console.log(` ✓ Added ${PLUGIN_NAME} to plugins array`)
|
|
186
|
+
} else {
|
|
187
|
+
console.log(` ⊙ Plugin ${PLUGIN_NAME} already in plugins array`)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Write merged config back to the same file (opencode.json or opencode.jsonc)
|
|
191
|
+
writeConfig(TARGET_CONFIG, mergedConfig)
|
|
192
|
+
console.log(` ✓ Merged configuration into ${path.basename(TARGET_CONFIG)}`)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ==========================================
|
|
196
|
+
// MAIN INSTALLATION FUNCTION
|
|
197
|
+
// ==========================================
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Main installer function
|
|
201
|
+
*/
|
|
202
|
+
function main() {
|
|
203
|
+
try {
|
|
204
|
+
// Parse command line arguments
|
|
205
|
+
const args = process.argv.slice(2)
|
|
206
|
+
const forceFlag = args.includes('--force')
|
|
207
|
+
|
|
208
|
+
console.log('✓ Installing GLM Quota Plugin...\n')
|
|
209
|
+
|
|
210
|
+
// Install integration files
|
|
211
|
+
installCommand(forceFlag)
|
|
212
|
+
installSkill(forceFlag)
|
|
213
|
+
mergeConfig()
|
|
214
|
+
|
|
215
|
+
console.log()
|
|
216
|
+
console.log('✓ Installation complete!')
|
|
217
|
+
console.log('✓ Restart OpenCode to use /glm_quota command')
|
|
218
|
+
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.error(`\n✗ Installation failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
221
|
+
console.error('✗ Check file permissions and try again')
|
|
222
|
+
process.exit(1)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Run installer
|
|
227
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
228
|
+
main()
|
|
229
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"agent": {
|
|
4
|
+
"glm-quota-exec": {
|
|
5
|
+
"mode": "subagent",
|
|
6
|
+
"system": "You are a minimal tool executor. Your only purpose is to execute the glm_quota tool when requested. Do not explain, reason, or add any commentary. Simply call the tool and return its output directly.",
|
|
7
|
+
"provider": "opencode",
|
|
8
|
+
"options": {
|
|
9
|
+
"system": "You are a minimal tool executor. Your only purpose is to execute the glm_quota tool when requested. Do not explain, reason, or add any commentary. Simply call the tool and return its output directly.",
|
|
10
|
+
"provider": "opencode"
|
|
11
|
+
},
|
|
12
|
+
"permission": {}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
// Note: This agent definition is merged into user's existing OpenCode config
|
|
16
|
+
// by the installer script to avoid overwriting their custom configurations
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: glm-quota
|
|
3
|
+
description: Query Z.ai GLM Coding Plan usage statistics including quota limits, model usage, and MCP tool usage
|
|
4
|
+
parameters:
|
|
5
|
+
- name: detailed
|
|
6
|
+
type: boolean
|
|
7
|
+
optional: true
|
|
8
|
+
description: Show detailed usage breakdown
|
|
9
|
+
---
|
|
10
|
+
Query GLM quota usage including token limits and MCP tool usage.
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-glm-quota",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode plugin to query Z.ai GLM Coding Plan usage statistics including quota limits, model usage, and MCP tool usage",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"opencode-glm-quota-install": "./bin/install.js"
|
|
10
|
+
},
|
|
8
11
|
"files": [
|
|
9
12
|
"dist",
|
|
13
|
+
"integration",
|
|
14
|
+
"bin",
|
|
10
15
|
"README.md",
|
|
11
16
|
"LICENSE"
|
|
12
17
|
],
|
|
@@ -14,6 +19,7 @@
|
|
|
14
19
|
"build": "tsc",
|
|
15
20
|
"clean": "rm -rf dist",
|
|
16
21
|
"prepublishOnly": "npm run clean && npm run build",
|
|
22
|
+
"postinstall": "node bin/install.js",
|
|
17
23
|
"test": "tsx --test $(find tests -name '*.test.ts' -type f)",
|
|
18
24
|
"test:coverage": "c8 --reporter=lcov --reporter=text -- npm test",
|
|
19
25
|
"lint": "eslint src/"
|
|
@@ -41,6 +47,9 @@
|
|
|
41
47
|
"url": "https://github.com/guyinwonder168/opencode-glm-quota/issues"
|
|
42
48
|
},
|
|
43
49
|
"homepage": "https://github.com/guyinwonder168/opencode-glm-quota#readme",
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"jsonc-parser": "^3.2.0"
|
|
52
|
+
},
|
|
44
53
|
"peerDependencies": {
|
|
45
54
|
"@opencode-ai/plugin": ">=0.1.0"
|
|
46
55
|
},
|
|
@@ -48,7 +57,7 @@
|
|
|
48
57
|
"access": "public"
|
|
49
58
|
},
|
|
50
59
|
"devDependencies": {
|
|
51
|
-
"@opencode-ai/plugin": "
|
|
60
|
+
"@opencode-ai/plugin": "^1.1.30",
|
|
52
61
|
"@types/node": "^20.0.0",
|
|
53
62
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
54
63
|
"@typescript-eslint/parser": "^6.0.0",
|