termdock 1.4.39 → 1.4.40
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/dist/client/assets/{abnfDiagram-VRR7QNED-CnSBvlEl.js → abnfDiagram-VRR7QNED-1BbN_Bdc.js} +1 -1
- package/dist/client/assets/{arc-CALnqP2n.js → arc-B4X31X3m.js} +1 -1
- package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-Cdm6dAQd.js → architectureDiagram-ZJ3FMSHR-BlauOFgG.js} +1 -1
- package/dist/client/assets/{blockDiagram-677ZJIJ3-DtmrJpi1.js → blockDiagram-677ZJIJ3-B0mtlYc1.js} +1 -1
- package/dist/client/assets/{c4Diagram-LMCZKHZV-DUfPhQWl.js → c4Diagram-LMCZKHZV-B52qGtrK.js} +1 -1
- package/dist/client/assets/channel-D2AA77kQ.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-Bw9S69zo.js → chunk-2Q5K7J3B-TkkyEbTr.js} +1 -1
- package/dist/client/assets/{chunk-32BRIVSS-kA50Go3u.js → chunk-32BRIVSS-rmkLGfWN.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-ott1soDa.js → chunk-5VM5RSS4-CifeRLjF.js} +1 -1
- package/dist/client/assets/{chunk-EX3LRPZG-Dt9wOwBs.js → chunk-EX3LRPZG-B_bint1e.js} +1 -1
- package/dist/client/assets/{chunk-JWPE2WC7-C30-Z0yw.js → chunk-JWPE2WC7-DOZwisqR.js} +1 -1
- package/dist/client/assets/{chunk-MOJQB5TN-B1mAX1TP.js → chunk-MOJQB5TN-BEWrO_py.js} +1 -1
- package/dist/client/assets/{chunk-RYQCIY6F-B8ysyDpM.js → chunk-RYQCIY6F-DM20-r1x.js} +1 -1
- package/dist/client/assets/{chunk-V7JOEXUC-DQ_-bi4y.js → chunk-V7JOEXUC-06fOLFGY.js} +1 -1
- package/dist/client/assets/{chunk-VR4S4FIN-B4D6dY6b.js → chunk-VR4S4FIN-C9ivxt4F.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-jyClBYKv.js → chunk-XXDRQBXY-BU98Kf52.js} +1 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-DFpEzsId.js +1 -0
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-DFpEzsId.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-CfK-PvOQ.js → cose-bilkent-JH36ORCC-036xKocd.js} +1 -1
- package/dist/client/assets/{cynefin-VYW2F7L2-C9B5lUbv.js → cynefin-VYW2F7L2-Nqf06Kmu.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-TSTJHNR4-DEVoE6FG.js → cynefinDiagram-TSTJHNR4-Qa8iUoZZ.js} +1 -1
- package/dist/client/assets/{dagre-VKFMJZFB-qmfg5oNl.js → dagre-VKFMJZFB-q5f0Dltl.js} +1 -1
- package/dist/client/assets/{diagram-FQU43EPY-BFiSSiki.js → diagram-FQU43EPY-CIVowYlH.js} +1 -1
- package/dist/client/assets/{diagram-G47NLZAW-DCyYqHvi.js → diagram-G47NLZAW-Dv1vCJ3c.js} +1 -1
- package/dist/client/assets/{diagram-NH7WQ7WH-BW8VNzAI.js → diagram-NH7WQ7WH-lLyO_C5q.js} +1 -1
- package/dist/client/assets/{diagram-OA4YK3LP-D6nchqb6.js → diagram-OA4YK3LP-3cNxfhEy.js} +1 -1
- package/dist/client/assets/{diagram-WEI45ONY-ov91AGGg.js → diagram-WEI45ONY-BLRqKjFK.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-CCIWWBDH-Bpxswjdx.js → ebnfDiagram-CCIWWBDH-CsSbfhPV.js} +1 -1
- package/dist/client/assets/{erDiagram-Q63AITRT-afC1trBs.js → erDiagram-Q63AITRT-Dx9mks1A.js} +1 -1
- package/dist/client/assets/{flowDiagram-23GEKE2U-CT0Zgy4g.js → flowDiagram-23GEKE2U-CqDiBbzG.js} +1 -1
- package/dist/client/assets/{ganttDiagram-NO4QXBWP-DwaUqtjy.js → ganttDiagram-NO4QXBWP-Ch-l1-IG.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-THiUXP7D.js → gitGraphDiagram-IHSO6WYX-CRhTdG6q.js} +1 -1
- package/dist/client/assets/index-B2_LqlRR.js +430 -0
- package/dist/client/assets/index-BQkNIHH4.css +1 -0
- package/dist/client/assets/{infoDiagram-FWYZ7A6U-B9HtN83k.js → infoDiagram-FWYZ7A6U-B3fKiraO.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-Bv9MGIw7.js → ishikawaDiagram-FXEZZL3T-DBG_zMtG.js} +1 -1
- package/dist/client/assets/{journeyDiagram-5HDEW3XC-BHF_IOk8.js → journeyDiagram-5HDEW3XC-Bl1jwFh0.js} +1 -1
- package/dist/client/assets/{kanban-definition-HUTT4EX6-BnXI6zKI.js → kanban-definition-HUTT4EX6-D0bYnuMX.js} +1 -1
- package/dist/client/assets/{linear-D5-uPtP1.js → linear-Bqm0-Qpp.js} +1 -1
- package/dist/client/assets/{mermaid.core-C8AmiozH.js → mermaid.core-EBsM-F0c.js} +4 -4
- package/dist/client/assets/{mindmap-definition-LN4V7U3C-C07CYbmD.js → mindmap-definition-LN4V7U3C-BQin-jQj.js} +1 -1
- package/dist/client/assets/{pegDiagram-2B236MQR-CgxltL7c.js → pegDiagram-2B236MQR-HTTycFMY.js} +1 -1
- package/dist/client/assets/{pieDiagram-ENE6RG2P-C43JmmWs.js → pieDiagram-ENE6RG2P-V0Je9c4E.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-D4Dykc-0.js → quadrantDiagram-ABIIQ3AL-C3BCyDnb.js} +1 -1
- package/dist/client/assets/{railroadDiagram-RFXS5EU6-xHLG6dKk.js → railroadDiagram-RFXS5EU6-CFBxVWwn.js} +1 -1
- package/dist/client/assets/{requirementDiagram-TGXJPOKE-XO7M19hA.js → requirementDiagram-TGXJPOKE-OCuv5jiF.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-HTMAVEWB-CTOauFrk.js → sankeyDiagram-HTMAVEWB-CJDTF9-0.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-BmiBP2Ql.js → sequenceDiagram-DBY2YBRQ-DNsYn886.js} +1 -1
- package/dist/client/assets/{sizeCapture-X5ZJPWSS-DPa4M1sV.js → sizeCapture-X5ZJPWSS-UCObVrPF.js} +1 -1
- package/dist/client/assets/{stateDiagram-2N3HPSRC-B-ZG_536.js → stateDiagram-2N3HPSRC-DRVv_Dm9.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-BhnNNo6s.js +1 -0
- package/dist/client/assets/{swimlanes-5IMT3BWC-BnrujRnH.js → swimlanes-5IMT3BWC-B3pf_PPr.js} +2 -2
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-By5sWNT_.js +8 -0
- package/dist/client/assets/{timeline-definition-FHXFAJF6-B_CovjvP.js → timeline-definition-FHXFAJF6-DtXbFtyH.js} +1 -1
- package/dist/client/assets/{vennDiagram-L72KCM5P-DVZK2btB.js → vennDiagram-L72KCM5P-D5Ln-8Wo.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-EHGQE667-FXnlzV-g.js → wardleyDiagram-EHGQE667-DBg6Xz6w.js} +1 -1
- package/dist/client/assets/{xychartDiagram-FW5EYKEG-Bom661tv.js → xychartDiagram-FW5EYKEG-_x4UP6WR.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +1 -1
- package/dist/server/quota/QuotaManager.js +142 -0
- package/dist/server/quota/providers/claude.js +125 -0
- package/dist/server/quota/providers/codex.js +175 -0
- package/dist/server/quota/providers/copilot.js +125 -0
- package/dist/server/quota/providers/gemini.js +52 -0
- package/dist/server/quota/providers/grok.js +76 -0
- package/dist/server/quota/providers/kimi.js +113 -0
- package/dist/server/quota/tokenDiscovery.js +239 -0
- package/dist/server/quota/types.js +8 -0
- package/dist/server/routes/terminal.js +47 -0
- package/package.json +1 -1
- package/dist/client/assets/channel-DBje1ooC.js +0 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-UVRtMcd4.js +0 -1
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-UVRtMcd4.js +0 -1
- package/dist/client/assets/index-Dpxt3np6.js +0 -430
- package/dist/client/assets/index-d23ibKJx.css +0 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-DLJqIMm-.js +0 -1
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-BtyJTDhv.js +0 -8
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token discovery — reads auth tokens from agent configuration files.
|
|
3
|
+
*
|
|
4
|
+
* Termdock's hook installers already know where each agent stores its config.
|
|
5
|
+
* This module extracts OAuth tokens / API keys so the QuotaManager can call
|
|
6
|
+
* provider usage APIs without asking the user to re-enter credentials.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
const HOME = homedir();
|
|
12
|
+
// ── Claude / Anthropic ──────────────────────────────────────────────
|
|
13
|
+
/** Read ~/.local/share/opencode/auth.json for Anthropic OAuth tokens. */
|
|
14
|
+
function discoverFromOpenCodeAuth() {
|
|
15
|
+
const results = [];
|
|
16
|
+
const authPath = path.join(HOME, '.local/share/opencode/auth.json');
|
|
17
|
+
try {
|
|
18
|
+
const raw = fs.readFileSync(authPath, 'utf-8');
|
|
19
|
+
const auth = JSON.parse(raw);
|
|
20
|
+
const oauths = Array.isArray(auth) ? auth : ('oauth' in auth ? auth.oauth : [auth]);
|
|
21
|
+
const entries = Array.isArray(oauths) ? oauths : [oauths];
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
if (entry.type === 'anthropic' && entry.access) {
|
|
24
|
+
results.push({
|
|
25
|
+
token: entry.access,
|
|
26
|
+
source: 'opencode auth.json',
|
|
27
|
+
accountId: entry.accountId || entry.accountLabel || undefined,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// File missing or unreadable — no Anthropic tokens from this source.
|
|
34
|
+
}
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
/** Read ~/.claude/settings.json for stored credentials. */
|
|
38
|
+
function discoverFromClaudeSettings() {
|
|
39
|
+
const results = [];
|
|
40
|
+
const settingsPath = path.join(HOME, '.claude/settings.json');
|
|
41
|
+
try {
|
|
42
|
+
const raw = fs.readFileSync(settingsPath, 'utf-8');
|
|
43
|
+
const settings = JSON.parse(raw);
|
|
44
|
+
// Claude Code stores OAuth credentials in various shapes.
|
|
45
|
+
// The most reliable source is the oauth account info.
|
|
46
|
+
const oauth = settings?.oauthAccount;
|
|
47
|
+
if (oauth?.accessToken) {
|
|
48
|
+
results.push({
|
|
49
|
+
token: oauth.accessToken,
|
|
50
|
+
source: 'claude settings.json',
|
|
51
|
+
email: oauth.email || undefined,
|
|
52
|
+
accountId: oauth.accountUuid || undefined,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Not found or unreadable.
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
61
|
+
export function discoverClaudeTokens() {
|
|
62
|
+
const fromOpenCode = discoverFromOpenCodeAuth().filter((t) => t.source === 'opencode auth.json');
|
|
63
|
+
// OpenCode auth.json already covers Anthropic — avoid duplicates.
|
|
64
|
+
if (fromOpenCode.length > 0) {
|
|
65
|
+
// Merge with claude settings for email enrichment
|
|
66
|
+
const fromClaude = discoverFromClaudeSettings();
|
|
67
|
+
for (const t of fromOpenCode) {
|
|
68
|
+
const match = fromClaude.find((c) => c.email && c.token !== t.token);
|
|
69
|
+
if (match && !t.email) {
|
|
70
|
+
t.email = match.email;
|
|
71
|
+
t.accountId = match.accountId;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return fromOpenCode;
|
|
75
|
+
}
|
|
76
|
+
return discoverFromClaudeSettings();
|
|
77
|
+
}
|
|
78
|
+
// ── Codex / OpenAI ───────────────────────────────────────────────────
|
|
79
|
+
function discoverCodexFromOpenCodeAuth() {
|
|
80
|
+
const results = [];
|
|
81
|
+
const authPath = path.join(HOME, '.local/share/opencode/auth.json');
|
|
82
|
+
try {
|
|
83
|
+
const raw = fs.readFileSync(authPath, 'utf-8');
|
|
84
|
+
const auth = JSON.parse(raw);
|
|
85
|
+
const oauths = Array.isArray(auth) ? auth : ('oauth' in auth ? auth.oauth : [auth]);
|
|
86
|
+
const entries = Array.isArray(oauths) ? oauths : [oauths];
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if ((entry.type === 'openai' || entry.type === 'chatgpt' || entry.type === 'codex') && entry.access) {
|
|
89
|
+
results.push({
|
|
90
|
+
token: entry.access,
|
|
91
|
+
source: 'opencode auth.json',
|
|
92
|
+
accountId: entry.accountId || undefined,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Not found.
|
|
99
|
+
}
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
102
|
+
/** Read ~/.codex/auth.json for Codex CLI native OAuth tokens. */
|
|
103
|
+
function discoverCodexFromCodexAuth() {
|
|
104
|
+
const results = [];
|
|
105
|
+
const authPath = path.join(HOME, '.codex', 'auth.json');
|
|
106
|
+
try {
|
|
107
|
+
const raw = fs.readFileSync(authPath, 'utf-8');
|
|
108
|
+
const auth = JSON.parse(raw);
|
|
109
|
+
const tokens = auth?.tokens;
|
|
110
|
+
if (tokens?.access_token) {
|
|
111
|
+
results.push({
|
|
112
|
+
token: tokens.access_token,
|
|
113
|
+
source: 'codex auth.json',
|
|
114
|
+
accountId: tokens.account_id || undefined,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Not found.
|
|
120
|
+
}
|
|
121
|
+
return results;
|
|
122
|
+
}
|
|
123
|
+
function discoverCodexFromEnv() {
|
|
124
|
+
const results = [];
|
|
125
|
+
const envVars = ['OPENAI_API_KEY', 'CODEX_API_KEY', 'CHATGPT_TOKEN'];
|
|
126
|
+
for (const v of envVars) {
|
|
127
|
+
const val = process.env[v];
|
|
128
|
+
if (val && val.trim()) {
|
|
129
|
+
results.push({ token: val.trim(), source: `env:${v}` });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
export function discoverCodexTokens() {
|
|
135
|
+
// Priority: Codex CLI auth > OpenCode auth > env
|
|
136
|
+
const fromCodex = discoverCodexFromCodexAuth();
|
|
137
|
+
if (fromCodex.length > 0)
|
|
138
|
+
return fromCodex;
|
|
139
|
+
const fromOpenCode = discoverCodexFromOpenCodeAuth();
|
|
140
|
+
if (fromOpenCode.length > 0)
|
|
141
|
+
return fromOpenCode;
|
|
142
|
+
return discoverCodexFromEnv();
|
|
143
|
+
}
|
|
144
|
+
// ── Kimi ─────────────────────────────────────────────────────────────
|
|
145
|
+
export function discoverKimiTokens() {
|
|
146
|
+
const results = [];
|
|
147
|
+
// Check KIMI_API_KEY env
|
|
148
|
+
const envKey = process.env.KIMI_API_KEY;
|
|
149
|
+
if (envKey?.trim()) {
|
|
150
|
+
results.push({ token: envKey.trim(), source: 'env:KIMI_API_KEY' });
|
|
151
|
+
}
|
|
152
|
+
// Check ~/.kimi-code config
|
|
153
|
+
const configPath = path.join(HOME, '.kimi-code', 'config.json');
|
|
154
|
+
try {
|
|
155
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
156
|
+
const cfg = JSON.parse(raw);
|
|
157
|
+
if (cfg.apiKey) {
|
|
158
|
+
results.push({ token: cfg.apiKey, source: 'kimi-code config.json' });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Not found.
|
|
163
|
+
}
|
|
164
|
+
// Check OpenCode auth.json for kimi type
|
|
165
|
+
const authPath = path.join(HOME, '.local/share/opencode/auth.json');
|
|
166
|
+
try {
|
|
167
|
+
const raw = fs.readFileSync(authPath, 'utf-8');
|
|
168
|
+
const auth = JSON.parse(raw);
|
|
169
|
+
const entries = Array.isArray(auth) ? auth : [auth];
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
if (entry.type === 'kimi' && entry.access) {
|
|
172
|
+
results.push({ token: entry.access, source: 'opencode auth.json' });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Not found.
|
|
178
|
+
}
|
|
179
|
+
return results;
|
|
180
|
+
}
|
|
181
|
+
// ── GitHub Copilot ───────────────────────────────────────────────────
|
|
182
|
+
export function discoverCopilotTokens() {
|
|
183
|
+
const results = [];
|
|
184
|
+
// OpenCode auth.json
|
|
185
|
+
const authPath = path.join(HOME, '.local/share/opencode/auth.json');
|
|
186
|
+
try {
|
|
187
|
+
const raw = fs.readFileSync(authPath, 'utf-8');
|
|
188
|
+
const auth = JSON.parse(raw);
|
|
189
|
+
const entries = Array.isArray(auth) ? auth : [auth];
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
if ((entry.type === 'github' || entry.type === 'copilot') && entry.access) {
|
|
192
|
+
results.push({
|
|
193
|
+
token: entry.access,
|
|
194
|
+
source: 'opencode auth.json',
|
|
195
|
+
accountId: entry.accountId || entry.accountLabel || undefined,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// Not found.
|
|
202
|
+
}
|
|
203
|
+
// Copilot CLI config
|
|
204
|
+
const cliConfigPath = path.join(HOME, '.copilot', 'config.json');
|
|
205
|
+
try {
|
|
206
|
+
const raw = fs.readFileSync(cliConfigPath, 'utf-8');
|
|
207
|
+
const cfg = JSON.parse(raw);
|
|
208
|
+
if (cfg.token) {
|
|
209
|
+
results.push({ token: cfg.token, source: 'copilot config.json' });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Not found.
|
|
214
|
+
}
|
|
215
|
+
// GH_TOKEN env
|
|
216
|
+
const ghToken = process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
|
217
|
+
if (ghToken?.trim()) {
|
|
218
|
+
results.push({ token: ghToken.trim(), source: 'env:GITHUB_TOKEN' });
|
|
219
|
+
}
|
|
220
|
+
return results;
|
|
221
|
+
}
|
|
222
|
+
// ── Gemini ────────────────────────────────────────────────────────────
|
|
223
|
+
export function discoverGeminiTokens() {
|
|
224
|
+
const results = [];
|
|
225
|
+
const envKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
|
|
226
|
+
if (envKey?.trim()) {
|
|
227
|
+
results.push({ token: envKey.trim(), source: `env:GEMINI_API_KEY` });
|
|
228
|
+
}
|
|
229
|
+
return results;
|
|
230
|
+
}
|
|
231
|
+
// ── Grok ──────────────────────────────────────────────────────────────
|
|
232
|
+
export function discoverGrokTokens() {
|
|
233
|
+
const results = [];
|
|
234
|
+
const envKey = process.env.GROK_API_KEY || process.env.XAI_API_KEY;
|
|
235
|
+
if (envKey?.trim()) {
|
|
236
|
+
results.push({ token: envKey.trim(), source: `env:GROK_API_KEY` });
|
|
237
|
+
}
|
|
238
|
+
return results;
|
|
239
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscription quota tracking types.
|
|
3
|
+
*
|
|
4
|
+
* Each provider's fetch() returns a QuotaResult with usage percentage,
|
|
5
|
+
* remaining/entitlement for quota-based plans, and optional per-window
|
|
6
|
+
* breakdowns (e.g. Claude's 5h/7d windows, Codex's primary/secondary).
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -24,6 +24,7 @@ import { listAllHookAgents, refreshStaleHooksAtLaunch, installHooksForSlug, unin
|
|
|
24
24
|
import { loadPlugins, savePlugin, removePlugin, readPluginIcon, } from '../agent/plugins.js';
|
|
25
25
|
import { registerPluginAgents } from '../agent/registry.js';
|
|
26
26
|
import { notifyAgentTransition } from '../notifications/pushService.js';
|
|
27
|
+
import { getQuotaStatus, refreshQuota, startQuotaManager, } from '../quota/QuotaManager.js';
|
|
27
28
|
const router = express.Router();
|
|
28
29
|
const execFileAsync = promisify(execFile);
|
|
29
30
|
const TERMDOCK_DIR = `${os.homedir()}/.termdock`;
|
|
@@ -4300,6 +4301,52 @@ router.get('/agent-plugin-icon/:slug', (req, res) => {
|
|
|
4300
4301
|
res.send(svg);
|
|
4301
4302
|
});
|
|
4302
4303
|
// ── end Agent plugins API ──
|
|
4304
|
+
// ── Subscription quota API ──
|
|
4305
|
+
function broadcastQuotaToAll() {
|
|
4306
|
+
const status = getQuotaStatus();
|
|
4307
|
+
const payload = {
|
|
4308
|
+
type: 'quota-status',
|
|
4309
|
+
providers: status.providers,
|
|
4310
|
+
updatedAt: status.updatedAt,
|
|
4311
|
+
};
|
|
4312
|
+
const data = JSON.stringify(payload);
|
|
4313
|
+
// Send to control WS clients
|
|
4314
|
+
for (const [clientId, ws] of controlClients) {
|
|
4315
|
+
if (ws.readyState !== ws.OPEN) {
|
|
4316
|
+
controlClients.delete(clientId);
|
|
4317
|
+
continue;
|
|
4318
|
+
}
|
|
4319
|
+
try {
|
|
4320
|
+
ws.send(data);
|
|
4321
|
+
}
|
|
4322
|
+
catch {
|
|
4323
|
+
controlClients.delete(clientId);
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
// Send to per-session WS clients
|
|
4327
|
+
for (const [sessionId] of terminalSessions) {
|
|
4328
|
+
try {
|
|
4329
|
+
broadcastJsonWs(sessionId, payload);
|
|
4330
|
+
}
|
|
4331
|
+
catch { /* best-effort */ }
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
// Start quota polling — broadcast fn hooks into both WS channels
|
|
4335
|
+
startQuotaManager({ broadcast: broadcastQuotaToAll });
|
|
4336
|
+
router.get('/quota', (_req, res) => {
|
|
4337
|
+
res.json(getQuotaStatus());
|
|
4338
|
+
});
|
|
4339
|
+
router.post('/quota/refresh', async (_req, res) => {
|
|
4340
|
+
try {
|
|
4341
|
+
const status = await refreshQuota();
|
|
4342
|
+
broadcastQuotaToAll();
|
|
4343
|
+
res.json(status);
|
|
4344
|
+
}
|
|
4345
|
+
catch (e) {
|
|
4346
|
+
res.status(500).json({ error: e instanceof Error ? e.message : 'Refresh failed' });
|
|
4347
|
+
}
|
|
4348
|
+
});
|
|
4349
|
+
// ── Subscription quota API end ──
|
|
4303
4350
|
// ── Program detection config API ──
|
|
4304
4351
|
router.get('/program-detection', async (_req, res) => {
|
|
4305
4352
|
res.json(await loadProgramDetectionFromDisk());
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{U as a,C as n}from"./mermaid.core-C8AmiozH.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as t,C as e}from"./chunk-V7JOEXUC-DQ_-bi4y.js";import{b as i}from"./mermaid.core-C8AmiozH.js";import"./chunk-5VM5RSS4-ott1soDa.js";import"./chunk-XXDRQBXY-jyClBYKv.js";import"./chunk-VR4S4FIN-B4D6dY6b.js";import"./chunk-32BRIVSS-kA50Go3u.js";import"./purify.es-CC4Brkr_.js";import"./index-Dpxt3np6.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as t,C as e}from"./chunk-V7JOEXUC-DQ_-bi4y.js";import{b as i}from"./mermaid.core-C8AmiozH.js";import"./chunk-5VM5RSS4-ott1soDa.js";import"./chunk-XXDRQBXY-jyClBYKv.js";import"./chunk-VR4S4FIN-B4D6dY6b.js";import"./chunk-32BRIVSS-kA50Go3u.js";import"./purify.es-CC4Brkr_.js";import"./index-Dpxt3np6.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};
|