@goodandready/dsh-subscriptions 0.6.4 → 0.6.5
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/lib/blob.js +0 -8
- package/lib/client.js +48 -12
- package/lib/http.js +4 -1
- package/lib/index.js +5 -0
- package/lib/quarantine.js +0 -7
- package/lib/ratelimit-parser.js +0 -24
- package/lib/ratelimit.js +0 -8
- package/lib/refs.js +5 -0
- package/lib/relative-time.js +6 -0
- package/lib/session-pin.js +11 -0
- package/lib/vendors/antigravity.js +3 -1
- package/lib/vendors/grok.js +2 -1
- package/package.json +6 -3
package/lib/blob.js
CHANGED
package/lib/client.js
CHANGED
|
@@ -54,6 +54,10 @@ window.__ModuleLoader__.load({
|
|
|
54
54
|
'.dsub-chevOpen{transform:rotate(180deg)}' +
|
|
55
55
|
'.dsub-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:12px}' +
|
|
56
56
|
'.dsub-block{margin-top:10px}' +
|
|
57
|
+
'.dsub-group{margin-top:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:0 14px}' +
|
|
58
|
+
'.dsub-group[open]{padding-bottom:6px}' +
|
|
59
|
+
'.dsub-groupHead{cursor:pointer;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);padding:10px 0;list-style:none}' +
|
|
60
|
+
'.dsub-groupHead::-webkit-details-marker{display:none}' +
|
|
57
61
|
'.dsub-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:8px}' +
|
|
58
62
|
'.dsub-grow{flex:1 1 180px;min-width:140px}' +
|
|
59
63
|
'.dsub-helpBox{margin-top:8px;padding:12px 14px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);font-size:12px;line-height:1.6}' +
|
|
@@ -310,6 +314,12 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
310
314
|
'intro': 'Log in with a consumer subscription. Tokens stay on the host credentials store. The browser never reads them back. After Connect, if the provider lands on localhost or a vendor page, paste the redirected URL or the code here.',
|
|
311
315
|
'useOrigin': 'Use this Web UI origin as OAuth redirect_uri',
|
|
312
316
|
'useOriginHint': 'Leave off unless you registered your own OAuth client for this origin. Vendor CLI clients usually require their published redirect URI plus the paste step.',
|
|
317
|
+
'closeModal': 'Close',
|
|
318
|
+
'groupAuth': 'Sign-in and redirect',
|
|
319
|
+
'groupPrivacy': 'Privacy and model list',
|
|
320
|
+
'groupRotation': 'Rotation and limits',
|
|
321
|
+
'groupProvider': 'Provider behaviour',
|
|
322
|
+
'groupFallback': 'Local fallback',
|
|
313
323
|
'privacyMask': 'Mask emails and account identifiers',
|
|
314
324
|
'privacyMaskHint': 'For demos and screen sharing: emails show as j***n@example.com.',
|
|
315
325
|
'autoLoopback': 'Auto Loopback',
|
|
@@ -539,6 +549,10 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
539
549
|
function badgeFor(account, t) {
|
|
540
550
|
if (!account || !account.configured) return t('notConnected')
|
|
541
551
|
if (account.validationUrl) return t('verifyAccount')
|
|
552
|
+
// #303: a quarantined account says why it is parked.
|
|
553
|
+
if (account.quarantineReason && account.quarantineUntil && account.quarantineUntil > Date.now()) {
|
|
554
|
+
return account.quarantineReason === 'VERIFY' ? t('verifyAccount') : String(account.quarantineReason)
|
|
555
|
+
}
|
|
542
556
|
if (account.cooldownUntil && account.cooldownUntil > Date.now()) return t('coolingDown')
|
|
543
557
|
if (account.usagePercent != null && account.usagePercent >= 100) return t('usageFull')
|
|
544
558
|
return t('connected')
|
|
@@ -862,6 +876,8 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
862
876
|
React.createElement('div', { className: 'dsub-h' }, t('title')),
|
|
863
877
|
React.createElement('div', { className: 'dsub-sub' },
|
|
864
878
|
t('intro')),
|
|
879
|
+
React.createElement('details', { className: 'dsub-group', open: true },
|
|
880
|
+
React.createElement('summary', { className: 'dsub-groupHead' }, t('groupAuth')),
|
|
865
881
|
React.createElement('label', { className: 'dsub-row' },
|
|
866
882
|
React.createElement('input', {
|
|
867
883
|
type: 'checkbox',
|
|
@@ -875,23 +891,26 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
875
891
|
React.createElement('label', { className: 'dsub-row' },
|
|
876
892
|
React.createElement('input', {
|
|
877
893
|
type: 'checkbox',
|
|
878
|
-
checked: !!draft.
|
|
879
|
-
onChange: (e) => setDraft((d) => Object.assign({}, d, {
|
|
894
|
+
checked: !!draft.autoLoopback,
|
|
895
|
+
onChange: (e) => setDraft((d) => Object.assign({}, d, { autoLoopback: e.target.checked })),
|
|
880
896
|
}),
|
|
881
|
-
React.createElement('span', null, t('
|
|
897
|
+
React.createElement('span', null, t('autoLoopback')),
|
|
882
898
|
),
|
|
883
899
|
React.createElement('div', { className: 'dsub-sub' },
|
|
884
|
-
t('
|
|
900
|
+
t('autoLoopbackHint')),
|
|
901
|
+
),
|
|
902
|
+
React.createElement('details', { className: 'dsub-group', open: true },
|
|
903
|
+
React.createElement('summary', { className: 'dsub-groupHead' }, t('groupPrivacy')),
|
|
885
904
|
React.createElement('label', { className: 'dsub-row' },
|
|
886
905
|
React.createElement('input', {
|
|
887
906
|
type: 'checkbox',
|
|
888
|
-
checked: !!draft.
|
|
889
|
-
onChange: (e) => setDraft((d) => Object.assign({}, d, {
|
|
907
|
+
checked: !!draft.privacyMask,
|
|
908
|
+
onChange: (e) => setDraft((d) => Object.assign({}, d, { privacyMask: e.target.checked })),
|
|
890
909
|
}),
|
|
891
|
-
React.createElement('span', null, t('
|
|
910
|
+
React.createElement('span', null, t('privacyMask')),
|
|
892
911
|
),
|
|
893
912
|
React.createElement('div', { className: 'dsub-sub' },
|
|
894
|
-
t('
|
|
913
|
+
t('privacyMaskHint')),
|
|
895
914
|
React.createElement('label', { className: 'dsub-row' },
|
|
896
915
|
React.createElement('input', {
|
|
897
916
|
type: 'checkbox',
|
|
@@ -902,6 +921,9 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
902
921
|
),
|
|
903
922
|
React.createElement('div', { className: 'dsub-sub' },
|
|
904
923
|
t('hideDeprecatedHint')),
|
|
924
|
+
),
|
|
925
|
+
React.createElement('details', { className: 'dsub-group', open: true },
|
|
926
|
+
React.createElement('summary', { className: 'dsub-groupHead' }, t('groupRotation')),
|
|
905
927
|
React.createElement('div', { className: 'dsub-row' },
|
|
906
928
|
React.createElement('span', { className: 'dsub-h' }, t('composerQuota')),
|
|
907
929
|
React.createElement('select', {
|
|
@@ -931,6 +953,9 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
931
953
|
),
|
|
932
954
|
React.createElement('div', { className: 'dsub-sub' },
|
|
933
955
|
t('expiryNotifyDaysHint')),
|
|
956
|
+
),
|
|
957
|
+
React.createElement('details', { className: 'dsub-group', open: true },
|
|
958
|
+
React.createElement('summary', { className: 'dsub-groupHead' }, t('groupProvider')),
|
|
934
959
|
React.createElement('label', { className: 'dsub-row' },
|
|
935
960
|
React.createElement('input', {
|
|
936
961
|
type: 'checkbox',
|
|
@@ -956,6 +981,9 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
956
981
|
),
|
|
957
982
|
React.createElement('div', { className: 'dsub-sub' },
|
|
958
983
|
t('codexVerbosityHint')),
|
|
984
|
+
),
|
|
985
|
+
React.createElement('details', { className: 'dsub-group', open: true },
|
|
986
|
+
React.createElement('summary', { className: 'dsub-groupHead' }, t('groupFallback')),
|
|
959
987
|
React.createElement('label', { className: 'dsub-row' },
|
|
960
988
|
React.createElement('input', {
|
|
961
989
|
type: 'checkbox',
|
|
@@ -982,6 +1010,7 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
982
1010
|
onChange: (e) => setDraft((d) => Object.assign({}, d, { ollamaFallbackModel: e.target.value })),
|
|
983
1011
|
}),
|
|
984
1012
|
) : null,
|
|
1013
|
+
),
|
|
985
1014
|
React.createElement('details', { className: 'dsub-details' },
|
|
986
1015
|
React.createElement('summary', { className: 'dsub-summary' }, t('advancedSettings')),
|
|
987
1016
|
React.createElement('div', { className: 'dsub-block' },
|
|
@@ -1691,15 +1720,20 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1691
1720
|
const rows = state.accounts.map((a, i) => {
|
|
1692
1721
|
const isLogged = state.logged.includes(a.provider)
|
|
1693
1722
|
const cooled = a.cooldownUntil && a.cooldownUntil > now
|
|
1723
|
+
const quarantined = Boolean(a.quarantineReason) && Number(a.quarantineUntil || 0) > now
|
|
1694
1724
|
const pct = a.usagePercent != null ? Math.round(a.usagePercent) : null
|
|
1695
1725
|
const b = brandBadge(a.provider)
|
|
1696
1726
|
const isActiveThis = state.active && state.active.provider === a.provider && state.active.index === a.index
|
|
1697
1727
|
|
|
1698
1728
|
let statusClass = 'dsub-statusOff'
|
|
1699
1729
|
let statusText = t('subsNotLogged')
|
|
1700
|
-
if (
|
|
1730
|
+
if (quarantined) {
|
|
1731
|
+
statusClass = 'dsub-statusBad'
|
|
1732
|
+
statusText = a.quarantineReason === 'VERIFY' ? t('verifyAccount') : String(a.quarantineReason)
|
|
1733
|
+
} else if (cooled) {
|
|
1701
1734
|
statusClass = 'dsub-statusWarn'
|
|
1702
|
-
|
|
1735
|
+
// Only some model families may be cooled down - say which ones.
|
|
1736
|
+
statusText = t('subsCooldown') + (Array.isArray(a.cooldownFamilies) && a.cooldownFamilies.length ? ' · ' + a.cooldownFamilies.join('/') : '')
|
|
1703
1737
|
} else if (isLogged) {
|
|
1704
1738
|
if (pct >= 90) { statusClass = 'dsub-statusBad'; statusText = '90%+ ' + t('subsUsed') }
|
|
1705
1739
|
else if (pct >= 50) { statusClass = 'dsub-statusWarn'; statusText = pct + '% ' + t('subsUsed') }
|
|
@@ -1715,7 +1749,7 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1715
1749
|
isActiveThis ? React.createElement('span', { className: 'dsub-pillTag', style: { background: 'color-mix(in srgb,var(--dsw-alias-state-success-primary) 15%,transparent)', color: 'var(--dsw-alias-state-success-primary)' } }, 'ACTIVE') : null,
|
|
1716
1750
|
a.proxy ? React.createElement('span', { className: 'dsub-dim', title: a.proxy }, '🌐 proxy') : null,
|
|
1717
1751
|
),
|
|
1718
|
-
React.createElement('span', { className: 'dsub-accStatusTag ' + statusClass }, statusText),
|
|
1752
|
+
React.createElement('span', { className: 'dsub-accStatusTag ' + statusClass, role: 'status', 'aria-live': 'polite' }, statusText),
|
|
1719
1753
|
),
|
|
1720
1754
|
(isLogged && pct != null) ? React.createElement('div', { className: 'dsub-barTrack', style: { marginTop: 4, height: 4 } },
|
|
1721
1755
|
React.createElement('div', {
|
|
@@ -1746,7 +1780,7 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1746
1780
|
className: 'dsub-modalWrap',
|
|
1747
1781
|
onClick: (e) => { if (e.target === e.currentTarget) setState((s) => Object.assign({}, s, { open: false })) },
|
|
1748
1782
|
},
|
|
1749
|
-
React.createElement('div', { className: 'dsub-modal', role: 'dialog', 'aria-label': t('subsModalTitle') },
|
|
1783
|
+
React.createElement('div', { className: 'dsub-modal', role: 'dialog', 'aria-modal': 'true', 'aria-label': t('subsModalTitle') },
|
|
1750
1784
|
React.createElement('div', { className: 'dsub-modalHead' },
|
|
1751
1785
|
React.createElement('div', { className: 'dsub-modalTitleWrap' },
|
|
1752
1786
|
React.createElement('div', { className: 'dsub-modalIcon' }, '⚡'),
|
|
@@ -1765,6 +1799,8 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1765
1799
|
React.createElement('button', {
|
|
1766
1800
|
type: 'button',
|
|
1767
1801
|
className: 'dsub-mini',
|
|
1802
|
+
autoFocus: true,
|
|
1803
|
+
'aria-label': t('closeModal'),
|
|
1768
1804
|
onClick: () => setState((s) => Object.assign({}, s, { open: false })),
|
|
1769
1805
|
}, '✕'),
|
|
1770
1806
|
),
|
package/lib/http.js
CHANGED
|
@@ -30,7 +30,10 @@ export function readBody(req, maxBytes = 64 * 1024) {
|
|
|
30
30
|
})
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* @param {import('node:http').IncomingMessage} request
|
|
35
|
+
* @returns {boolean}
|
|
36
|
+
* Reject cross-site writes. Do not require loopback: Web UI is used over LAN and reverse proxies. */
|
|
34
37
|
export function isTrustedSettingsRequest(request) {
|
|
35
38
|
return request.headers['sec-fetch-site'] !== 'cross-site'
|
|
36
39
|
}
|
package/lib/index.js
CHANGED
|
@@ -440,6 +440,11 @@ export function apply(ctx, config) {
|
|
|
440
440
|
configured: info.configured,
|
|
441
441
|
writable: info.writable,
|
|
442
442
|
cooldownUntil: info.cooldownUntil,
|
|
443
|
+
// #303: expose the cooldown scope and quarantine so the card can
|
|
444
|
+
// explain why only some models are blocked.
|
|
445
|
+
cooldownFamilies: info.cooldownFamilies || null,
|
|
446
|
+
quarantineUntil: info.quarantineUntil || 0,
|
|
447
|
+
quarantineReason: info.quarantineReason || null,
|
|
443
448
|
usagePercent: info.usagePercent,
|
|
444
449
|
quota: info.quota || null,
|
|
445
450
|
usage: info.usage || null,
|
package/lib/quarantine.js
CHANGED
|
@@ -29,10 +29,3 @@ export function isQuarantined(account, nowMs = Date.now()) {
|
|
|
29
29
|
return Number(account.quarantineUntil) > Number(nowMs)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export function releaseFromQuarantine(account) {
|
|
33
|
-
const next = { ...account }
|
|
34
|
-
delete next.quarantineReason
|
|
35
|
-
delete next.quarantineUntil
|
|
36
|
-
if (next.status === 'quarantine') next.status = 'active'
|
|
37
|
-
return next
|
|
38
|
-
}
|
package/lib/ratelimit-parser.js
CHANGED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export function parseRateLimitHeaders(headers) {
|
|
2
|
-
if (!headers || typeof headers !== 'object') return null
|
|
3
|
-
|
|
4
|
-
const get = (key) => {
|
|
5
|
-
const k = Object.keys(headers).find((h) => h.toLowerCase() === key.toLowerCase())
|
|
6
|
-
return k ? headers[k] : null
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const remainingTokens = get('x-ratelimit-remaining-tokens')
|
|
10
|
-
const resetTokens = get('x-ratelimit-reset-tokens')
|
|
11
|
-
const remainingRequests = get('x-ratelimit-remaining-requests')
|
|
12
|
-
const resetRequests = get('x-ratelimit-reset-requests')
|
|
13
|
-
|
|
14
|
-
if (!remainingTokens && !resetTokens && !remainingRequests && !resetRequests) {
|
|
15
|
-
return null
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
remainingTokens: remainingTokens != null ? Number(remainingTokens) : null,
|
|
20
|
-
resetTokensTime: resetTokens || null,
|
|
21
|
-
remainingRequests: remainingRequests != null ? Number(remainingRequests) : null,
|
|
22
|
-
resetRequestsTime: resetRequests || null
|
|
23
|
-
}
|
|
24
|
-
}
|
package/lib/ratelimit.js
CHANGED
|
@@ -208,11 +208,3 @@ export function quotaSnapshot(provider, headers, bodyJson, nowMs = Date.now()) {
|
|
|
208
208
|
return { remaining, limit, resetAt: resetAt ?? null, usedPercent, measuredAt: nowMs }
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
export function formatQuota(q) {
|
|
212
|
-
if (!q) return ""
|
|
213
|
-
const parts = []
|
|
214
|
-
if (q.remaining!=null && q.limit!=null) parts.push(q.remaining+"/"+q.limit)
|
|
215
|
-
else if (q.remaining!=null) parts.push("left "+q.remaining)
|
|
216
|
-
if (q.resetAt) parts.push("reset "+new Date(q.resetAt).toLocaleString())
|
|
217
|
-
return parts.join(" · ")
|
|
218
|
-
}
|
package/lib/refs.js
CHANGED
|
@@ -61,6 +61,11 @@ export function displayName(provider) {
|
|
|
61
61
|
return DISPLAY[provider] || displayExtra[provider] || provider
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @param {string} provider
|
|
66
|
+
* @param {number} index
|
|
67
|
+
* @returns {string} provider-scoped credential ref, e.g. CODEX_OAUTH_1
|
|
68
|
+
*/
|
|
64
69
|
export function oauthRef(provider, index) {
|
|
65
70
|
const n = Number(index)
|
|
66
71
|
if (!Number.isInteger(n) || n < 1) throw new Error('account index must be an integer >= 1')
|
package/lib/relative-time.js
CHANGED
|
@@ -26,6 +26,12 @@ function fill(template, n) {
|
|
|
26
26
|
return String(template).replace('{n}', String(n))
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* @param {number} resetAt epoch ms of the quota window reset
|
|
31
|
+
* @param {string} [lang]
|
|
32
|
+
* @param {number} [now]
|
|
33
|
+
* @returns {string} human-readable remaining time, down to the minute
|
|
34
|
+
*/
|
|
29
35
|
export function formatRelativeReset(resetAt, lang = 'ru', now = Date.now()) {
|
|
30
36
|
if (typeof resetAt !== 'number' || !Number.isFinite(resetAt) || resetAt <= 0) return ''
|
|
31
37
|
const delta = resetAt - now
|
package/lib/session-pin.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
const sessionPins = new Map()
|
|
2
2
|
const DEFAULT_TTL_MS = 30 * 60 * 1000 // 30 mins
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Bind a session id to an account ref for ttlMs (default 30 minutes).
|
|
6
|
+
* @param {string|undefined} sessionId
|
|
7
|
+
* @param {string|undefined} accountRef
|
|
8
|
+
* @param {number} [ttlMs]
|
|
9
|
+
* @returns {void}
|
|
10
|
+
*/
|
|
4
11
|
export function pinSession(sessionId, accountRef, ttlMs = DEFAULT_TTL_MS) {
|
|
5
12
|
if (!sessionId || !accountRef) return
|
|
6
13
|
sessionPins.set(sessionId, {
|
|
@@ -9,6 +16,10 @@ export function pinSession(sessionId, accountRef, ttlMs = DEFAULT_TTL_MS) {
|
|
|
9
16
|
})
|
|
10
17
|
}
|
|
11
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @param {string|undefined} sessionId
|
|
21
|
+
* @returns {string|null} the bound account ref, or null when absent/expired
|
|
22
|
+
*/
|
|
12
23
|
export function getPinnedAccountRef(sessionId) {
|
|
13
24
|
if (!sessionId) return null
|
|
14
25
|
const entry = sessionPins.get(sessionId)
|
|
@@ -150,7 +150,9 @@ export async function usage(blob, _cfg, fetchImpl) {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal, saveBlob }) {
|
|
153
|
-
|
|
153
|
+
// Abort must cover the project pre-flight as well as the stream.
|
|
154
|
+
const preflight = (u, i) => fetchImpl(u, { ...(i || {}), signal })
|
|
155
|
+
const ready = await withProject(blob, preflight, saveBlob)
|
|
154
156
|
const { envelope, sessionId } = streamEnvelope({
|
|
155
157
|
projectId: ready.projectId,
|
|
156
158
|
model: options.model,
|
package/lib/vendors/grok.js
CHANGED
|
@@ -236,7 +236,8 @@ export async function* streamOnce({ blob, options, fetchImpl, headers, config, s
|
|
|
236
236
|
if (!body.instructions) delete body.instructions
|
|
237
237
|
// grok manages reasoning itself (cli catalog aware); drop the codex-level field
|
|
238
238
|
delete body.reasoning
|
|
239
|
-
|
|
239
|
+
// Thread the caller AbortSignal through the catalog pre-flight too.
|
|
240
|
+
const catalog = await cliCatalogCached(blob, config, (u, i) => fetchImpl(u, { ...(i || {}), signal })).catch(() => new Map())
|
|
240
241
|
const reasoning = grokReasoningBody(options.model, options.reasoningEffort, catalog)
|
|
241
242
|
if (reasoning) body.reasoning = reasoning
|
|
242
243
|
body = grokBodyForModel(options.model, body)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Use ChatGPT Codex, Claude, Grok, Antigravity, Kimi, GLM, Cursor, Kiro, Copilot, Qwen, ERNIE, Spark, JetBrains, Perplexity, Replit, and Cody subscriptions as DeepSeek Harness LLM providers via OAuth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"url": "https://github.com/GooDAnDReaDY/dsh-subscriptions/issues"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
|
-
"test": "eslint lib/ && node --test test/*.test.mjs"
|
|
38
|
+
"test": "eslint lib/ && node --test test/*.test.mjs",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.checkjs.json"
|
|
39
40
|
},
|
|
40
41
|
"dsh": {
|
|
41
42
|
"bundle": {
|
|
@@ -62,6 +63,8 @@
|
|
|
62
63
|
"undici": "^8.10.1"
|
|
63
64
|
},
|
|
64
65
|
"devDependencies": {
|
|
65
|
-
"@deepseek-ai/dsh-llm": "0.1.0-rc.8"
|
|
66
|
+
"@deepseek-ai/dsh-llm": "0.1.0-rc.8",
|
|
67
|
+
"@types/node": "^22.20.2",
|
|
68
|
+
"typescript": "^7.0.2"
|
|
66
69
|
}
|
|
67
70
|
}
|