@goodandready/dsh-subscriptions 0.5.26 → 0.5.30

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.
@@ -0,0 +1,80 @@
1
+ import z from '@deepseek-ai/schemastery'
2
+ import { PROVIDERS } from './refs.js'
3
+
4
+ export const Slot = z.object({
5
+ provider: z.string().default('codex')
6
+ .description('One of: codex, claude, grok, antigravity.'),
7
+ index: z.number().default(1)
8
+ .description('Account slot number. Credential ref is <PROVIDER>_OAUTH_<index>.'),
9
+ label: z.string().default('')
10
+ .description('Optional display label. Empty uses the account email after login.'),
11
+ expiresAt: z.number().default(0)
12
+ .description('#67 Optional subscription expiry timestamp (ms). When set and within expiryNotifyDays, the header chip shows the account and expiry date.'),
13
+ proxyUrl: z.string().default('')
14
+ .description('#88 Per-account proxy URL (http://, https://, socks5://). All requests for this account route through it. Empty = direct connection.'),
15
+ })
16
+
17
+ export const defaultSlots = PROVIDERS.map((provider) => ({ provider, index: 1, label: '' }))
18
+
19
+ export const Config = z.object({
20
+ cooldownMs: z.number().default(30 * 60 * 1000)
21
+ .description('After RATE_LIMIT/QUOTA/429, skip that account for this many milliseconds.'),
22
+ switchAtRemaining: z.number().default(0.01)
23
+ .description('If remaining <= this (absolute or <1 fraction), treat as exhausted before request. 0 disables.'),
24
+ refreshAheadMs: z.number().default(5 * 60 * 1000)
25
+ .description('Background refresh when expiry within this many ms.'),
26
+ refreshRetryMs: z.number().default(10 * 60 * 1000)
27
+ .description('Do not retry background refresh more often than this after failure.'),
28
+ probeIntervalMin: z.number().default(15)
29
+ .description('Background health-check interval in minutes. 0 disables.'),
30
+ notifyLimits: z.boolean().default(true)
31
+ .description('Emit log notices when usage crosses 70/90/100% of a window.'),
32
+ expiryNotifyDays: z.number().default(7)
33
+ .description('#67 Warn in the header chip this many days before a subscription expiry (expiresAt). 0 disables.'),
34
+ privacyMask: z.boolean().default(false)
35
+ .description('#98 Hide personal data in the UI: emails show as j***n@example.com.'),
36
+ slots: z.array(Slot).default(defaultSlots)
37
+ .description('Account slots. Secrets are not stored here; only the credential ref names.'),
38
+ useWebCallback: z.boolean().default(false)
39
+ .description('When on, redirect_uri is this Web UI origin + /dsh-subscriptions/oauth/callback. When off, the vendor CLI registered redirect is used and you paste the redirected URL.'),
40
+ autoLoopback: z.boolean().default(true)
41
+ .description('#89 When on and the vendor redirect_uri is a loopback address (codex :1455, grok :56121), a temporary local server catches the OAuth callback automatically - no paste needed. Paste fallback stays available.'),
42
+ ollamaBaseUrl: z.string().default('http://127.0.0.1:11434')
43
+ .description('#91 Local Ollama base URL. Served as the ollama provider in the native model picker when reachable.'),
44
+ ollamaFallback: z.boolean().default(true)
45
+ .description('#91 When every account of a provider is exhausted, continue the chat on local Ollama instead of failing.'),
46
+ ollamaFallbackModel: z.string().default('')
47
+ .description('#91 Ollama model used for the fallback (for example qwen2.5-coder). Empty = first model from /api/tags.'),
48
+ hideDeprecatedModels: z.boolean().default(false)
49
+ .description('#94 Hide test/preview/beta/legacy model ids from the native model picker.'),
50
+ codexClientId: z.string().default(''),
51
+ codexVerbosity: z.string().default('')
52
+ .description('#93 Response verbosity for Codex reasoning models: low, medium or high. Empty = protocol default.'),
53
+ codexFastMode: z.boolean().default(false)
54
+ .description('#92 Fast Mode for Codex: sends service_tier priority (1.5x speed billing tier) with every request.'),
55
+ composerQuota: z.string().default('off')
56
+ .description('#84 Composer quota indicator mode: off, percent, bar or forecast (predictive runway from a sliding window).'),
57
+ codexRedirectUri: z.string().default(''),
58
+ codexBaseUrl: z.string().default(''),
59
+ claudeClientId: z.string().default(''),
60
+ claudeRedirectUri: z.string().default(''),
61
+ grokClientId: z.string().default(''),
62
+ grokRedirectUri: z.string().default(''),
63
+ grokBaseUrl: z.string().default(''),
64
+ grokClientVersion: z.string().default('')
65
+ .description('Grok CLI identity version header. Empty uses the built-in default.'),
66
+ antigravityClientId: z.string().default(''),
67
+ antigravityRedirectUri: z.string().default(''),
68
+ customVendors: z.array(z.any()).default([])
69
+ .description('Declarative OpenAI-Responses-compatible providers. See README.'),
70
+ })
71
+
72
+ export function publicConfig(cfg) {
73
+ const clone = structuredClone(cfg)
74
+ // #242: redact all *ClientSecret fields before exposing config via GET /config.
75
+ // Secrets must never leave the server; the UI does not need them.
76
+ for (const key of Object.keys(clone)) {
77
+ if (key.endsWith('ClientSecret')) clone[key] = clone[key] ? '••••••' : ''
78
+ }
79
+ return clone
80
+ }