@algochad/archcoder 2.0.2

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 (157) hide show
  1. package/README.md +113 -0
  2. package/bin/cli-entry.js +55 -0
  3. package/bin/cli-output.js +145 -0
  4. package/bin/cli.js +5108 -0
  5. package/bin/cli.test.js +56 -0
  6. package/dist/apple-touch-icon-120x120.png +0 -0
  7. package/dist/apple-touch-icon-152x152.png +0 -0
  8. package/dist/apple-touch-icon-167x167.png +0 -0
  9. package/dist/apple-touch-icon-180x180.png +0 -0
  10. package/dist/apple-touch-icon.png +0 -0
  11. package/dist/apple-touch-icon.svg +67 -0
  12. package/dist/assets/MultiRunWindow-BZp3MjJP.js +1 -0
  13. package/dist/assets/SettingsWindow-DoGYXpX7.js +1 -0
  14. package/dist/assets/TerminalView-BN7BR5Ff.js +3 -0
  15. package/dist/assets/TimelineDialog-ZQ33oVQR.js +1 -0
  16. package/dist/assets/ToolOutputDialog-Blv3pnug.js +16 -0
  17. package/dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  18. package/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  19. package/dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  20. package/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  21. package/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  22. package/dist/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
  23. package/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  24. package/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  25. package/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  26. package/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  27. package/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  28. package/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  29. package/dist/assets/index-CtCEGYrr.css +1 -0
  30. package/dist/assets/index-o_d2wtWC.js +48 -0
  31. package/dist/assets/main-5QGBtzdq.css +1 -0
  32. package/dist/assets/main-B6oiMU86.js +8033 -0
  33. package/dist/assets/vendor--DbVqbJpV.css +1 -0
  34. package/dist/assets/vendor-.bun-HTKwyaEM.js +10086 -0
  35. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  36. package/dist/assets/worker-bqd4RMrj.js +155 -0
  37. package/dist/favicon-16.png +0 -0
  38. package/dist/favicon-32.png +0 -0
  39. package/dist/favicon.png +0 -0
  40. package/dist/favicon.svg +67 -0
  41. package/dist/index.html +533 -0
  42. package/dist/logo-dark-192x192.png +0 -0
  43. package/dist/logo-dark-512x512.svg +16 -0
  44. package/dist/logo-light-192x192.png +0 -0
  45. package/dist/logo-light-512x512.svg +16 -0
  46. package/dist/pwa-192.png +0 -0
  47. package/dist/pwa-512.png +0 -0
  48. package/dist/pwa-maskable-192.png +0 -0
  49. package/dist/pwa-maskable-512.png +0 -0
  50. package/dist/site.webmanifest +22 -0
  51. package/dist/sw.js +1 -0
  52. package/package.json +107 -0
  53. package/public/apple-touch-icon-120x120.png +0 -0
  54. package/public/apple-touch-icon-152x152.png +0 -0
  55. package/public/apple-touch-icon-167x167.png +0 -0
  56. package/public/apple-touch-icon-180x180.png +0 -0
  57. package/public/apple-touch-icon.png +0 -0
  58. package/public/apple-touch-icon.svg +67 -0
  59. package/public/favicon-16.png +0 -0
  60. package/public/favicon-32.png +0 -0
  61. package/public/favicon.png +0 -0
  62. package/public/favicon.svg +67 -0
  63. package/public/logo-dark-192x192.png +0 -0
  64. package/public/logo-dark-512x512.svg +16 -0
  65. package/public/logo-light-192x192.png +0 -0
  66. package/public/logo-light-512x512.svg +16 -0
  67. package/public/pwa-192.png +0 -0
  68. package/public/pwa-512.png +0 -0
  69. package/public/pwa-maskable-192.png +0 -0
  70. package/public/pwa-maskable-512.png +0 -0
  71. package/public/site.webmanifest +22 -0
  72. package/server/TERMINAL_INPUT_WS_PROTOCOL.md +44 -0
  73. package/server/index.d.ts +37 -0
  74. package/server/index.js +14694 -0
  75. package/server/lib/cloudflare-tunnel.js +650 -0
  76. package/server/lib/git/DOCUMENTATION.md +146 -0
  77. package/server/lib/git/credentials.js +74 -0
  78. package/server/lib/git/identity-storage.js +110 -0
  79. package/server/lib/git/index.js +6 -0
  80. package/server/lib/git/service.js +3117 -0
  81. package/server/lib/github/DOCUMENTATION.md +170 -0
  82. package/server/lib/github/auth.js +307 -0
  83. package/server/lib/github/device-flow.js +50 -0
  84. package/server/lib/github/index.js +24 -0
  85. package/server/lib/github/octokit.js +10 -0
  86. package/server/lib/github/pr-status.js +478 -0
  87. package/server/lib/github/repo/index.js +55 -0
  88. package/server/lib/installer/desktop.js +289 -0
  89. package/server/lib/installer/download.js +208 -0
  90. package/server/lib/installer/index.js +45 -0
  91. package/server/lib/installer/platform.js +100 -0
  92. package/server/lib/notifications/DOCUMENTATION.md +61 -0
  93. package/server/lib/notifications/index.js +1 -0
  94. package/server/lib/notifications/message.js +49 -0
  95. package/server/lib/notifications/message.test.js +59 -0
  96. package/server/lib/opencode/DOCUMENTATION.md +59 -0
  97. package/server/lib/opencode/agents.js +634 -0
  98. package/server/lib/opencode/auth.js +81 -0
  99. package/server/lib/opencode/commands.js +339 -0
  100. package/server/lib/opencode/index.js +66 -0
  101. package/server/lib/opencode/mcp.js +206 -0
  102. package/server/lib/opencode/providers.js +96 -0
  103. package/server/lib/opencode/shared.js +527 -0
  104. package/server/lib/opencode/skills.js +480 -0
  105. package/server/lib/opencode/tunnel-auth.js +591 -0
  106. package/server/lib/opencode/ui-auth.js +510 -0
  107. package/server/lib/package-manager.js +505 -0
  108. package/server/lib/quota/DOCUMENTATION.md +55 -0
  109. package/server/lib/quota/index.js +24 -0
  110. package/server/lib/quota/providers/claude.js +107 -0
  111. package/server/lib/quota/providers/codex.js +113 -0
  112. package/server/lib/quota/providers/copilot.js +165 -0
  113. package/server/lib/quota/providers/google/api.js +92 -0
  114. package/server/lib/quota/providers/google/auth.js +108 -0
  115. package/server/lib/quota/providers/google/index.js +124 -0
  116. package/server/lib/quota/providers/google/transforms.js +109 -0
  117. package/server/lib/quota/providers/index.js +152 -0
  118. package/server/lib/quota/providers/interface.js +55 -0
  119. package/server/lib/quota/providers/kimi.js +108 -0
  120. package/server/lib/quota/providers/minimax-cn-coding-plan.js +15 -0
  121. package/server/lib/quota/providers/minimax-coding-plan.js +15 -0
  122. package/server/lib/quota/providers/minimax-shared.js +136 -0
  123. package/server/lib/quota/providers/nanogpt.js +124 -0
  124. package/server/lib/quota/providers/ollama-cloud.js +112 -0
  125. package/server/lib/quota/providers/openai.js +91 -0
  126. package/server/lib/quota/providers/openrouter.js +92 -0
  127. package/server/lib/quota/providers/zai.js +91 -0
  128. package/server/lib/quota/utils/auth.js +46 -0
  129. package/server/lib/quota/utils/formatters.js +76 -0
  130. package/server/lib/quota/utils/index.js +10 -0
  131. package/server/lib/quota/utils/transformers.js +55 -0
  132. package/server/lib/skills-catalog/DOCUMENTATION.md +178 -0
  133. package/server/lib/skills-catalog/cache.js +32 -0
  134. package/server/lib/skills-catalog/clawdhub/api.js +158 -0
  135. package/server/lib/skills-catalog/clawdhub/index.js +30 -0
  136. package/server/lib/skills-catalog/clawdhub/install.js +238 -0
  137. package/server/lib/skills-catalog/clawdhub/scan.js +113 -0
  138. package/server/lib/skills-catalog/curated-sources.js +21 -0
  139. package/server/lib/skills-catalog/git.js +77 -0
  140. package/server/lib/skills-catalog/index.js +42 -0
  141. package/server/lib/skills-catalog/install.js +294 -0
  142. package/server/lib/skills-catalog/scan.js +221 -0
  143. package/server/lib/skills-catalog/source.js +85 -0
  144. package/server/lib/terminal/DOCUMENTATION.md +114 -0
  145. package/server/lib/terminal/index.js +12 -0
  146. package/server/lib/terminal/input-ws-protocol.js +66 -0
  147. package/server/lib/terminal/input-ws-protocol.test.js +138 -0
  148. package/server/lib/tts/DOCUMENTATION.md +134 -0
  149. package/server/lib/tts/index.js +16 -0
  150. package/server/lib/tts/service.js +162 -0
  151. package/server/lib/tts/summarization.js +171 -0
  152. package/server/lib/tunnels/index.js +166 -0
  153. package/server/lib/tunnels/providers/cloudflare.js +260 -0
  154. package/server/lib/tunnels/registry.js +51 -0
  155. package/server/lib/tunnels/types.js +219 -0
  156. package/server/lib/utils/lru.js +107 -0
  157. package/server/lib/utils/sse.js +121 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Quota Providers Registry
3
+ *
4
+ * Implements quota fetching for various AI providers using a registry pattern.
5
+ * @module quota/providers
6
+ */
7
+
8
+ import { buildResult } from '../utils/index.js';
9
+
10
+ import * as claude from './claude.js';
11
+ import * as codex from './codex.js';
12
+ import * as copilot from './copilot.js';
13
+ import * as google from './google/index.js';
14
+ import * as kimi from './kimi.js';
15
+ import * as nanogpt from './nanogpt.js';
16
+ import * as openai from './openai.js';
17
+ import * as openrouter from './openrouter.js';
18
+ import * as zai from './zai.js';
19
+ import * as minimaxCodingPlan from './minimax-coding-plan.js';
20
+ import * as minimaxCnCodingPlan from './minimax-cn-coding-plan.js';
21
+ import * as ollamaCloud from './ollama-cloud.js';
22
+
23
+ const registry = {
24
+ claude: {
25
+ providerId: claude.providerId,
26
+ providerName: claude.providerName,
27
+ isConfigured: claude.isConfigured,
28
+ fetchQuota: claude.fetchQuota
29
+ },
30
+ codex: {
31
+ providerId: codex.providerId,
32
+ providerName: codex.providerName,
33
+ isConfigured: codex.isConfigured,
34
+ fetchQuota: codex.fetchQuota
35
+ },
36
+ google: {
37
+ providerId: 'google',
38
+ providerName: 'Google',
39
+ isConfigured: () => google.resolveGoogleAuthSources().length > 0,
40
+ fetchQuota: google.fetchGoogleQuota
41
+ },
42
+ 'zai-coding-plan': {
43
+ providerId: zai.providerId,
44
+ providerName: zai.providerName,
45
+ isConfigured: zai.isConfigured,
46
+ fetchQuota: zai.fetchQuota
47
+ },
48
+ 'kimi-for-coding': {
49
+ providerId: kimi.providerId,
50
+ providerName: kimi.providerName,
51
+ isConfigured: kimi.isConfigured,
52
+ fetchQuota: kimi.fetchQuota
53
+ },
54
+ openrouter: {
55
+ providerId: openrouter.providerId,
56
+ providerName: openrouter.providerName,
57
+ isConfigured: openrouter.isConfigured,
58
+ fetchQuota: openrouter.fetchQuota
59
+ },
60
+ 'nano-gpt': {
61
+ providerId: nanogpt.providerId,
62
+ providerName: nanogpt.providerName,
63
+ isConfigured: nanogpt.isConfigured,
64
+ fetchQuota: nanogpt.fetchQuota
65
+ },
66
+ 'github-copilot': {
67
+ providerId: copilot.providerId,
68
+ providerName: copilot.providerName,
69
+ isConfigured: copilot.isConfigured,
70
+ fetchQuota: copilot.fetchQuota
71
+ },
72
+ 'github-copilot-addon': {
73
+ providerId: copilot.providerIdAddon,
74
+ providerName: copilot.providerNameAddon,
75
+ isConfigured: copilot.isConfigured,
76
+ fetchQuota: copilot.fetchQuotaAddon
77
+ },
78
+ 'minimax-coding-plan': {
79
+ providerId: minimaxCodingPlan.providerId,
80
+ providerName: minimaxCodingPlan.providerName,
81
+ isConfigured: minimaxCodingPlan.isConfigured,
82
+ fetchQuota: minimaxCodingPlan.fetchQuota
83
+ },
84
+ 'minimax-cn-coding-plan': {
85
+ providerId: minimaxCnCodingPlan.providerId,
86
+ providerName: minimaxCnCodingPlan.providerName,
87
+ isConfigured: minimaxCnCodingPlan.isConfigured,
88
+ fetchQuota: minimaxCnCodingPlan.fetchQuota
89
+ },
90
+ 'ollama-cloud': {
91
+ providerId: ollamaCloud.providerId,
92
+ providerName: ollamaCloud.providerName,
93
+ isConfigured: ollamaCloud.isConfigured,
94
+ fetchQuota: ollamaCloud.fetchQuota
95
+ }
96
+ };
97
+
98
+ export const listConfiguredQuotaProviders = () => {
99
+ const configured = [];
100
+
101
+ for (const [id, provider] of Object.entries(registry)) {
102
+ try {
103
+ if (provider.isConfigured()) {
104
+ configured.push(id);
105
+ }
106
+ } catch {
107
+ // Ignore provider-specific config errors in list API.
108
+ }
109
+ }
110
+
111
+ return configured;
112
+ };
113
+
114
+ export const fetchQuotaForProvider = async (providerId) => {
115
+ const provider = registry[providerId];
116
+
117
+ if (!provider) {
118
+ return buildResult({
119
+ providerId,
120
+ providerName: providerId,
121
+ ok: false,
122
+ configured: false,
123
+ error: 'Unsupported provider'
124
+ });
125
+ }
126
+
127
+ try {
128
+ return await provider.fetchQuota();
129
+ } catch (error) {
130
+ return buildResult({
131
+ providerId: provider.providerId,
132
+ providerName: provider.providerName,
133
+ ok: false,
134
+ configured: true,
135
+ error: error instanceof Error ? error.message : 'Request failed'
136
+ });
137
+ }
138
+ };
139
+
140
+ export const fetchClaudeQuota = claude.fetchQuota;
141
+ export const fetchOpenaiQuota = openai.fetchQuota;
142
+ export const fetchGoogleQuota = google.fetchGoogleQuota;
143
+ export const fetchCodexQuota = codex.fetchQuota;
144
+ export const fetchCopilotQuota = copilot.fetchQuota;
145
+ export const fetchCopilotAddonQuota = copilot.fetchQuotaAddon;
146
+ export const fetchKimiQuota = kimi.fetchQuota;
147
+ export const fetchOpenRouterQuota = openrouter.fetchQuota;
148
+ export const fetchZaiQuota = zai.fetchQuota;
149
+ export const fetchNanoGptQuota = nanogpt.fetchQuota;
150
+ export const fetchMinimaxCodingPlanQuota = minimaxCodingPlan.fetchQuota;
151
+ export const fetchMinimaxCnCodingPlanQuota = minimaxCnCodingPlan.fetchQuota;
152
+ export const fetchOllamaCloudQuota = ollamaCloud.fetchQuota;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Quota Provider Interface
3
+ *
4
+ * Defines the contract for implementing quota providers.
5
+ * @module quota/providers
6
+ */
7
+
8
+ /**
9
+ * @typedef {Object} UsageWindow
10
+ * @property {number|null} usedPercent - Percentage of usage (0-100)
11
+ * @property {number|null} remainingPercent - Percentage remaining (0-100)
12
+ * @property {number|null} windowSeconds - Window duration in seconds
13
+ * @property {number|null} resetAfterSeconds - Seconds until reset
14
+ * @property {number|null} resetAt - Unix timestamp when quota resets
15
+ * @property {string|null} resetAtFormatted - Human-readable reset time
16
+ * @property {string|null} resetAfterFormatted - Human-readable time until reset
17
+ * @property {string|null} valueLabel - Optional label for display (e.g., "$10.00 remaining")
18
+ */
19
+
20
+ /**
21
+ * @typedef {Object} ProviderUsage
22
+ * @property {Object.<string, UsageWindow>} windows - Usage windows by key (e.g., '5h', '7d', 'daily')
23
+ * @property {Object.<string, Object>} [models] - Model-specific usage (provider-specific)
24
+ */
25
+
26
+ /**
27
+ * @typedef {Object} QuotaProviderResult
28
+ * @property {string} providerId - Unique identifier for the provider
29
+ * @property {string} providerName - Display name for the provider
30
+ * @property {boolean} ok - Whether the fetch was successful
31
+ * @property {boolean} configured - Whether the provider is configured
32
+ * @property {ProviderUsage|null} usage - Usage data if successful
33
+ * @property {string|null} [error] - Error message if not successful
34
+ * @property {number} fetchedAt - Unix timestamp when the result was fetched
35
+ */
36
+
37
+ /**
38
+ * @typedef {Function} ProviderQuotaFetcher
39
+ * @returns {Promise<QuotaProviderResult>}
40
+ */
41
+
42
+ /**
43
+ * @typedef {Function} ProviderConfigurationChecker
44
+ * @param {Object.<string, unknown>} [auth]
45
+ * @returns {boolean}
46
+ */
47
+
48
+ /**
49
+ * @typedef {Object} QuotaProvider
50
+ * @property {string} providerId
51
+ * @property {string} providerName
52
+ * @property {string[]} aliases
53
+ * @property {ProviderConfigurationChecker} isConfigured
54
+ * @property {ProviderQuotaFetcher} fetchQuota
55
+ */
@@ -0,0 +1,108 @@
1
+ import { readAuthFile } from '../../opencode/auth.js';
2
+ import {
3
+ getAuthEntry,
4
+ normalizeAuthEntry,
5
+ buildResult,
6
+ toUsageWindow,
7
+ toNumber,
8
+ toTimestamp,
9
+ durationToLabel,
10
+ durationToSeconds
11
+ } from '../utils/index.js';
12
+
13
+ export const providerId = 'kimi-for-coding';
14
+ export const providerName = 'Kimi for Coding';
15
+ export const aliases = ['kimi-for-coding', 'kimi'];
16
+
17
+ export const isConfigured = () => {
18
+ const auth = readAuthFile();
19
+ const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
20
+ return Boolean(entry?.key || entry?.token);
21
+ };
22
+
23
+ export const fetchQuota = async () => {
24
+ const auth = readAuthFile();
25
+ const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
26
+ const apiKey = entry?.key ?? entry?.token;
27
+
28
+ if (!apiKey) {
29
+ return buildResult({
30
+ providerId,
31
+ providerName,
32
+ ok: false,
33
+ configured: false,
34
+ error: 'Not configured'
35
+ });
36
+ }
37
+
38
+ try {
39
+ const response = await fetch('https://api.kimi.com/coding/v1/usages', {
40
+ method: 'GET',
41
+ headers: {
42
+ Authorization: `Bearer ${apiKey}`,
43
+ 'Content-Type': 'application/json'
44
+ }
45
+ });
46
+
47
+ if (!response.ok) {
48
+ return buildResult({
49
+ providerId,
50
+ providerName,
51
+ ok: false,
52
+ configured: true,
53
+ error: `API error: ${response.status}`
54
+ });
55
+ }
56
+
57
+ const payload = await response.json();
58
+ const windows = {};
59
+ const usage = payload?.usage ?? null;
60
+ if (usage) {
61
+ const limit = toNumber(usage.limit);
62
+ const remaining = toNumber(usage.remaining);
63
+ const usedPercent = limit && remaining !== null
64
+ ? Math.max(0, Math.min(100, 100 - (remaining / limit) * 100))
65
+ : null;
66
+ windows.weekly = toUsageWindow({
67
+ usedPercent,
68
+ windowSeconds: null,
69
+ resetAt: toTimestamp(usage.resetTime)
70
+ });
71
+ }
72
+
73
+ const limits = Array.isArray(payload?.limits) ? payload.limits : [];
74
+ for (const limit of limits) {
75
+ const window = limit?.window;
76
+ const detail = limit?.detail;
77
+ const rawLabel = durationToLabel(window?.duration, window?.timeUnit);
78
+ const windowSeconds = durationToSeconds(window?.duration, window?.timeUnit);
79
+ const label = windowSeconds === 5 * 60 * 60 ? `Rate Limit (${rawLabel})` : rawLabel;
80
+ const total = toNumber(detail?.limit);
81
+ const remaining = toNumber(detail?.remaining);
82
+ const usedPercent = total && remaining !== null
83
+ ? Math.max(0, Math.min(100, 100 - (remaining / total) * 100))
84
+ : null;
85
+ windows[label] = toUsageWindow({
86
+ usedPercent,
87
+ windowSeconds,
88
+ resetAt: toTimestamp(detail?.resetTime)
89
+ });
90
+ }
91
+
92
+ return buildResult({
93
+ providerId,
94
+ providerName,
95
+ ok: true,
96
+ configured: true,
97
+ usage: { windows }
98
+ });
99
+ } catch (error) {
100
+ return buildResult({
101
+ providerId,
102
+ providerName,
103
+ ok: false,
104
+ configured: true,
105
+ error: error instanceof Error ? error.message : 'Request failed'
106
+ });
107
+ }
108
+ };
@@ -0,0 +1,15 @@
1
+ // MiniMax Coding Plan Provider
2
+ import { createMiniMaxCodingPlanProvider } from './minimax-shared.js';
3
+
4
+ const provider = createMiniMaxCodingPlanProvider({
5
+ providerId: 'minimax-cn-coding-plan',
6
+ providerName: 'MiniMax Coding Plan (minimaxi.com)',
7
+ aliases: ['minimax-cn-coding-plan'],
8
+ endpoint: 'https://www.minimaxi.com/v1/api/openplatform/coding_plan/remains',
9
+ });
10
+
11
+ export const providerId = provider.providerId;
12
+ export const providerName = provider.providerName;
13
+ export const aliases = provider.aliases;
14
+ export const isConfigured = provider.isConfigured;
15
+ export const fetchQuota = provider.fetchQuota;
@@ -0,0 +1,15 @@
1
+ // MiniMax Coding Plan Provider
2
+ import { createMiniMaxCodingPlanProvider } from './minimax-shared.js';
3
+
4
+ const provider = createMiniMaxCodingPlanProvider({
5
+ providerId: 'minimax-coding-plan',
6
+ providerName: 'MiniMax Coding Plan (minimax.io)',
7
+ aliases: ['minimax-coding-plan'],
8
+ endpoint: 'https://www.minimax.io/v1/api/openplatform/coding_plan/remains',
9
+ });
10
+
11
+ export const providerId = provider.providerId;
12
+ export const providerName = provider.providerName;
13
+ export const aliases = provider.aliases;
14
+ export const isConfigured = provider.isConfigured;
15
+ export const fetchQuota = provider.fetchQuota;
@@ -0,0 +1,136 @@
1
+ import { readAuthFile } from '../../opencode/auth.js';
2
+ import {
3
+ getAuthEntry,
4
+ normalizeAuthEntry,
5
+ buildResult,
6
+ toUsageWindow,
7
+ toNumber,
8
+ toTimestamp,
9
+ } from '../utils/index.js';
10
+
11
+ export const createMiniMaxCodingPlanProvider = ({ providerId, providerName, aliases, endpoint }) => {
12
+ const isConfigured = () => {
13
+ const auth = readAuthFile();
14
+ const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
15
+ return Boolean(entry?.key || entry?.token);
16
+ };
17
+
18
+ const fetchQuota = async () => {
19
+ const auth = readAuthFile();
20
+ const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
21
+ const apiKey = entry?.key ?? entry?.token;
22
+
23
+ if (!apiKey) {
24
+ return buildResult({
25
+ providerId,
26
+ providerName,
27
+ ok: false,
28
+ configured: false,
29
+ error: 'Not configured',
30
+ });
31
+ }
32
+
33
+ try {
34
+ const response = await fetch(endpoint, {
35
+ method: 'GET',
36
+ headers: {
37
+ Authorization: `Bearer ${apiKey}`,
38
+ 'Content-Type': 'application/json',
39
+ },
40
+ });
41
+
42
+ if (!response.ok) {
43
+ return buildResult({
44
+ providerId,
45
+ providerName,
46
+ ok: false,
47
+ configured: true,
48
+ error: `API error: ${response.status}`,
49
+ });
50
+ }
51
+
52
+ const payload = await response.json();
53
+ const baseResp = payload?.base_resp;
54
+ if (baseResp && baseResp.status_code !== 0) {
55
+ return buildResult({
56
+ providerId,
57
+ providerName,
58
+ ok: false,
59
+ configured: true,
60
+ error: baseResp.status_msg || `API error: ${baseResp.status_code}`,
61
+ });
62
+ }
63
+
64
+ const firstModel = payload?.model_remains?.[0];
65
+ if (!firstModel) {
66
+ return buildResult({
67
+ providerId,
68
+ providerName,
69
+ ok: false,
70
+ configured: true,
71
+ error: 'No model quota data available',
72
+ });
73
+ }
74
+
75
+ const intervalTotal = toNumber(firstModel.current_interval_total_count);
76
+ const intervalUsage = toNumber(firstModel.current_interval_usage_count);
77
+ const intervalStartAt = toTimestamp(firstModel.start_time);
78
+ const intervalResetAt = toTimestamp(firstModel.end_time);
79
+ const weeklyTotal = toNumber(firstModel.current_weekly_total_count);
80
+ const weeklyUsage = toNumber(firstModel.current_weekly_usage_count);
81
+ const weeklyStartAt = toTimestamp(firstModel.weekly_start_time);
82
+ const weeklyResetAt = toTimestamp(firstModel.weekly_end_time);
83
+ const intervalUsed = intervalTotal - intervalUsage;
84
+ const weeklyUsed = weeklyTotal - weeklyUsage;
85
+ const intervalUsedPercent =
86
+ intervalTotal > 0 ? Math.max(0, Math.min(100, (intervalUsed / intervalTotal) * 100)) : null;
87
+ const intervalWindowSeconds =
88
+ intervalStartAt && intervalResetAt && intervalResetAt > intervalStartAt
89
+ ? Math.floor((intervalResetAt - intervalStartAt) / 1000)
90
+ : null;
91
+ const weeklyUsedPercent =
92
+ weeklyTotal > 0 ? Math.max(0, Math.min(100, (weeklyUsed / weeklyTotal) * 100)) : null;
93
+ const weeklyWindowSeconds =
94
+ weeklyStartAt && weeklyResetAt && weeklyResetAt > weeklyStartAt
95
+ ? Math.floor((weeklyResetAt - weeklyStartAt) / 1000)
96
+ : null;
97
+
98
+ const windows = {
99
+ '5h': toUsageWindow({
100
+ usedPercent: intervalUsedPercent,
101
+ windowSeconds: intervalWindowSeconds,
102
+ resetAt: intervalResetAt,
103
+ }),
104
+ weekly: toUsageWindow({
105
+ usedPercent: weeklyUsedPercent,
106
+ windowSeconds: weeklyWindowSeconds,
107
+ resetAt: weeklyResetAt,
108
+ }),
109
+ };
110
+
111
+ return buildResult({
112
+ providerId,
113
+ providerName,
114
+ ok: true,
115
+ configured: true,
116
+ usage: { windows },
117
+ });
118
+ } catch (error) {
119
+ return buildResult({
120
+ providerId,
121
+ providerName,
122
+ ok: false,
123
+ configured: true,
124
+ error: error instanceof Error ? error.message : 'Request failed',
125
+ });
126
+ }
127
+ };
128
+
129
+ return {
130
+ providerId,
131
+ providerName,
132
+ aliases,
133
+ isConfigured,
134
+ fetchQuota,
135
+ };
136
+ };
@@ -0,0 +1,124 @@
1
+ import { readAuthFile } from '../../opencode/auth.js';
2
+ import {
3
+ getAuthEntry,
4
+ normalizeAuthEntry,
5
+ buildResult,
6
+ toUsageWindow,
7
+ toNumber,
8
+ toTimestamp
9
+ } from '../utils/index.js';
10
+
11
+ const NANO_GPT_DAILY_WINDOW_SECONDS = 86400;
12
+
13
+ export const providerId = 'nano-gpt';
14
+ export const providerName = 'NanoGPT';
15
+ export const aliases = ['nano-gpt', 'nanogpt', 'nano_gpt'];
16
+
17
+ export const isConfigured = () => {
18
+ const auth = readAuthFile();
19
+ const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
20
+ return Boolean(entry?.key || entry?.token);
21
+ };
22
+
23
+ export const fetchQuota = async () => {
24
+ const auth = readAuthFile();
25
+ const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
26
+ const apiKey = entry?.key ?? entry?.token;
27
+
28
+ if (!apiKey) {
29
+ return buildResult({
30
+ providerId,
31
+ providerName,
32
+ ok: false,
33
+ configured: false,
34
+ error: 'Not configured'
35
+ });
36
+ }
37
+
38
+ try {
39
+ const response = await fetch('https://nano-gpt.com/api/subscription/v1/usage', {
40
+ method: 'GET',
41
+ headers: {
42
+ Authorization: `Bearer ${apiKey}`,
43
+ 'Content-Type': 'application/json'
44
+ }
45
+ });
46
+
47
+ if (!response.ok) {
48
+ return buildResult({
49
+ providerId,
50
+ providerName,
51
+ ok: false,
52
+ configured: true,
53
+ error: `API error: ${response.status}`
54
+ });
55
+ }
56
+
57
+ const payload = await response.json();
58
+ const windows = {};
59
+ const period = payload?.period ?? null;
60
+ const daily = payload?.daily ?? null;
61
+ const monthly = payload?.monthly ?? null;
62
+ const state = payload?.state ?? 'active';
63
+
64
+ if (daily) {
65
+ let usedPercent = null;
66
+ const percentUsed = daily?.percentUsed;
67
+ if (typeof percentUsed === 'number') {
68
+ usedPercent = Math.max(0, Math.min(100, percentUsed * 100));
69
+ } else {
70
+ const used = toNumber(daily?.used);
71
+ const limit = toNumber(daily?.limit ?? daily?.limits?.daily);
72
+ if (used !== null && limit !== null && limit > 0) {
73
+ usedPercent = Math.max(0, Math.min(100, (used / limit) * 100));
74
+ }
75
+ }
76
+ const resetAt = toTimestamp(daily?.resetAt);
77
+ const valueLabel = state !== 'active' ? `(${state})` : null;
78
+ windows['daily'] = toUsageWindow({
79
+ usedPercent,
80
+ windowSeconds: NANO_GPT_DAILY_WINDOW_SECONDS,
81
+ resetAt,
82
+ valueLabel
83
+ });
84
+ }
85
+
86
+ if (monthly) {
87
+ let usedPercent = null;
88
+ const percentUsed = monthly?.percentUsed;
89
+ if (typeof percentUsed === 'number') {
90
+ usedPercent = Math.max(0, Math.min(100, percentUsed * 100));
91
+ } else {
92
+ const used = toNumber(monthly?.used);
93
+ const limit = toNumber(monthly?.limit ?? monthly?.limits?.monthly);
94
+ if (used !== null && limit !== null && limit > 0) {
95
+ usedPercent = Math.max(0, Math.min(100, (used / limit) * 100));
96
+ }
97
+ }
98
+ const resetAt = toTimestamp(monthly?.resetAt ?? period?.currentPeriodEnd);
99
+ const valueLabel = state !== 'active' ? `(${state})` : null;
100
+ windows['monthly'] = toUsageWindow({
101
+ usedPercent,
102
+ windowSeconds: null,
103
+ resetAt,
104
+ valueLabel
105
+ });
106
+ }
107
+
108
+ return buildResult({
109
+ providerId,
110
+ providerName,
111
+ ok: true,
112
+ configured: true,
113
+ usage: { windows }
114
+ });
115
+ } catch (error) {
116
+ return buildResult({
117
+ providerId,
118
+ providerName,
119
+ ok: false,
120
+ configured: true,
121
+ error: error instanceof Error ? error.message : 'Request failed'
122
+ });
123
+ }
124
+ };