@goodandready/dsh-subscriptions 0.6.71 → 0.6.82
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/README.md +13 -0
- package/README.ru.md +13 -0
- package/README.zh.md +13 -0
- package/lib/accounts-view.js +101 -5
- package/lib/accounts.js +150 -11
- package/lib/adapter.js +3 -0
- package/lib/alerts.js +35 -0
- package/lib/analytics.js +31 -2
- package/lib/billing.js +99 -0
- package/lib/blob.js +18 -0
- package/lib/capabilities.js +208 -0
- package/lib/client.js +944 -828
- package/lib/config-schema.js +46 -4
- package/lib/diagnostic-wizard.js +2 -5
- package/lib/diagnostics.js +2 -2
- package/lib/doctor.js +163 -0
- package/lib/error-buffer.js +2 -2
- package/lib/forecast.js +56 -0
- package/lib/headless-auth.js +379 -0
- package/lib/history.js +36 -0
- package/lib/http.js +26 -4
- package/lib/index.js +98 -11
- package/lib/instructions.js +138 -0
- package/lib/jwt.js +84 -17
- package/lib/loopback.js +3 -2
- package/lib/mask.js +16 -0
- package/lib/oauth-pending.js +12 -0
- package/lib/pricing.js +89 -0
- package/lib/refs.js +2 -0
- package/lib/reset-clock.js +55 -0
- package/lib/rotate.js +18 -4
- package/lib/routes/accounts.js +203 -0
- package/lib/routes/oauth.js +44 -2
- package/lib/routes/proxy.js +14 -0
- package/lib/routes/status.js +99 -5
- package/lib/scope-inspector.js +51 -0
- package/lib/stream-rotate.js +31 -10
- package/lib/updater.js +21 -0
- package/lib/vault-sync.js +412 -0
- package/lib/vault.js +1 -1
- package/lib/vendor-factory.js +6 -2
- package/lib/vendors/antigravity.js +11 -2
- package/lib/vendors/claude.js +56 -8
- package/lib/vendors/codex.js +34 -6
- package/lib/vendors/copilot-cli.js +8 -1
- package/lib/vendors/ernie.js +9 -2
- package/lib/vendors/grok.js +66 -6
- package/lib/vendors/jetbrains.js +133 -13
- package/lib/vendors/perplexity.js +148 -17
- package/package.json +1 -1
package/lib/billing.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Subscription Billing Cycle Renewal Countdown & Payment Status Notifier (#558)
|
|
2
|
+
// Parses renewal dates, detects payment issues, formats countdowns and generates warnings.
|
|
3
|
+
|
|
4
|
+
const ONE_DAY_MS = 24 * 60 * 60 * 1000
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Formats a renewal timestamp into a human-readable countdown string.
|
|
8
|
+
* @param {number|null} renewsAt
|
|
9
|
+
* @param {number} [nowMs]
|
|
10
|
+
* @returns {string}
|
|
11
|
+
*/
|
|
12
|
+
export function formatRenewalCountdown(renewsAt, nowMs = Date.now()) {
|
|
13
|
+
const ts = Number(renewsAt)
|
|
14
|
+
if (!Number.isFinite(ts) || ts <= 0) return ''
|
|
15
|
+
|
|
16
|
+
const diffMs = ts - nowMs
|
|
17
|
+
if (diffMs <= 0) {
|
|
18
|
+
return 'Renewal overdue'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const days = Math.floor(diffMs / ONE_DAY_MS)
|
|
22
|
+
const d = new Date(ts)
|
|
23
|
+
const yyyy = d.getUTCFullYear()
|
|
24
|
+
const mm = String(d.getUTCMonth() + 1).padStart(2, '0')
|
|
25
|
+
const dd = String(d.getUTCDate()).padStart(2, '0')
|
|
26
|
+
const dateStr = `${yyyy}-${mm}-${dd}`
|
|
27
|
+
|
|
28
|
+
if (days === 0) {
|
|
29
|
+
return `Renews today (${dateStr})`
|
|
30
|
+
}
|
|
31
|
+
if (days === 1) {
|
|
32
|
+
return `Renews in 1 day (${dateStr})`
|
|
33
|
+
}
|
|
34
|
+
return `Renews in ${days} days (${dateStr})`
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extracts billing cycle metadata and evaluates payment status for a slot.
|
|
39
|
+
* @param {object} [blob]
|
|
40
|
+
* @param {object} [slot]
|
|
41
|
+
* @param {number} [nowMs]
|
|
42
|
+
* @returns {object}
|
|
43
|
+
*/
|
|
44
|
+
export function extractBillingInfo(blob = {}, slot = {}, nowMs = Date.now()) {
|
|
45
|
+
const rawStatus = String(
|
|
46
|
+
blob?.subscriptionStatus ||
|
|
47
|
+
blob?.paymentStatus ||
|
|
48
|
+
blob?.planStatus ||
|
|
49
|
+
''
|
|
50
|
+
).toLowerCase().trim()
|
|
51
|
+
|
|
52
|
+
const rawRenew = (
|
|
53
|
+
blob?.billingCycleEnd ||
|
|
54
|
+
blob?.periodEnd ||
|
|
55
|
+
blob?.renewsAt ||
|
|
56
|
+
slot?.expiresAt ||
|
|
57
|
+
blob?.expiresAt ||
|
|
58
|
+
null
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
const renewsAt = Number.isFinite(Number(rawRenew)) && Number(rawRenew) > 0 ? Number(rawRenew) : null
|
|
62
|
+
const renewalCountdown = formatRenewalCountdown(renewsAt, nowMs)
|
|
63
|
+
|
|
64
|
+
let status = 'active'
|
|
65
|
+
let paymentFailed = false
|
|
66
|
+
let isOverdue = false
|
|
67
|
+
let badgeText = ''
|
|
68
|
+
let warning = ''
|
|
69
|
+
|
|
70
|
+
if (['past_due', 'unpaid', 'payment_failed', 'failed'].includes(rawStatus)) {
|
|
71
|
+
status = 'past_due'
|
|
72
|
+
paymentFailed = true
|
|
73
|
+
badgeText = 'Payment Failed'
|
|
74
|
+
warning = 'Payment failed or subscription past due - check provider billing'
|
|
75
|
+
} else if (['cancelled', 'canceled', 'revoked'].includes(rawStatus)) {
|
|
76
|
+
status = 'cancelled'
|
|
77
|
+
badgeText = 'Cancelled'
|
|
78
|
+
warning = 'Subscription has been cancelled with provider'
|
|
79
|
+
} else if (['trial_expired', 'expired'].includes(rawStatus)) {
|
|
80
|
+
status = 'trial_expired'
|
|
81
|
+
badgeText = 'Trial Expired'
|
|
82
|
+
warning = 'Subscription trial or plan has expired'
|
|
83
|
+
} else if (renewsAt && renewsAt < nowMs) {
|
|
84
|
+
status = 'past_due'
|
|
85
|
+
isOverdue = true
|
|
86
|
+
badgeText = 'Renewal Overdue'
|
|
87
|
+
warning = 'Subscription renewal date has passed'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
renewsAt,
|
|
92
|
+
renewalCountdown,
|
|
93
|
+
status,
|
|
94
|
+
isOverdue,
|
|
95
|
+
paymentFailed,
|
|
96
|
+
badgeText,
|
|
97
|
+
warning,
|
|
98
|
+
}
|
|
99
|
+
}
|
package/lib/blob.js
CHANGED
|
@@ -45,6 +45,15 @@ export function serializeBlob(obj) {
|
|
|
45
45
|
email: asString(obj.email),
|
|
46
46
|
accountId: asString(obj.accountId),
|
|
47
47
|
projectId: asString(obj.projectId),
|
|
48
|
+
selectedOrgId: asString(obj.selectedOrgId),
|
|
49
|
+
selectedOrgName: asString(obj.selectedOrgName),
|
|
50
|
+
...(Array.isArray(obj.organizations) ? { organizations: obj.organizations } : {}),
|
|
51
|
+
...(Array.isArray(obj.workspaces) ? { workspaces: obj.workspaces } : {}),
|
|
52
|
+
...(obj.organizationId ? { organizationId: asString(obj.organizationId) } : {}),
|
|
53
|
+
...(obj.plan ? { plan: asString(obj.plan) } : {}),
|
|
54
|
+
...(obj.avatarUrl ? { avatarUrl: asString(obj.avatarUrl) } : {}),
|
|
55
|
+
...(Array.isArray(obj.scopes) ? { scopes: obj.scopes.map(asString) } : {}),
|
|
56
|
+
...(obj.scopeWarning ? { scopeWarning: asString(obj.scopeWarning) } : {}),
|
|
48
57
|
...(obj.clientId ? { clientId: asString(obj.clientId) } : {}),
|
|
49
58
|
...(obj.clientSecret ? { clientSecret: asString(obj.clientSecret) } : {}),
|
|
50
59
|
...(obj.idToken ? { idToken: asString(obj.idToken) } : {}),
|
|
@@ -65,6 +74,15 @@ export function parseBlob(text) {
|
|
|
65
74
|
email: asString(obj.email),
|
|
66
75
|
accountId: asString(obj.accountId),
|
|
67
76
|
projectId: asString(obj.projectId),
|
|
77
|
+
selectedOrgId: asString(obj.selectedOrgId),
|
|
78
|
+
selectedOrgName: asString(obj.selectedOrgName),
|
|
79
|
+
...(Array.isArray(obj.organizations) ? { organizations: obj.organizations } : {}),
|
|
80
|
+
...(Array.isArray(obj.workspaces) ? { workspaces: obj.workspaces } : {}),
|
|
81
|
+
...(obj.organizationId ? { organizationId: asString(obj.organizationId) } : {}),
|
|
82
|
+
...(obj.plan ? { plan: asString(obj.plan) } : {}),
|
|
83
|
+
...(obj.avatarUrl ? { avatarUrl: asString(obj.avatarUrl) } : {}),
|
|
84
|
+
...(Array.isArray(obj.scopes) ? { scopes: obj.scopes.map(asString) } : {}),
|
|
85
|
+
...(obj.scopeWarning ? { scopeWarning: asString(obj.scopeWarning) } : {}),
|
|
68
86
|
...(obj.clientId ? { clientId: asString(obj.clientId) } : {}),
|
|
69
87
|
...(obj.clientSecret ? { clientSecret: asString(obj.clientSecret) } : {}),
|
|
70
88
|
...(obj.idToken ? { idToken: asString(obj.idToken) } : {}),
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Subscription Capability Matrix & Context Limit Inspector (#556)
|
|
2
|
+
// Determines per-account tier capabilities (context window, thinking mode, vision, tools)
|
|
3
|
+
// and dynamically adapts outgoing requests to prevent HTTP 400 Bad Request rejections.
|
|
4
|
+
|
|
5
|
+
export const PROVIDER_CAPABILITIES = {
|
|
6
|
+
codex: {
|
|
7
|
+
contextWindow: 128000,
|
|
8
|
+
contextLabel: '128K',
|
|
9
|
+
thinking: true,
|
|
10
|
+
thinkingType: 'low_med_high',
|
|
11
|
+
vision: true,
|
|
12
|
+
tools: true,
|
|
13
|
+
maxOutputTokens: 16384,
|
|
14
|
+
},
|
|
15
|
+
claude: {
|
|
16
|
+
contextWindow: 200000,
|
|
17
|
+
contextLabel: '200K',
|
|
18
|
+
thinking: true,
|
|
19
|
+
thinkingType: 'budget',
|
|
20
|
+
vision: true,
|
|
21
|
+
tools: true,
|
|
22
|
+
maxOutputTokens: 8192,
|
|
23
|
+
},
|
|
24
|
+
grok: {
|
|
25
|
+
contextWindow: 128000,
|
|
26
|
+
contextLabel: '128K',
|
|
27
|
+
thinking: true,
|
|
28
|
+
thinkingType: 'low_med_high',
|
|
29
|
+
vision: true,
|
|
30
|
+
tools: true,
|
|
31
|
+
maxOutputTokens: 8192,
|
|
32
|
+
},
|
|
33
|
+
antigravity: {
|
|
34
|
+
contextWindow: 1000000,
|
|
35
|
+
contextLabel: '1M',
|
|
36
|
+
thinking: true,
|
|
37
|
+
thinkingType: 'tiered',
|
|
38
|
+
vision: true,
|
|
39
|
+
tools: true,
|
|
40
|
+
maxOutputTokens: 8192,
|
|
41
|
+
},
|
|
42
|
+
copilot: {
|
|
43
|
+
contextWindow: 128000,
|
|
44
|
+
contextLabel: '128K',
|
|
45
|
+
thinking: false,
|
|
46
|
+
thinkingType: null,
|
|
47
|
+
vision: true,
|
|
48
|
+
tools: true,
|
|
49
|
+
maxOutputTokens: 4096,
|
|
50
|
+
},
|
|
51
|
+
kimi: {
|
|
52
|
+
contextWindow: 200000,
|
|
53
|
+
contextLabel: '200K',
|
|
54
|
+
thinking: true,
|
|
55
|
+
thinkingType: 'budget',
|
|
56
|
+
vision: false,
|
|
57
|
+
tools: true,
|
|
58
|
+
maxOutputTokens: 4096,
|
|
59
|
+
},
|
|
60
|
+
glm: {
|
|
61
|
+
contextWindow: 128000,
|
|
62
|
+
contextLabel: '128K',
|
|
63
|
+
thinking: false,
|
|
64
|
+
thinkingType: null,
|
|
65
|
+
vision: true,
|
|
66
|
+
tools: true,
|
|
67
|
+
maxOutputTokens: 4096,
|
|
68
|
+
},
|
|
69
|
+
cursor: {
|
|
70
|
+
contextWindow: 128000,
|
|
71
|
+
contextLabel: '128K',
|
|
72
|
+
thinking: true,
|
|
73
|
+
thinkingType: 'budget',
|
|
74
|
+
vision: true,
|
|
75
|
+
tools: true,
|
|
76
|
+
maxOutputTokens: 8192,
|
|
77
|
+
},
|
|
78
|
+
kiro: {
|
|
79
|
+
contextWindow: 128000,
|
|
80
|
+
contextLabel: '128K',
|
|
81
|
+
thinking: false,
|
|
82
|
+
thinkingType: null,
|
|
83
|
+
vision: false,
|
|
84
|
+
tools: true,
|
|
85
|
+
maxOutputTokens: 4096,
|
|
86
|
+
},
|
|
87
|
+
perplexity: {
|
|
88
|
+
contextWindow: 200000,
|
|
89
|
+
contextLabel: '200K',
|
|
90
|
+
thinking: true,
|
|
91
|
+
thinkingType: 'low_med_high',
|
|
92
|
+
vision: false,
|
|
93
|
+
tools: true,
|
|
94
|
+
maxOutputTokens: 8192,
|
|
95
|
+
},
|
|
96
|
+
jetbrains: {
|
|
97
|
+
contextWindow: 200000,
|
|
98
|
+
contextLabel: '200K',
|
|
99
|
+
thinking: true,
|
|
100
|
+
thinkingType: 'budget',
|
|
101
|
+
vision: true,
|
|
102
|
+
tools: true,
|
|
103
|
+
maxOutputTokens: 8192,
|
|
104
|
+
},
|
|
105
|
+
default: {
|
|
106
|
+
contextWindow: 128000,
|
|
107
|
+
contextLabel: '128K',
|
|
108
|
+
thinking: false,
|
|
109
|
+
thinkingType: null,
|
|
110
|
+
vision: true,
|
|
111
|
+
tools: true,
|
|
112
|
+
maxOutputTokens: 4096,
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Inspects account capabilities based on provider, token blob metadata and plan.
|
|
118
|
+
* @param {string} provider
|
|
119
|
+
* @param {any} [blob]
|
|
120
|
+
* @param {any} [info]
|
|
121
|
+
* @returns {object}
|
|
122
|
+
*/
|
|
123
|
+
export function inspectAccountCapabilities(provider, blob = {}, info = {}) {
|
|
124
|
+
const p = String(provider || '').toLowerCase()
|
|
125
|
+
const base = PROVIDER_CAPABILITIES[p] || PROVIDER_CAPABILITIES.default
|
|
126
|
+
const plan = String(blob?.plan || blob?.paidTierName || info?.plan || info?.paidTierName || '').toLowerCase()
|
|
127
|
+
|
|
128
|
+
let contextWindow = base.contextWindow
|
|
129
|
+
let contextLabel = base.contextLabel
|
|
130
|
+
let thinking = base.thinking
|
|
131
|
+
let thinkingType = base.thinkingType
|
|
132
|
+
let vision = base.vision
|
|
133
|
+
let tools = base.tools
|
|
134
|
+
let maxOutputTokens = base.maxOutputTokens
|
|
135
|
+
|
|
136
|
+
// Tier upgrades for Team / Enterprise / Ultra subscriptions
|
|
137
|
+
if (p === 'claude') {
|
|
138
|
+
if (plan.includes('team') || plan.includes('enterprise') || plan.includes('500k')) {
|
|
139
|
+
contextWindow = 500000
|
|
140
|
+
contextLabel = '500K'
|
|
141
|
+
}
|
|
142
|
+
} else if (p === 'antigravity') {
|
|
143
|
+
if (plan.includes('ultra') || plan.includes('enterprise') || plan.includes('2m')) {
|
|
144
|
+
contextWindow = 2000000
|
|
145
|
+
contextLabel = '2M'
|
|
146
|
+
}
|
|
147
|
+
} else if (p === 'grok') {
|
|
148
|
+
if (plan.includes('pro') || plan.includes('team') || plan.includes('1m')) {
|
|
149
|
+
contextWindow = 1000000
|
|
150
|
+
contextLabel = '1M'
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const tags = []
|
|
155
|
+
tags.push(`${contextLabel} Context`)
|
|
156
|
+
if (thinking) {
|
|
157
|
+
tags.push(thinkingType === 'budget' ? 'Extended Thinking' : (thinkingType === 'tiered' ? 'Tiered Thinking' : 'Adaptive Thinking'))
|
|
158
|
+
}
|
|
159
|
+
if (vision) tags.push('Vision')
|
|
160
|
+
if (tools) tags.push('Tools')
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
provider: p,
|
|
164
|
+
contextWindow,
|
|
165
|
+
contextLabel,
|
|
166
|
+
thinking,
|
|
167
|
+
thinkingType,
|
|
168
|
+
vision,
|
|
169
|
+
tools,
|
|
170
|
+
maxOutputTokens,
|
|
171
|
+
tags,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Normalizes an outgoing stream request according to account capabilities to avoid 400 errors.
|
|
177
|
+
* @param {any} options
|
|
178
|
+
* @param {any} capabilities
|
|
179
|
+
* @returns {object}
|
|
180
|
+
*/
|
|
181
|
+
export function normalizeCapabilitiesRequest(options = {}, capabilities = {}) {
|
|
182
|
+
if (!options || typeof options !== 'object') return options
|
|
183
|
+
const out = { ...options }
|
|
184
|
+
|
|
185
|
+
// If upstream does not support tools, strip tool parameters
|
|
186
|
+
if (capabilities && capabilities.tools === false) {
|
|
187
|
+
if (out.tools) delete out.tools
|
|
188
|
+
if (out.tool_choice) delete out.tool_choice
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// If upstream does not support reasoning/thinking, strip reasoning_effort
|
|
192
|
+
if (capabilities && capabilities.thinking === false) {
|
|
193
|
+
if (out.reasoning_effort) delete out.reasoning_effort
|
|
194
|
+
if (out.thinking) delete out.thinking
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Clamp maxTokens if exceeding maximum supported output tokens
|
|
198
|
+
if (capabilities && typeof capabilities.maxOutputTokens === 'number' && capabilities.maxOutputTokens > 0) {
|
|
199
|
+
if (typeof out.max_tokens === 'number' && out.max_tokens > capabilities.maxOutputTokens) {
|
|
200
|
+
out.max_tokens = capabilities.maxOutputTokens
|
|
201
|
+
}
|
|
202
|
+
if (typeof out.maxTokens === 'number' && out.maxTokens > capabilities.maxOutputTokens) {
|
|
203
|
+
out.maxTokens = capabilities.maxOutputTokens
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return out
|
|
208
|
+
}
|