@goodandready/dsh-subscriptions 0.7.36 → 0.7.37
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/config-schema.js +2 -0
- package/lib/media-policy.js +48 -0
- package/lib/provider-icon.js +22 -0
- package/package.json +1 -1
package/lib/config-schema.js
CHANGED
|
@@ -253,6 +253,8 @@ export const Config = z.object({
|
|
|
253
253
|
.description('#694 Data-only vendor packs registered through the provider SDK.'),
|
|
254
254
|
nativeTools: z.boolean().volatile().default(false)
|
|
255
255
|
.description('#18 Opt-in subscription-native image/x/web/video tools. Off by default because these names shadow builtin tools; enable only when the host does not provide them.'),
|
|
256
|
+
toolPolicies: z.any().volatile().default({})
|
|
257
|
+
.description('#809 Per-provider tool policy map: { provider: { web, image, video, search, tools } }.'),
|
|
256
258
|
})
|
|
257
259
|
|
|
258
260
|
export function publicConfig(cfg) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function statusOf(err) {
|
|
2
|
+
return Number(err && (err.status || err.statusCode)) || 0
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/** #822: do not retry ambiguous image/video failures. */
|
|
6
|
+
export function classifyMediaFailure(err) {
|
|
7
|
+
const status = statusOf(err)
|
|
8
|
+
const code = String(err && err.code || '').toUpperCase()
|
|
9
|
+
if (code === 'ABORT_ERR' || code === 'CANCELED' || code === 'CANCELLED') return 'cancelled'
|
|
10
|
+
if (code === 'AMBIGUOUS' || code === 'TIMEOUT_AFTER_SUBMIT') return 'ambiguous'
|
|
11
|
+
if (status === 401 || status === 403 || status === 400 || status === 422) return 'fatal'
|
|
12
|
+
if (status === 429 || status === 408 || status >= 500) return 'retryable'
|
|
13
|
+
if (code === 'TIMEOUT' || code === 'EMPTY_RESPONSE' || code === 'NETWORK') return 'retryable'
|
|
14
|
+
return 'fatal'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function shouldRetryMedia(err, attempt, maxRetries = 0) {
|
|
18
|
+
if (Number(attempt) >= Number(maxRetries)) return false
|
|
19
|
+
return classifyMediaFailure(err) === 'retryable'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** #800: conservative media-pool selection. */
|
|
23
|
+
export function selectMediaAccount(accounts, { nowMs = Date.now(), defaultRef = null, exclude = [] } = {}) {
|
|
24
|
+
const skip = new Set(exclude)
|
|
25
|
+
const usable = (Array.isArray(accounts) ? accounts : []).filter((account) => {
|
|
26
|
+
if (!account || !account.hasToken || skip.has(account.ref)) return false
|
|
27
|
+
if (account.poolEnabled === false) return false
|
|
28
|
+
if (account.cooldownUntil && Number(account.cooldownUntil) > nowMs) return false
|
|
29
|
+
if (account.quarantineUntil && Number(account.quarantineUntil) > nowMs) return false
|
|
30
|
+
if (account.usagePercent != null && Number(account.usagePercent) >= 100) return false
|
|
31
|
+
return true
|
|
32
|
+
})
|
|
33
|
+
if (!usable.length) return null
|
|
34
|
+
return usable.find((account) => account.ref === defaultRef) || usable[0]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** #809: per-provider tool policy. */
|
|
38
|
+
export function resolveToolPolicy(config, provider) {
|
|
39
|
+
const defaults = { web: true, image: true, video: true, search: true, tools: true }
|
|
40
|
+
const all = config && config.toolPolicies
|
|
41
|
+
const row = all && typeof all === 'object' ? all[provider] : null
|
|
42
|
+
if (!row || typeof row !== 'object') return defaults
|
|
43
|
+
return Object.fromEntries(Object.entries(defaults).map(([key, value]) => [key, row[key] === undefined ? value : Boolean(row[key])]))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function toolAllowed(config, provider, kind) {
|
|
47
|
+
return resolveToolPolicy(config, provider)[kind] !== false
|
|
48
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** #835/#836: safe provider icon candidate with deterministic fallback. */
|
|
2
|
+
export function providerInitials(provider) {
|
|
3
|
+
const text = String(provider || '').trim()
|
|
4
|
+
if (!text) return '?'
|
|
5
|
+
return text.slice(0, 2).toUpperCase()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function safeProviderIconUrl(raw) {
|
|
9
|
+
const value = String(raw || '').trim()
|
|
10
|
+
if (!value) return ''
|
|
11
|
+
try {
|
|
12
|
+
const url = new URL(value)
|
|
13
|
+
if (url.protocol === 'http:' || url.protocol === 'https:') return url.href
|
|
14
|
+
if (url.protocol === 'data:' && /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml)[;,]/i.test(value)) return value
|
|
15
|
+
} catch { /* fall through */ }
|
|
16
|
+
return ''
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resolveProviderIcon({ provider, iconUrl, hostIconUrl } = {}) {
|
|
20
|
+
const safe = safeProviderIconUrl(iconUrl) || safeProviderIconUrl(hostIconUrl)
|
|
21
|
+
return { url: safe, fallback: providerInitials(provider), source: safe ? 'url' : 'initials' }
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.37",
|
|
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",
|