@mdrv/opencode-quota 262.0.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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +189 -0
  3. package/bin/copilot-quota.ts +374 -0
  4. package/bin/glm-quota.ts +467 -0
  5. package/bin/install.js +439 -0
  6. package/bin/kimi-quota.ts +314 -0
  7. package/dist/bin/copilot-quota.d.ts +8 -0
  8. package/dist/bin/copilot-quota.d.ts.map +1 -0
  9. package/dist/bin/copilot-quota.js +298 -0
  10. package/dist/bin/copilot-quota.js.map +1 -0
  11. package/dist/bin/glm-quota.d.ts +8 -0
  12. package/dist/bin/glm-quota.d.ts.map +1 -0
  13. package/dist/bin/glm-quota.js +367 -0
  14. package/dist/bin/glm-quota.js.map +1 -0
  15. package/dist/bin/kimi-quota.d.ts +3 -0
  16. package/dist/bin/kimi-quota.d.ts.map +1 -0
  17. package/dist/bin/kimi-quota.js +241 -0
  18. package/dist/bin/kimi-quota.js.map +1 -0
  19. package/dist/src/api/client.d.ts +76 -0
  20. package/dist/src/api/client.d.ts.map +1 -0
  21. package/dist/src/api/client.js +203 -0
  22. package/dist/src/api/client.js.map +1 -0
  23. package/dist/src/api/endpoints.d.ts +22 -0
  24. package/dist/src/api/endpoints.d.ts.map +1 -0
  25. package/dist/src/api/endpoints.js +41 -0
  26. package/dist/src/api/endpoints.js.map +1 -0
  27. package/dist/src/api/platforms.d.ts +20 -0
  28. package/dist/src/api/platforms.d.ts.map +1 -0
  29. package/dist/src/api/platforms.js +38 -0
  30. package/dist/src/api/platforms.js.map +1 -0
  31. package/dist/src/index.d.ts +10 -0
  32. package/dist/src/index.d.ts.map +1 -0
  33. package/dist/src/index.js +723 -0
  34. package/dist/src/index.js.map +1 -0
  35. package/dist/src/shared/logging.d.ts +7 -0
  36. package/dist/src/shared/logging.d.ts.map +1 -0
  37. package/dist/src/shared/logging.js +29 -0
  38. package/dist/src/shared/logging.js.map +1 -0
  39. package/dist/src/utils/box-constants.d.ts +43 -0
  40. package/dist/src/utils/box-constants.d.ts.map +1 -0
  41. package/dist/src/utils/box-constants.js +43 -0
  42. package/dist/src/utils/box-constants.js.map +1 -0
  43. package/dist/src/utils/date-formatter.d.ts +17 -0
  44. package/dist/src/utils/date-formatter.d.ts.map +1 -0
  45. package/dist/src/utils/date-formatter.js +33 -0
  46. package/dist/src/utils/date-formatter.js.map +1 -0
  47. package/dist/src/utils/error-formatter.d.ts +17 -0
  48. package/dist/src/utils/error-formatter.d.ts.map +1 -0
  49. package/dist/src/utils/error-formatter.js +60 -0
  50. package/dist/src/utils/error-formatter.js.map +1 -0
  51. package/dist/src/utils/progress-bar.d.ts +35 -0
  52. package/dist/src/utils/progress-bar.d.ts.map +1 -0
  53. package/dist/src/utils/progress-bar.js +43 -0
  54. package/dist/src/utils/progress-bar.js.map +1 -0
  55. package/dist/src/utils/reset-timer.d.ts +11 -0
  56. package/dist/src/utils/reset-timer.d.ts.map +1 -0
  57. package/dist/src/utils/reset-timer.js +32 -0
  58. package/dist/src/utils/reset-timer.js.map +1 -0
  59. package/dist/src/utils/time-window.d.ts +30 -0
  60. package/dist/src/utils/time-window.d.ts.map +1 -0
  61. package/dist/src/utils/time-window.js +34 -0
  62. package/dist/src/utils/time-window.js.map +1 -0
  63. package/dist/tests/error-handling/api-errors.test.d.ts +7 -0
  64. package/dist/tests/error-handling/api-errors.test.d.ts.map +1 -0
  65. package/dist/tests/error-handling/api-errors.test.js +110 -0
  66. package/dist/tests/error-handling/api-errors.test.js.map +1 -0
  67. package/dist/tests/error-handling/auth-errors.test.d.ts +7 -0
  68. package/dist/tests/error-handling/auth-errors.test.d.ts.map +1 -0
  69. package/dist/tests/error-handling/auth-errors.test.js +110 -0
  70. package/dist/tests/error-handling/auth-errors.test.js.map +1 -0
  71. package/dist/tests/error-handling/network-errors.test.d.ts +7 -0
  72. package/dist/tests/error-handling/network-errors.test.d.ts.map +1 -0
  73. package/dist/tests/error-handling/network-errors.test.js +94 -0
  74. package/dist/tests/error-handling/network-errors.test.js.map +1 -0
  75. package/dist/tests/error-handling/parse-errors.test.d.ts +7 -0
  76. package/dist/tests/error-handling/parse-errors.test.d.ts.map +1 -0
  77. package/dist/tests/error-handling/parse-errors.test.js +87 -0
  78. package/dist/tests/error-handling/parse-errors.test.js.map +1 -0
  79. package/dist/tests/error-handling/token-sanitization.test.d.ts +2 -0
  80. package/dist/tests/error-handling/token-sanitization.test.d.ts.map +1 -0
  81. package/dist/tests/error-handling/token-sanitization.test.js +59 -0
  82. package/dist/tests/error-handling/token-sanitization.test.js.map +1 -0
  83. package/dist/tests/functional/date-formatter.test.d.ts +5 -0
  84. package/dist/tests/functional/date-formatter.test.d.ts.map +1 -0
  85. package/dist/tests/functional/date-formatter.test.js +46 -0
  86. package/dist/tests/functional/date-formatter.test.js.map +1 -0
  87. package/dist/tests/functional/progress-bar.test.d.ts +5 -0
  88. package/dist/tests/functional/progress-bar.test.d.ts.map +1 -0
  89. package/dist/tests/functional/progress-bar.test.js +82 -0
  90. package/dist/tests/functional/progress-bar.test.js.map +1 -0
  91. package/dist/tests/functional/reset-timer.test.d.ts +6 -0
  92. package/dist/tests/functional/reset-timer.test.d.ts.map +1 -0
  93. package/dist/tests/functional/reset-timer.test.js +67 -0
  94. package/dist/tests/functional/reset-timer.test.js.map +1 -0
  95. package/dist/tests/functional/time-window.test.d.ts +5 -0
  96. package/dist/tests/functional/time-window.test.d.ts.map +1 -0
  97. package/dist/tests/functional/time-window.test.js +46 -0
  98. package/dist/tests/functional/time-window.test.js.map +1 -0
  99. package/dist/tests/integration/box-alignment.test.d.ts +8 -0
  100. package/dist/tests/integration/box-alignment.test.d.ts.map +1 -0
  101. package/dist/tests/integration/box-alignment.test.js +238 -0
  102. package/dist/tests/integration/box-alignment.test.js.map +1 -0
  103. package/dist/tests/integration/error-handling.test.d.ts +2 -0
  104. package/dist/tests/integration/error-handling.test.d.ts.map +1 -0
  105. package/dist/tests/integration/error-handling.test.js +36 -0
  106. package/dist/tests/integration/error-handling.test.js.map +1 -0
  107. package/dist/tests/integration/installer-config.test.d.ts +2 -0
  108. package/dist/tests/integration/installer-config.test.d.ts.map +1 -0
  109. package/dist/tests/integration/installer-config.test.js +65 -0
  110. package/dist/tests/integration/installer-config.test.js.map +1 -0
  111. package/dist/tests/integration/plugin-catch-block.test.d.ts +2 -0
  112. package/dist/tests/integration/plugin-catch-block.test.d.ts.map +1 -0
  113. package/dist/tests/integration/plugin-catch-block.test.js +134 -0
  114. package/dist/tests/integration/plugin-catch-block.test.js.map +1 -0
  115. package/dist/tests/integration/reset-time-display.test.d.ts +6 -0
  116. package/dist/tests/integration/reset-time-display.test.d.ts.map +1 -0
  117. package/dist/tests/integration/reset-time-display.test.js +138 -0
  118. package/dist/tests/integration/reset-time-display.test.js.map +1 -0
  119. package/dist/tests/module/http-client.test.d.ts +2 -0
  120. package/dist/tests/module/http-client.test.d.ts.map +1 -0
  121. package/dist/tests/module/http-client.test.js +49 -0
  122. package/dist/tests/module/http-client.test.js.map +1 -0
  123. package/dist/tests/module/platform-detection.test.d.ts +5 -0
  124. package/dist/tests/module/platform-detection.test.d.ts.map +1 -0
  125. package/dist/tests/module/platform-detection.test.js +48 -0
  126. package/dist/tests/module/platform-detection.test.js.map +1 -0
  127. package/integration/agents/copilot-quota-exec.md +20 -0
  128. package/integration/agents/glm-quota-exec.md +20 -0
  129. package/integration/command/copilot_quota.md +6 -0
  130. package/integration/command/glm_quota.md +6 -0
  131. package/integration/skills/copilot-quota/SKILL.md +11 -0
  132. package/integration/skills/glm-quota/SKILL.md +11 -0
  133. package/package.json +69 -0
package/bin/install.js ADDED
@@ -0,0 +1,439 @@
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
+ * node bin/install.js uninstall # Remove integration files and config
13
+ */
14
+
15
+ import { spawnSync } from 'child_process'
16
+ import * as fs from 'fs'
17
+ import { parse as parseJsonc } from 'jsonc-parser'
18
+ import * as os from 'os'
19
+ import * as path from 'path'
20
+
21
+ // ==========================================
22
+ // CONSTANTS
23
+ // ==========================================
24
+
25
+ const __filename = decodeURIComponent(new URL(import.meta.url).pathname)
26
+ const __dirname = path.dirname(__filename)
27
+ const SOURCE_DIR = path.join(__dirname, '..', 'integration')
28
+
29
+ // GLM Quota files
30
+ const COMMAND_FILE = path.join(SOURCE_DIR, 'command', 'glm_quota.md')
31
+ const SKILL_FILE = path.join(SOURCE_DIR, 'skills', 'glm-quota', 'SKILL.md')
32
+ const AGENT_FILE = path.join(SOURCE_DIR, 'agents', 'glm-quota-exec.md')
33
+
34
+ // Copilot Quota files
35
+ const COPILOT_COMMAND_FILE = path.join(SOURCE_DIR, 'command', 'copilot_quota.md')
36
+ const COPILOT_SKILL_FILE = path.join(SOURCE_DIR, 'skills', 'copilot-quota', 'SKILL.md')
37
+ const COPILOT_AGENT_FILE = path.join(SOURCE_DIR, 'agents', 'copilot-quota-exec.md')
38
+
39
+ const CONFIG_DIR = path.join(os.homedir(), '.config', 'opencode')
40
+
41
+ // GLM Quota targets
42
+ const TARGET_COMMAND = path.join(CONFIG_DIR, 'command', 'glm_quota.md')
43
+ const TARGET_SKILL = path.join(CONFIG_DIR, 'skills', 'glm-quota', 'SKILL.md')
44
+ const TARGET_AGENT = path.join(CONFIG_DIR, 'agents', 'glm-quota-exec.md')
45
+
46
+ // Copilot Quota targets
47
+ const TARGET_COPILOT_COMMAND = path.join(CONFIG_DIR, 'command', 'copilot_quota.md')
48
+ const TARGET_COPILOT_SKILL = path.join(CONFIG_DIR, 'skills', 'copilot-quota', 'SKILL.md')
49
+ const TARGET_COPILOT_AGENT = path.join(CONFIG_DIR, 'agents', 'copilot-quota-exec.md')
50
+
51
+ // Check which config file exists (opencode.json or opencode.jsonc)
52
+ const TARGET_CONFIG_JSON = path.join(CONFIG_DIR, 'opencode.json')
53
+ const TARGET_CONFIG_JSONC = path.join(CONFIG_DIR, 'opencode.jsonc')
54
+ let TARGET_CONFIG = null
55
+ if (fileExists(TARGET_CONFIG_JSON)) {
56
+ TARGET_CONFIG = TARGET_CONFIG_JSON
57
+ } else if (fileExists(TARGET_CONFIG_JSONC)) {
58
+ TARGET_CONFIG = TARGET_CONFIG_JSONC
59
+ } else {
60
+ // Default to opencode.json if neither exists
61
+ TARGET_CONFIG = TARGET_CONFIG_JSON
62
+ }
63
+
64
+ // ==========================================
65
+ // UTILITY FUNCTIONS
66
+ // ==========================================
67
+
68
+ /**
69
+ * Ensure directory exists, create if missing
70
+ */
71
+ function ensureDirectory(dirPath) {
72
+ if (!fs.existsSync(dirPath)) {
73
+ fs.mkdirSync(dirPath, { recursive: true })
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Check if file exists
79
+ */
80
+ function fileExists(filePath) {
81
+ return fs.existsSync(filePath)
82
+ }
83
+
84
+ /**
85
+ * Copy file from source to destination
86
+ */
87
+ function copyFile(source, destination) {
88
+ ensureDirectory(path.dirname(destination))
89
+ fs.copyFileSync(source, destination)
90
+ }
91
+
92
+ /**
93
+ * Remove file if it exists
94
+ */
95
+ function removeFile(filePath, label) {
96
+ if (fileExists(filePath)) {
97
+ fs.unlinkSync(filePath)
98
+ console.log(` ✓ Removed ${label}`)
99
+ } else {
100
+ console.log(` ⊙ Not found ${label}`)
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Remove directory if it exists
106
+ */
107
+ function removeDirectory(dirPath, label) {
108
+ if (fileExists(dirPath)) {
109
+ fs.rmSync(dirPath, { recursive: true, force: true })
110
+ console.log(` ✓ Removed ${label}`)
111
+ } else {
112
+ console.log(` ⊙ Not found ${label}`)
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Parse JSON or JSONC file
118
+ */
119
+ function parseConfig(filePath) {
120
+ try {
121
+ const content = fs.readFileSync(filePath, 'utf-8')
122
+ return parseJsonc(content)
123
+ } catch (error) {
124
+ throw new Error(`Failed to parse ${filePath}: ${error instanceof Error ? error.message : String(error)}`)
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Write JSON file
130
+ */
131
+ function writeConfig(filePath, data) {
132
+ ensureDirectory(path.dirname(filePath))
133
+ const json = JSON.stringify(data, null, 2) + '\n'
134
+ fs.writeFileSync(filePath, json)
135
+ console.log(` ✓ Wrote ${filePath} (${json.length} bytes)`)
136
+ }
137
+
138
+ /**
139
+ * Deep merge objects
140
+ */
141
+ function deepMerge(target, source) {
142
+ const result = { ...target }
143
+
144
+ for (const key of Object.keys(source)) {
145
+ if (source[key] instanceof Object && key in result && result[key] instanceof Object) {
146
+ result[key] = deepMerge(result[key], source[key])
147
+ } else {
148
+ result[key] = source[key]
149
+ }
150
+ }
151
+
152
+ return result
153
+ }
154
+
155
+ /**
156
+ * Prompt user for confirmation
157
+ */
158
+ function promptConfirm(message) {
159
+ process.stdout.write(`${message} (y/N) `)
160
+ const response = process.stdin.read()
161
+ return response?.trim().toLowerCase() === 'y'
162
+ }
163
+
164
+ // ==========================================
165
+ // INSTALLATION FUNCTIONS
166
+ // ==========================================
167
+
168
+ /**
169
+ * Install command file
170
+ */
171
+ function installCommand(force) {
172
+ if (fileExists(TARGET_COMMAND) && !force) {
173
+ if (!promptConfirm(`Command file exists: ${TARGET_COMMAND}\nOverwrite?`)) {
174
+ console.log(` ⊘ Skipped ${TARGET_COMMAND}`)
175
+ return
176
+ }
177
+ }
178
+
179
+ copyFile(COMMAND_FILE, TARGET_COMMAND)
180
+ console.log(` ✓ Created ${TARGET_COMMAND}`)
181
+ }
182
+
183
+ /**
184
+ * Install skill file
185
+ */
186
+ function installSkill(force) {
187
+ if (fileExists(TARGET_SKILL) && !force) {
188
+ if (!promptConfirm(`Skill directory exists: ${path.dirname(TARGET_SKILL)}\nOverwrite?`)) {
189
+ console.log(` ⊘ Skipped ${TARGET_SKILL}`)
190
+ return
191
+ }
192
+ }
193
+
194
+ copyFile(SKILL_FILE, TARGET_SKILL)
195
+ console.log(` ✓ Created ${path.join(path.basename(path.dirname(TARGET_SKILL)), path.basename(TARGET_SKILL))}`)
196
+ }
197
+
198
+ /**
199
+ * Install agent file
200
+ */
201
+ function installAgent(force) {
202
+ if (fileExists(TARGET_AGENT) && !force) {
203
+ if (!promptConfirm(`Agent file exists: ${TARGET_AGENT}\nOverwrite?`)) {
204
+ console.log(` ⊘ Skipped ${TARGET_AGENT}`)
205
+ return
206
+ }
207
+ }
208
+
209
+ copyFile(AGENT_FILE, TARGET_AGENT)
210
+ console.log(` ✓ Created ${TARGET_AGENT}`)
211
+ }
212
+
213
+ /**
214
+ * Install Copilot command file
215
+ */
216
+ function installCopilotCommand(force) {
217
+ if (fileExists(TARGET_COPILOT_COMMAND) && !force) {
218
+ if (!promptConfirm(`Copilot command file exists: ${TARGET_COPILOT_COMMAND}\nOverwrite?`)) {
219
+ console.log(` ⊘ Skipped ${TARGET_COPILOT_COMMAND}`)
220
+ return
221
+ }
222
+ }
223
+
224
+ copyFile(COPILOT_COMMAND_FILE, TARGET_COPILOT_COMMAND)
225
+ console.log(` ✓ Created ${TARGET_COPILOT_COMMAND}`)
226
+ }
227
+
228
+ /**
229
+ * Install Copilot skill file
230
+ */
231
+ function installCopilotSkill(force) {
232
+ if (fileExists(TARGET_COPILOT_SKILL) && !force) {
233
+ if (!promptConfirm(`Copilot skill directory exists: ${path.dirname(TARGET_COPILOT_SKILL)}\nOverwrite?`)) {
234
+ console.log(` ⊘ Skipped ${TARGET_COPILOT_SKILL}`)
235
+ return
236
+ }
237
+ }
238
+
239
+ copyFile(COPILOT_SKILL_FILE, TARGET_COPILOT_SKILL)
240
+ console.log(
241
+ ` ✓ Created ${path.join(path.basename(path.dirname(TARGET_COPILOT_SKILL)), path.basename(TARGET_COPILOT_SKILL))}`,
242
+ )
243
+ }
244
+
245
+ /**
246
+ * Install Copilot agent file
247
+ */
248
+ function installCopilotAgent(force) {
249
+ if (fileExists(TARGET_COPILOT_AGENT) && !force) {
250
+ if (!promptConfirm(`Copilot agent file exists: ${TARGET_COPILOT_AGENT}\nOverwrite?`)) {
251
+ console.log(` ⊘ Skipped ${TARGET_COPILOT_AGENT}`)
252
+ return
253
+ }
254
+ }
255
+
256
+ copyFile(COPILOT_AGENT_FILE, TARGET_COPILOT_AGENT)
257
+ console.log(` ✓ Created ${TARGET_COPILOT_AGENT}`)
258
+ }
259
+
260
+ /**
261
+ * Update plugin configuration and cleanup old JSON agent
262
+ */
263
+ function updatePluginConfig() {
264
+ // Parse existing config if it exists
265
+ let existingConfig = {}
266
+ if (fileExists(TARGET_CONFIG)) {
267
+ existingConfig = parseConfig(TARGET_CONFIG)
268
+ }
269
+
270
+ const PLUGIN_NAME = 'opencode-glm-quota'
271
+
272
+ // CLEANUP: Remove old JSON agent config if it exists (migration from v1.3.x)
273
+ if (existingConfig.agent && existingConfig.agent['glm-quota-exec']) {
274
+ delete existingConfig.agent['glm-quota-exec']
275
+ if (Object.keys(existingConfig.agent).length === 0) {
276
+ delete existingConfig.agent
277
+ }
278
+ console.log(' ✓ Removed old JSON agent config (migrated to Markdown)')
279
+ }
280
+
281
+ // OpenCode config key is "plugin" (singular). Migrate legacy "plugins" entries.
282
+ const plugin = Array.isArray(existingConfig.plugin) ? [...existingConfig.plugin] : []
283
+ const legacyPlugins = Array.isArray(existingConfig.plugins) ? existingConfig.plugins : []
284
+
285
+ if (legacyPlugins.length > 0) {
286
+ for (const name of legacyPlugins) {
287
+ if (typeof name === 'string' && !plugin.includes(name)) {
288
+ plugin.push(name)
289
+ }
290
+ }
291
+ delete existingConfig.plugins
292
+ console.log(' ✓ Migrated legacy plugins array to plugin')
293
+ }
294
+
295
+ // Only add if not already present
296
+ if (!plugin.includes(PLUGIN_NAME)) {
297
+ plugin.push(PLUGIN_NAME)
298
+ console.log(` ✓ Added ${PLUGIN_NAME} to plugin array`)
299
+ } else {
300
+ console.log(` ⊙ Plugin ${PLUGIN_NAME} already in plugin array`)
301
+ }
302
+
303
+ existingConfig.plugin = plugin
304
+
305
+ // Write config back to same file (opencode.json or opencode.jsonc)
306
+ writeConfig(TARGET_CONFIG, existingConfig)
307
+ console.log(` ✓ Updated ${path.basename(TARGET_CONFIG)}`)
308
+ }
309
+
310
+ /**
311
+ * Remove plugin configuration and agent configuration
312
+ */
313
+ function removeConfig() {
314
+ if (!fileExists(TARGET_CONFIG)) {
315
+ console.log(` ⊙ Config not found: ${TARGET_CONFIG}`)
316
+ return
317
+ }
318
+
319
+ const PLUGIN_NAME = 'opencode-glm-quota'
320
+ const existingConfig = parseConfig(TARGET_CONFIG)
321
+ let changed = false
322
+
323
+ if (Array.isArray(existingConfig.plugin)) {
324
+ const next = existingConfig.plugin.filter((name) => name !== PLUGIN_NAME)
325
+ if (next.length !== existingConfig.plugin.length) {
326
+ existingConfig.plugin = next
327
+ changed = true
328
+ console.log(' ✓ Removed plugin from plugin array')
329
+ }
330
+ }
331
+
332
+ if (Array.isArray(existingConfig.plugins)) {
333
+ const next = existingConfig.plugins.filter((name) => name !== PLUGIN_NAME)
334
+ if (next.length !== existingConfig.plugins.length) {
335
+ existingConfig.plugins = next
336
+ changed = true
337
+ console.log(' ✓ Removed plugin from plugins array')
338
+ }
339
+ }
340
+
341
+ if (existingConfig.agent && existingConfig.agent['glm-quota-exec']) {
342
+ delete existingConfig.agent['glm-quota-exec']
343
+ if (Object.keys(existingConfig.agent).length === 0) {
344
+ delete existingConfig.agent
345
+ }
346
+ changed = true
347
+ console.log(' ✓ Removed glm-quota-exec agent config')
348
+ }
349
+
350
+ if (changed) {
351
+ writeConfig(TARGET_CONFIG, existingConfig)
352
+ console.log(` ✓ Updated ${path.basename(TARGET_CONFIG)}`)
353
+ } else {
354
+ console.log(' ⊙ No config changes needed')
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Remove npm package
360
+ */
361
+ function removePackage(globalFlag) {
362
+ const args = ['remove', 'opencode-glm-quota']
363
+ if (globalFlag) {
364
+ args.push('--global')
365
+ }
366
+
367
+ const result = spawnSync('npm', args, { stdio: 'inherit' })
368
+ if (result.status !== 0) {
369
+ console.log(' ⊙ npm remove failed, remove manually if needed')
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Uninstall integration files and configuration
375
+ */
376
+ function uninstall(globalFlag) {
377
+ // Remove GLM Quota files
378
+ removeFile(TARGET_COMMAND, TARGET_COMMAND)
379
+ removeDirectory(path.dirname(TARGET_SKILL), path.dirname(TARGET_SKILL))
380
+ removeFile(TARGET_AGENT, TARGET_AGENT)
381
+
382
+ // Remove Copilot Quota files
383
+ removeFile(TARGET_COPILOT_COMMAND, TARGET_COPILOT_COMMAND)
384
+ removeDirectory(path.dirname(TARGET_COPILOT_SKILL), path.dirname(TARGET_COPILOT_SKILL))
385
+ removeFile(TARGET_COPILOT_AGENT, TARGET_COPILOT_AGENT)
386
+
387
+ removeConfig()
388
+ removePackage(globalFlag)
389
+ }
390
+
391
+ // ==========================================
392
+ // MAIN INSTALLATION FUNCTION
393
+ // ==========================================
394
+
395
+ /**
396
+ * Main installer function
397
+ */
398
+ function main() {
399
+ try {
400
+ // Parse command line arguments
401
+ const args = process.argv.slice(2)
402
+ const isUninstall = args.includes('uninstall')
403
+ const forceFlag = args.includes('--force')
404
+ const globalFlag = args.includes('--global') || args.includes('-g')
405
+
406
+ if (isUninstall) {
407
+ console.log('✓ Uninstalling GLM Quota Plugin...\n')
408
+ uninstall(globalFlag)
409
+ console.log()
410
+ console.log('✓ Uninstall complete!')
411
+ return
412
+ }
413
+
414
+ console.log('✓ Installing GLM Quota Plugin...\n')
415
+
416
+ // Install integration files
417
+ installCommand(forceFlag)
418
+ installSkill(forceFlag)
419
+ installAgent(forceFlag)
420
+
421
+ // Copilot Quota installation
422
+ installCopilotCommand(forceFlag)
423
+ installCopilotSkill(forceFlag)
424
+ installCopilotAgent(forceFlag)
425
+
426
+ updatePluginConfig()
427
+
428
+ console.log()
429
+ console.log('✓ Installation complete!')
430
+ console.log('✓ Restart OpenCode to use /glm_quota and /copilot_quota commands')
431
+ } catch (error) {
432
+ console.error(`\n✗ Installation failed: ${error instanceof Error ? error.message : String(error)}`)
433
+ console.error('✗ Check file permissions and try again')
434
+ process.exit(1)
435
+ }
436
+ }
437
+
438
+ // Run installer
439
+ main()