@hystudio/dsh-quota-pill 0.2.7 → 0.2.12
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/AGENTS.md +188 -20
- package/README.md +86 -17
- package/bin/opendesign-login.js +80 -0
- package/client.js +145 -27
- package/docs/screenshot.png +0 -0
- package/index.js +12 -4
- package/package.json +5 -3
- package/src/adapters-apikey.js +8 -3
- package/src/adapters-opendesign.js +199 -57
- package/src/autostart.js +64 -0
- package/src/credentials.js +60 -9
- package/src/http.js +45 -0
- package/src/opendesign-auth.js +95 -0
- package/src/providers.js +112 -0
- package/src/server.js +71 -55
- package/src/user-adapters.js +172 -0
package/client.js
CHANGED
|
@@ -87,9 +87,17 @@ window.__ModuleLoader__.load({
|
|
|
87
87
|
return ALL_PROVIDERS.map(([k]) => k).find((k) => m.startsWith(k + '/')) || '';
|
|
88
88
|
}
|
|
89
89
|
if (prefix === 'command-code') return 'commandcode';
|
|
90
|
+
if (prefix === 'commandcode-goat') return 'commandcode';
|
|
90
91
|
if (prefix === 'opencode-go') return 'opencode';
|
|
91
92
|
if (prefix === 'kimi-coding') return 'kimi';
|
|
92
93
|
if (prefix === 'antigravity') return 'agy';
|
|
94
|
+
// DSH ids that differ from the adapter keys. Without these the Current
|
|
95
|
+
// page cannot resolve the session's model even though the All page lists
|
|
96
|
+
// the provider (a real catalog has deepseek-account / grok /
|
|
97
|
+
// commandcode-goat / opendesign-go, not the short adapter names).
|
|
98
|
+
if (prefix === 'deepseek-account') return 'deepseek';
|
|
99
|
+
if (prefix === 'opendesign-go') return 'opendesign';
|
|
100
|
+
if (prefix === 'grok') return 'xai';
|
|
93
101
|
if (prefix === 'kimi' || m.startsWith('kimi') || m.startsWith('k3')) return 'kimi';
|
|
94
102
|
if (prefix === 'xai' || m.includes('grok')) return 'xai';
|
|
95
103
|
if (prefix === 'agy' || m.includes('gemini')) return 'agy';
|
|
@@ -158,6 +166,75 @@ window.__ModuleLoader__.load({
|
|
|
158
166
|
return hit ? hit[1] : (EXTRA_PROVIDER_NAMES[key] || String(key || ''));
|
|
159
167
|
}
|
|
160
168
|
|
|
169
|
+
/**
|
|
170
|
+
* 当前 provider 未被识别时,给新会话的 agent 的完整适配提示词。
|
|
171
|
+
* 用户点「复制提示词」后只需在新会话粘贴,不用自己描述上下文。
|
|
172
|
+
*/
|
|
173
|
+
function adaptationPrompt(model, provider, lang) {
|
|
174
|
+
const m = String(model || '(unknown)');
|
|
175
|
+
const p = String(provider && provider !== 'unknown' ? provider : 'unknown');
|
|
176
|
+
if (lang === 'zh') {
|
|
177
|
+
return [
|
|
178
|
+
'你是 DSH quota-pill 的 provider 适配 agent。当前模型 ' + m + ' 的 provider(' + p + ')未被插件识别,读取不到余额。',
|
|
179
|
+
'',
|
|
180
|
+
'请按 AGENTS.md 的 "Adding a new provider" 流程完成完整适配(不要修改 npm 包内文件,用户适配器会随插件更新保留):',
|
|
181
|
+
'1. 运行 curl -s "http://127.0.0.1:8319/setup?fresh=1",读取 catalog、unadaptedCatalog、userAdapters,确认未知的 DSH provider id。',
|
|
182
|
+
'2. 阅读 https://github.com/598934321/dsh-quota-pill/blob/main/AGENTS.md 和 src/user-adapters.js 的契约。',
|
|
183
|
+
'3. 找到该 provider 真实的额度 API 和凭据(官方 console/CLI/API;推理 key 不等于额度 key;不要猜数字)。',
|
|
184
|
+
'4. 在 ~/.dsh/quota-pill/adapters/<id>.mjs 新建用户适配器,导出 provider、catalogIds、displayName、credential、read(ctx);read 返回 subscription-quota / payg / usage-only / none / probe-failed 标准结构,可复用 ctx.readRef、ctx.readFirstRef、ctx.getJson、ctx.getJsonViaCurl。',
|
|
185
|
+
'5. 用 curl "http://127.0.0.1:8319/quota?model=' + encodeURIComponent(m) + '&fresh=1" 验证;失败要说明缺什么,绝不伪造读数。',
|
|
186
|
+
'6. 若需要凭据,只向用户要该 provider 的凭据;完成后运行 /quota/all?fresh=1 与 /setup?fresh=1,报告已适配/未适配的 provider。',
|
|
187
|
+
].join('\n');
|
|
188
|
+
}
|
|
189
|
+
return [
|
|
190
|
+
'You are the provider-adaptation agent for DSH quota-pill. The current model ' + m + ' uses a provider (' + p + ') the plugin does not recognize, so no quota can be read.',
|
|
191
|
+
'',
|
|
192
|
+
'Follow the "Adding a new provider" flow in AGENTS.md (do not edit files inside the npm package; user adapters survive plugin updates):',
|
|
193
|
+
'1. Run curl -s "http://127.0.0.1:8319/setup?fresh=1"; inspect catalog, unadaptedCatalog and userAdapters to identify the unknown DSH provider id.',
|
|
194
|
+
'2. Read https://github.com/598934321/dsh-quota-pill/blob/main/AGENTS.md and the contract in src/user-adapters.js.',
|
|
195
|
+
'3. Find that provider\'s real quota API and credential (official console/CLI/API; an inference key is not a quota key; never invent numbers).',
|
|
196
|
+
'4. Create ~/.dsh/quota-pill/adapters/<id>.mjs exporting provider, catalogIds, displayName, credential, read(ctx). read() returns the standard subscription-quota / payg / usage-only / none / probe-failed shape and may use ctx.readRef, ctx.readFirstRef, ctx.getJson, ctx.getJsonViaCurl.',
|
|
197
|
+
'5. Verify with curl "http://127.0.0.1:8319/quota?model=' + encodeURIComponent(m) + '&fresh=1"; if it fails, say exactly what is missing and do not fake a reading.',
|
|
198
|
+
'6. If a credential is needed, ask the user only for that provider\'s credential; then run /quota/all?fresh=1 and /setup?fresh=1 and report adapted/unadapted providers.',
|
|
199
|
+
].join('\n');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function copyToClipboard(text) {
|
|
203
|
+
try {
|
|
204
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
205
|
+
return navigator.clipboard.writeText(text);
|
|
206
|
+
}
|
|
207
|
+
} catch {}
|
|
208
|
+
try {
|
|
209
|
+
const el = document.createElement('textarea');
|
|
210
|
+
el.value = text;
|
|
211
|
+
el.setAttribute('readonly', '');
|
|
212
|
+
el.style.position = 'fixed';
|
|
213
|
+
el.style.opacity = '0';
|
|
214
|
+
document.body.appendChild(el);
|
|
215
|
+
el.select();
|
|
216
|
+
if (document.execCommand) document.execCommand('copy');
|
|
217
|
+
document.body.removeChild(el);
|
|
218
|
+
} catch {}
|
|
219
|
+
return Promise.resolve();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function isUnrecognizedProvider(data) {
|
|
223
|
+
return !!(data && (data.provider === 'unknown' || /no adapter matches/i.test(String(data.error || ''))));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function adaptHintBlock(data, model, lang) {
|
|
227
|
+
const prompt = adaptationPrompt(model, data && data.provider, lang);
|
|
228
|
+
return h('div', { className: 'quota-pill-adapt' },
|
|
229
|
+
h('div', { className: 'quota-pill-adapt-text' }, pick(lang, 'adaptHint')),
|
|
230
|
+
h('button', {
|
|
231
|
+
className: 'quota-pill-adapt-copy',
|
|
232
|
+
type: 'button',
|
|
233
|
+
onClick: () => copyToClipboard(prompt),
|
|
234
|
+
}, pick(lang, 'copyPrompt')),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
161
238
|
// ------------------------------------------------- 语言(跟随 DSH 的语言设置)
|
|
162
239
|
// DSH 的 @deepseek-ai/dsh-client-locale 会把活动语言写到 <html lang>。
|
|
163
240
|
// 读它即可跟随系统设置,并用 MutationObserver 订阅——切语言时无需刷新。
|
|
@@ -204,6 +281,8 @@ window.__ModuleLoader__.load({
|
|
|
204
281
|
resetDayHour: (d, h) => [`${d} 天 ${h} 小时后重置`, `Resets in ${d}d ${h}h`],
|
|
205
282
|
bodyProbeFailed: ['该模型当前不可用(可用性探测失败)。', 'This model is unavailable (availability probe failed).'],
|
|
206
283
|
bodyNoApi: ['该模型暂无可查额度接口。', 'No queryable quota API for this model.'],
|
|
284
|
+
adaptHint: ['无法识别该 provider 的余额。请复制提示词到新会话,让 agent 完成适配。', 'This provider is not recognized. Copy the prompt into a new session and ask an agent to adapt it.'],
|
|
285
|
+
copyPrompt: ['复制提示词', 'Copy prompt'],
|
|
207
286
|
querying: ['查询中…', 'Querying…'],
|
|
208
287
|
underThreshold: ['不足 0.1%', '<0.1%'],
|
|
209
288
|
turnsTotal: (n) => [`(${n} 轮合计)`, ` (${n} turns)`],
|
|
@@ -513,29 +592,28 @@ window.__ModuleLoader__.load({
|
|
|
513
592
|
credits: null,
|
|
514
593
|
}];
|
|
515
594
|
}
|
|
516
|
-
// Prepaid wallets (official DeepSeek):
|
|
517
|
-
//
|
|
518
|
-
//
|
|
595
|
+
// Prepaid wallets (official DeepSeek): the balance is the reading, and
|
|
596
|
+
// the optional bonus credit is its secondary line. Keeping them as two
|
|
597
|
+
// window rows inherited the window-row shape — a reserved (empty) reset
|
|
598
|
+
// line under each amount — which wasted vertical space and made DeepSeek
|
|
599
|
+
// look unlike every other provider group. One row with a real secondary
|
|
600
|
+
// line matches the rest of the list.
|
|
519
601
|
if (v.kind === 'payg' && v.credits && typeof v.credits.remain === 'number') {
|
|
520
|
-
const
|
|
602
|
+
const bonus = (v.bonus && typeof v.bonus.amount === 'number')
|
|
603
|
+
? `${pick(lang, 'bonusBalance')} ${money(v.bonus.amount, v.bonus.unit)}`
|
|
604
|
+
: '';
|
|
605
|
+
return [{
|
|
521
606
|
key: 'balance',
|
|
522
607
|
label: pick(lang, 'balanceLeft'),
|
|
523
608
|
pct: null,
|
|
524
609
|
reset: '',
|
|
610
|
+
detail: bonus,
|
|
525
611
|
credits: { remain: v.credits.remain, unit: v.credits.unit },
|
|
526
612
|
money: true,
|
|
613
|
+
// A wallet with no bonus has no secondary line at all; do not reserve
|
|
614
|
+
// an empty one just to keep the row as tall as the others.
|
|
615
|
+
compact: !bonus,
|
|
527
616
|
}];
|
|
528
|
-
if (v.bonus && typeof v.bonus.amount === 'number') {
|
|
529
|
-
rows.push({
|
|
530
|
-
key: 'bonus',
|
|
531
|
-
label: pick(lang, 'bonusBalance'),
|
|
532
|
-
pct: null,
|
|
533
|
-
reset: '',
|
|
534
|
-
credits: { remain: v.bonus.amount, unit: v.bonus.unit },
|
|
535
|
-
money: true,
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
return rows;
|
|
539
617
|
}
|
|
540
618
|
if (v.kind !== 'subscription-quota' || v.remainingPct === null || v.remainingPct === undefined) {
|
|
541
619
|
return [{ key: 'kind', label: describeQuota(v, lang).text, pct: null, reset: '', credits: null }];
|
|
@@ -586,7 +664,11 @@ window.__ModuleLoader__.load({
|
|
|
586
664
|
const model = current ? `${current.provider}/${current.model}` : null;
|
|
587
665
|
const provider = providerFor(model);
|
|
588
666
|
// 读数的缓存键按 provider|model——换模型时额度来源确实不同,不能混用。
|
|
589
|
-
|
|
667
|
+
// 未识别的 provider 也要有一个稳定的 key,否则 query() 会直接放弃,
|
|
668
|
+
// Current 页就永远看不到「无法识别」的提示。
|
|
669
|
+
const readKey = provider
|
|
670
|
+
? `${provider}|${current ? current.model : ''}`
|
|
671
|
+
: (current ? `unknown|${current.model}` : '');
|
|
590
672
|
const readKeyRef = React.useRef(readKey);
|
|
591
673
|
readKeyRef.current = readKey;
|
|
592
674
|
|
|
@@ -734,14 +816,29 @@ window.__ModuleLoader__.load({
|
|
|
734
816
|
if (!open || tab !== 'all') allRefreshRef.current = null;
|
|
735
817
|
}, [open, tab]);
|
|
736
818
|
|
|
819
|
+
// 切回 DSH(或从系统设置回来)时,DSH 里可能刚加/删了 provider;
|
|
820
|
+
// 「全部」页开着就重读一次,不必让用户先想起点刷新。
|
|
821
|
+
React.useEffect(() => {
|
|
822
|
+
if (!open || tab !== 'all') return undefined;
|
|
823
|
+
const onVisible = () => {
|
|
824
|
+
if (document.visibilityState === 'visible') loadAll(true);
|
|
825
|
+
};
|
|
826
|
+
window.addEventListener('focus', onVisible);
|
|
827
|
+
document.addEventListener('visibilitychange', onVisible);
|
|
828
|
+
return () => {
|
|
829
|
+
window.removeEventListener('focus', onVisible);
|
|
830
|
+
document.removeEventListener('visibilitychange', onVisible);
|
|
831
|
+
};
|
|
832
|
+
}, [open, tab, loadAll]);
|
|
833
|
+
|
|
737
834
|
/**
|
|
738
835
|
* 读一次额度。
|
|
739
836
|
* @param mode 'soft' 上下文变化时用——允许 sidecar 的 60s 缓存命中,没发生消耗就不必打上游
|
|
740
837
|
* 'fresh' 真有活动(任务起止 / 一轮结束)或手动刷新时用——强制读上游
|
|
741
838
|
*/
|
|
742
839
|
const query = React.useCallback((tag, mode) => {
|
|
743
|
-
if (!provider) { setData(null); return; }
|
|
744
840
|
const key = readKeyRef.current;
|
|
841
|
+
if (!key) { setData(null); return; }
|
|
745
842
|
const fresh = mode === 'fresh';
|
|
746
843
|
fetch(`${SIDECAR}/quota?model=${encodeURIComponent(model)}${fresh ? '&fresh=1' : ''}`)
|
|
747
844
|
.then((r) => r.json())
|
|
@@ -1030,7 +1127,19 @@ window.__ModuleLoader__.load({
|
|
|
1030
1127
|
// ① 有百分比的订阅额度 ② 积分类(pct 为 null,但有 credits)
|
|
1031
1128
|
// ③ 免费池(usage-only,既无百分比也无积分)
|
|
1032
1129
|
: canRenderBody(dataPayload)
|
|
1033
|
-
? quotaBody(
|
|
1130
|
+
? quotaBody(
|
|
1131
|
+
dataPayload,
|
|
1132
|
+
lastDelta,
|
|
1133
|
+
lastSpan,
|
|
1134
|
+
avgDelta,
|
|
1135
|
+
remainTasks,
|
|
1136
|
+
rec.turns.length,
|
|
1137
|
+
lang,
|
|
1138
|
+
dataPayload && dataPayload.displayName
|
|
1139
|
+
? String(dataPayload.displayName)
|
|
1140
|
+
: providerName(provider),
|
|
1141
|
+
stale,
|
|
1142
|
+
)
|
|
1034
1143
|
: h('div', {
|
|
1035
1144
|
className: 'quota-pill-empty',
|
|
1036
1145
|
// 这个 provider 从没刷过:显示「—」并让刷新动效说明正在取。
|
|
@@ -1040,6 +1149,8 @@ window.__ModuleLoader__.load({
|
|
|
1040
1149
|
},
|
|
1041
1150
|
dataPayload && dataPayload.kind === 'probe-failed' ? pick(lang, 'bodyProbeFailed')
|
|
1042
1151
|
: dataPayload && dataPayload.kind === 'token-meter' ? pick(lang, 'bodyNoApi')
|
|
1152
|
+
: isUnrecognizedProvider(dataPayload)
|
|
1153
|
+
? adaptHintBlock(dataPayload, model, lang)
|
|
1043
1154
|
: dataPayload && dataPayload.error ? String(dataPayload.error)
|
|
1044
1155
|
: stale ? '—' : pick(lang, 'querying')),
|
|
1045
1156
|
),
|
|
@@ -1274,7 +1385,13 @@ window.__ModuleLoader__.load({
|
|
|
1274
1385
|
}
|
|
1275
1386
|
|
|
1276
1387
|
function WindowRow(props) {
|
|
1277
|
-
const { label, pct, reset, credits, lang, stale, money: isMoney } = props;
|
|
1388
|
+
const { label, pct, reset, detail, credits, lang, stale, money: isMoney, compact } = props;
|
|
1389
|
+
// `detail` is a generic secondary line (a payg bonus, an aggregate hint);
|
|
1390
|
+
// `reset` is the window-reset countdown. Rows in a normal group reserve
|
|
1391
|
+
// the line even when empty so their numbers stay aligned inside the group;
|
|
1392
|
+
// a compact row drops it instead of leaving a blank line.
|
|
1393
|
+
const secondary = detail || reset || '';
|
|
1394
|
+
const reserveSecondary = !compact || !!secondary;
|
|
1278
1395
|
const isNum = typeof pct === 'number' && Number.isFinite(pct);
|
|
1279
1396
|
const tweened = useTween(isNum ? pct : null, 450);
|
|
1280
1397
|
// 首帧 tween 还没拿到起点时 shown 是 null;这时按目标值渲染静态读数,
|
|
@@ -1295,14 +1412,11 @@ window.__ModuleLoader__.load({
|
|
|
1295
1412
|
h('div', { className: 'quota-pill-win-main' },
|
|
1296
1413
|
h('div', { className: 'quota-pill-win-text' },
|
|
1297
1414
|
h('span', { className: 'quota-pill-win-label', title: label }, label),
|
|
1298
|
-
|
|
1299
|
-
// Conditionally omitting it made rows with a reset taller than those
|
|
1300
|
-
// without one, so the column of numbers did not line up.
|
|
1301
|
-
h('span', {
|
|
1415
|
+
reserveSecondary ? h('span', {
|
|
1302
1416
|
className: 'quota-pill-win-reset',
|
|
1303
|
-
title:
|
|
1304
|
-
style:
|
|
1305
|
-
},
|
|
1417
|
+
title: secondary || undefined,
|
|
1418
|
+
style: secondary ? undefined : { visibility: 'hidden' },
|
|
1419
|
+
}, secondary || '\u00a0') : null,
|
|
1306
1420
|
),
|
|
1307
1421
|
h('div', { className: 'quota-pill-win-gauge' },
|
|
1308
1422
|
showCredits
|
|
@@ -1344,7 +1458,7 @@ window.__ModuleLoader__.load({
|
|
|
1344
1458
|
h('div', { className: 'quota-pill-group-head' }, name),
|
|
1345
1459
|
// 组内行 key 带组名,避免不同 provider 的同名窗口(都是 5h/weekly)
|
|
1346
1460
|
// 被 React 当成同一个组件实例,导致 tween 跨 provider 插值。
|
|
1347
|
-
entries.map((e) => h(WindowRow, { key: `${name}|${e.key}`, label: e.label, pct: e.pct, reset: e.reset, credits: e.credits, lang, stale, money: e.money })),
|
|
1461
|
+
entries.map((e) => h(WindowRow, { key: `${name}|${e.key}`, label: e.label, pct: e.pct, reset: e.reset, detail: e.detail, compact: e.compact, credits: e.credits, lang, stale, money: e.money })),
|
|
1348
1462
|
);
|
|
1349
1463
|
}
|
|
1350
1464
|
|
|
@@ -1499,6 +1613,10 @@ window.__ModuleLoader__.load({
|
|
|
1499
1613
|
'.quota-pill-row dt{color:var(--dsw-alias-label-secondary)}',
|
|
1500
1614
|
'.quota-pill-row dd{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary);margin:0}',
|
|
1501
1615
|
'.quota-pill-empty{color:var(--dsw-alias-label-secondary)}',
|
|
1616
|
+
'.quota-pill-adapt{display:flex;flex-direction:column;gap:8px;padding:4px 0}',
|
|
1617
|
+
'.quota-pill-adapt-text{color:var(--dsw-alias-label-secondary)}',
|
|
1618
|
+
'.quota-pill-adapt-copy{align-self:flex-start;cursor:pointer;border:1px solid var(--dsw-alias-border-l3,rgba(127,127,127,.28));background:transparent;color:var(--dsw-alias-label-primary);font:inherit;padding:4px 8px;border-radius:var(--dsw-radius-sm,6px)}',
|
|
1619
|
+
'.quota-pill-adapt-copy:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
1502
1620
|
// 页签:当前 / 全部
|
|
1503
1621
|
'.quota-pill-tabs{display:flex;gap:2px;margin:-4px 0 10px;padding:2px;border-radius:var(--dsw-radius-sm,6px);background:var(--dsw-alias-interactive-bg-hover)}',
|
|
1504
1622
|
'.quota-pill-tab{flex:1;cursor:pointer;border:0;background:transparent;font:inherit;padding:3px 0;border-radius:var(--dsw-radius-sm,6px);color:var(--dsw-alias-label-secondary)}',
|
|
Binary file
|
package/index.js
CHANGED
|
@@ -2,18 +2,26 @@
|
|
|
2
2
|
* Host half of the quota-pill bundle.
|
|
3
3
|
*
|
|
4
4
|
* Rendering lives in client.js, which the web GUI loads separately. This half
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* starts the local sidecar on first launch, so a fresh install shows quota
|
|
6
|
+
* without a separate service unit. The sidecar is idempotent: if one is already
|
|
7
|
+
* listening (launchd/systemd/previous launch), ensureSidecar() leaves it alone.
|
|
7
8
|
*
|
|
8
9
|
* It still has to be a well-formed Cordis plugin, which means exporting `name`.
|
|
9
10
|
* Omitting it makes the loader reject the bundle at import time with
|
|
10
11
|
* "@hystudio/dsh-quota-pill: import failed" — and because the failure happens
|
|
11
12
|
* before activation, nothing in the UI says why. That is how 0.2.2 shipped.
|
|
12
13
|
*/
|
|
14
|
+
import { ensureSidecar } from './src/autostart.js';
|
|
15
|
+
|
|
13
16
|
export const name = 'quota-pill';
|
|
14
17
|
|
|
15
18
|
/** No host-side services are required. */
|
|
16
19
|
export const inject = [];
|
|
17
20
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Best-effort sidecar start. Never reject: a plugin host failure would be worse
|
|
23
|
+
* than "quota service is not running", which the pill already explains.
|
|
24
|
+
*/
|
|
25
|
+
export function apply() {
|
|
26
|
+
return ensureSidecar().catch(() => null);
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hystudio/dsh-quota-pill",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Quota pill for DeepSeek Harness: remaining quota for every LLM subscription you have, in one collapsible pill",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"./server": "./src/server.js"
|
|
30
30
|
},
|
|
31
31
|
"bin": {
|
|
32
|
-
"dsh-quota-pill": "bin/quota-pill-server.js"
|
|
32
|
+
"dsh-quota-pill": "bin/quota-pill-server.js",
|
|
33
|
+
"dsh-quota-pill-opendesign-login": "bin/opendesign-login.js"
|
|
33
34
|
},
|
|
34
35
|
"files": [
|
|
35
36
|
"index.js",
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"src/**/*.js",
|
|
39
40
|
"bin/**/*.js",
|
|
40
41
|
"docs/**/*.md",
|
|
42
|
+
"docs/**/*.png",
|
|
41
43
|
"README.md",
|
|
42
44
|
"AGENTS.md",
|
|
43
45
|
"LICENSE"
|
|
@@ -61,6 +63,6 @@
|
|
|
61
63
|
"scripts": {
|
|
62
64
|
"start": "node bin/quota-pill-server.js",
|
|
63
65
|
"test": "node --test test/*.test.js",
|
|
64
|
-
"check": "node --check src/server.js && node --check client.js"
|
|
66
|
+
"check": "node --check src/server.js && node --check src/providers.js && node --check src/autostart.js && node --check src/user-adapters.js && node --check client.js"
|
|
65
67
|
}
|
|
66
68
|
}
|
package/src/adapters-apikey.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ~/.dsh/.credentials.yaml → refs.<NAME>. No OAuth, no external tool: if the
|
|
6
6
|
* key is present the adapter works, and it is the only requirement.
|
|
7
7
|
*/
|
|
8
|
-
import { readRef, credentialSource } from './credentials.js';
|
|
8
|
+
import { readRef, readFirstRef, credentialSource } from './credentials.js';
|
|
9
9
|
import { getJson } from './http.js';
|
|
10
10
|
|
|
11
11
|
const clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
|
|
@@ -213,8 +213,13 @@ export async function opencode() {
|
|
|
213
213
|
* with weekly.cap 6). So the ceiling is inferred rather than hardcoded to 70.
|
|
214
214
|
*/
|
|
215
215
|
export async function commandcode() {
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
// DSH's own provider config names this ref after its apiKeyEnv
|
|
217
|
+
// (COMMANDCODE_GOAT_API_KEY); older docs use the short name. Accept both so
|
|
218
|
+
// a key the user already has is not reported as missing.
|
|
219
|
+
const token = readFirstRef(['COMMANDCODE_API_KEY', 'COMMANDCODE_GOAT_API_KEY']);
|
|
220
|
+
if (!token) {
|
|
221
|
+
return notConfigured('commandcode', 'COMMANDCODE_API_KEY or COMMANDCODE_GOAT_API_KEY');
|
|
222
|
+
}
|
|
218
223
|
|
|
219
224
|
const res = await getJson('https://api.commandcode.ai/alpha/billing/credits', {
|
|
220
225
|
token,
|