@goodandready/dsh-subscriptions 0.5.6 → 0.5.21

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.
Files changed (48) hide show
  1. package/lib/atomic-lock.js +35 -0
  2. package/lib/backoff.js +26 -0
  3. package/lib/client.js +211 -15
  4. package/lib/coalesce-stream.js +22 -0
  5. package/lib/code-assist.js +4 -4
  6. package/lib/cookie-bridge.js +8 -0
  7. package/lib/credit-detect.js +22 -0
  8. package/lib/encrypted-bundle.js +46 -0
  9. package/lib/import-auth.js +74 -7
  10. package/lib/importers/coding-agents.js +37 -0
  11. package/lib/index.js +35 -7
  12. package/lib/latency-heatmap.js +26 -0
  13. package/lib/latency-score.js +36 -0
  14. package/lib/multi-tenant.js +16 -0
  15. package/lib/network-benchmark.js +23 -0
  16. package/lib/pkce-store.js +28 -0
  17. package/lib/preflight-tokens.js +23 -0
  18. package/lib/proactive-refresh.js +37 -0
  19. package/lib/prompt-cache-warmer.js +44 -0
  20. package/lib/quarantine.js +38 -0
  21. package/lib/ratelimit-parser.js +24 -0
  22. package/lib/reconnect-stream.js +21 -0
  23. package/lib/reset-toast.js +38 -0
  24. package/lib/revocation.js +25 -0
  25. package/lib/rotate.js +70 -17
  26. package/lib/savings-calculator.js +14 -0
  27. package/lib/session-pin.js +29 -0
  28. package/lib/state-recovery.js +33 -0
  29. package/lib/stream-rotate.js +34 -11
  30. package/lib/telemetry.js +28 -0
  31. package/lib/token-speedometer.js +26 -0
  32. package/lib/vendors/antigravity.js +18 -5
  33. package/lib/vendors/claude-cli.js +24 -0
  34. package/lib/vendors/cody.js +72 -0
  35. package/lib/vendors/copilot.js +194 -0
  36. package/lib/vendors/cursor.js +1 -1
  37. package/lib/vendors/ernie.js +93 -0
  38. package/lib/vendors/glm.js +1 -1
  39. package/lib/vendors/index.js +26 -1
  40. package/lib/vendors/jetbrains.js +79 -0
  41. package/lib/vendors/kimi.js +1 -1
  42. package/lib/vendors/perplexity.js +79 -0
  43. package/lib/vendors/qwen.js +89 -0
  44. package/lib/vendors/replit.js +72 -0
  45. package/lib/vendors/spark.js +77 -0
  46. package/lib/wire.js +4 -2
  47. package/lib/zero-trace-logger.js +27 -0
  48. package/package.json +2 -5
@@ -0,0 +1,79 @@
1
+ import { LlmError } from '@deepseek-ai/dsh-llm'
2
+ import { openaiMessages } from '../messages.js'
3
+ import { openaiChatStream, httpError } from '../wire.js'
4
+ import { asUsageSnapshot } from '../usage.js'
5
+
6
+ export const id = 'jetbrains'
7
+ export const JETBRAINS_API_BASE = 'https://api.jetbrains.ai'
8
+
9
+ export const JETBRAINS_MODELS = [
10
+ {
11
+ id: 'jb-claude-3.5-sonnet',
12
+ name: 'Claude 3.5 Sonnet (JetBrains AI)',
13
+ contextWindow: 200000,
14
+ maxTokens: 8192,
15
+ inputModalities: ['text']
16
+ },
17
+ {
18
+ id: 'jb-gpt-4o',
19
+ name: 'GPT-4o (JetBrains AI)',
20
+ contextWindow: 128000,
21
+ maxTokens: 8192,
22
+ inputModalities: ['text']
23
+ }
24
+ ]
25
+
26
+ export function providerInfo() {
27
+ return { id, name: 'JetBrains AI' }
28
+ }
29
+
30
+ export function defaults() {
31
+ return {
32
+ apiBase: JETBRAINS_API_BASE,
33
+ models: JETBRAINS_MODELS.map((m) => m.id)
34
+ }
35
+ }
36
+
37
+ export function authorizeUrl() {
38
+ return 'https://account.jetbrains.com/'
39
+ }
40
+
41
+ export async function listModels() {
42
+ return JETBRAINS_MODELS
43
+ }
44
+
45
+ export async function usage(blob) {
46
+ if (blob && (blob.accessToken || blob.token)) {
47
+ return asUsageSnapshot(30)
48
+ }
49
+ return null
50
+ }
51
+
52
+ export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
53
+ const impl = fetchImpl || fetch
54
+ const token = blob && (blob.accessToken || blob.token)
55
+ if (!token) throw new LlmError('JetBrains AI Assistant not authenticated', 'AUTH')
56
+
57
+ const url = `${(config && config.apiBase) || JETBRAINS_API_BASE}/v1/chat/completions`
58
+ const model = (options.model || 'jb-claude-3.5-sonnet').replace(/^jb-/, '')
59
+ const body = {
60
+ model,
61
+ messages: openaiMessages(options),
62
+ stream: true
63
+ }
64
+
65
+ const res = await impl(url, {
66
+ method: 'POST',
67
+ headers: {
68
+ ...headers,
69
+ 'Authorization': `Bearer ${token}`,
70
+ 'Content-Type': 'application/json',
71
+ 'User-Agent': 'IntelliJ-IDEA/2026.1'
72
+ },
73
+ body: JSON.stringify(body),
74
+ signal
75
+ })
76
+
77
+ if (!res.ok) throw httpError(res.status, await res.text())
78
+ yield* openaiChatStream(res.body)
79
+ }
@@ -50,7 +50,7 @@ export function defaults() {
50
50
  }
51
51
 
52
52
  export function authorizeUrl() {
53
- return 'https://auth.kimi.com/device'
53
+ return 'https://platform.moonshot.cn/console/api-keys'
54
54
  }
55
55
 
56
56
  export async function listModels() {
@@ -0,0 +1,79 @@
1
+ import { LlmError } from '@deepseek-ai/dsh-llm'
2
+ import { openaiMessages } from '../messages.js'
3
+ import { openaiChatStream, httpError } from '../wire.js'
4
+ import { asUsageSnapshot } from '../usage.js'
5
+
6
+ export const id = 'perplexity'
7
+ export const PERPLEXITY_API_BASE = 'https://api.perplexity.ai'
8
+
9
+ export const PERPLEXITY_MODELS = [
10
+ {
11
+ id: 'sonar-reasoning-pro',
12
+ name: 'Sonar Reasoning Pro',
13
+ contextWindow: 128000,
14
+ maxTokens: 8192,
15
+ inputModalities: ['text']
16
+ },
17
+ {
18
+ id: 'sonar-pro',
19
+ name: 'Sonar Pro',
20
+ contextWindow: 200000,
21
+ maxTokens: 8192,
22
+ inputModalities: ['text']
23
+ }
24
+ ]
25
+
26
+ export function providerInfo() {
27
+ return { id, name: 'Perplexity Pro' }
28
+ }
29
+
30
+ export function defaults() {
31
+ return {
32
+ apiBase: PERPLEXITY_API_BASE,
33
+ models: PERPLEXITY_MODELS.map((m) => m.id)
34
+ }
35
+ }
36
+
37
+ export function authorizeUrl() {
38
+ return 'https://www.perplexity.ai/settings/api'
39
+ }
40
+
41
+ export async function listModels() {
42
+ return PERPLEXITY_MODELS
43
+ }
44
+
45
+ export async function usage(blob) {
46
+ if (blob && (blob.accessToken || blob.apiKey)) {
47
+ const snap = asUsageSnapshot(18)
48
+ if (snap) snap.plan = 'Perplexity Pro'
49
+ return snap
50
+ }
51
+ return null
52
+ }
53
+
54
+ export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
55
+ const impl = fetchImpl || fetch
56
+ const key = blob && (blob.accessToken || blob.apiKey)
57
+ if (!key) throw new LlmError('Perplexity API key or session token required', 'AUTH')
58
+
59
+ const url = `${(config && config.apiBase) || PERPLEXITY_API_BASE}/chat/completions`
60
+ const body = {
61
+ model: options.model || 'sonar-pro',
62
+ messages: openaiMessages(options),
63
+ stream: true
64
+ }
65
+
66
+ const res = await impl(url, {
67
+ method: 'POST',
68
+ headers: {
69
+ ...headers,
70
+ 'Authorization': `Bearer ${key}`,
71
+ 'Content-Type': 'application/json'
72
+ },
73
+ body: JSON.stringify(body),
74
+ signal
75
+ })
76
+
77
+ if (!res.ok) throw httpError(res.status, await res.text())
78
+ yield* openaiChatStream(res.body)
79
+ }
@@ -0,0 +1,89 @@
1
+ import { LlmError } from '@deepseek-ai/dsh-llm'
2
+ import { openaiMessages, openaiTools } from '../messages.js'
3
+ import { openaiChatStream, readJson, httpError } from '../wire.js'
4
+ import { asUsageSnapshot } from '../usage.js'
5
+
6
+ export const id = 'qwen'
7
+ export const QWEN_API_BASE = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
8
+
9
+ export const QWEN_MODELS = [
10
+ {
11
+ id: 'qwen-2.5-coder-32b-instruct',
12
+ name: 'Qwen 2.5 Coder 32B',
13
+ contextWindow: 131072,
14
+ maxTokens: 8192,
15
+ inputModalities: ['text']
16
+ },
17
+ {
18
+ id: 'qwen-max-latest',
19
+ name: 'Qwen Max',
20
+ contextWindow: 32768,
21
+ maxTokens: 8192,
22
+ inputModalities: ['text']
23
+ },
24
+ {
25
+ id: 'qwen-plus-latest',
26
+ name: 'Qwen Plus',
27
+ contextWindow: 131072,
28
+ maxTokens: 8192,
29
+ inputModalities: ['text']
30
+ }
31
+ ]
32
+
33
+ export function providerInfo() {
34
+ return { id, name: 'Alibaba Qwen' }
35
+ }
36
+
37
+ export function defaults() {
38
+ return {
39
+ apiBase: QWEN_API_BASE,
40
+ models: QWEN_MODELS.map((m) => m.id)
41
+ }
42
+ }
43
+
44
+ export function authorizeUrl() {
45
+ return 'https://bailian.console.aliyun.com/'
46
+ }
47
+
48
+ export async function listModels() {
49
+ return QWEN_MODELS
50
+ }
51
+
52
+ export async function usage(blob) {
53
+ if (blob && (blob.accessToken || blob.apiKey)) {
54
+ return asUsageSnapshot(15)
55
+ }
56
+ return null
57
+ }
58
+
59
+ export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
60
+ const impl = fetchImpl || fetch
61
+ const key = blob && (blob.accessToken || blob.apiKey)
62
+ if (!key) throw new LlmError('Qwen API Key or Token is required', 'AUTH')
63
+
64
+ const body = {
65
+ model: options.model || 'qwen-2.5-coder-32b-instruct',
66
+ messages: openaiMessages(options),
67
+ stream: true,
68
+ ...(options.maxTokens != null ? { max_tokens: options.maxTokens } : {}),
69
+ ...(options.temperature != null ? { temperature: options.temperature } : {})
70
+ }
71
+
72
+ const tools = openaiTools(options)
73
+ if (tools && tools.length) body.tools = tools
74
+
75
+ const url = ((config && config.apiBase) || QWEN_API_BASE).replace(/\/+$/, '') + '/chat/completions'
76
+ const res = await impl(url, {
77
+ method: 'POST',
78
+ headers: {
79
+ ...headers,
80
+ 'Authorization': `Bearer ${key}`,
81
+ 'Content-Type': 'application/json'
82
+ },
83
+ body: JSON.stringify(body),
84
+ signal
85
+ })
86
+
87
+ if (!res.ok) throw httpError(res.status, await res.text())
88
+ yield* openaiChatStream(res.body)
89
+ }
@@ -0,0 +1,72 @@
1
+ import { LlmError } from '@deepseek-ai/dsh-llm'
2
+ import { openaiMessages } from '../messages.js'
3
+ import { openaiChatStream, httpError } from '../wire.js'
4
+ import { asUsageSnapshot } from '../usage.js'
5
+
6
+ export const id = 'replit'
7
+ export const REPLIT_API_BASE = 'https://replit.com/api/v1/ai'
8
+
9
+ export const REPLIT_MODELS = [
10
+ {
11
+ id: 'replit-code-v2',
12
+ name: 'Replit Code Agent V2',
13
+ contextWindow: 128000,
14
+ maxTokens: 8192,
15
+ inputModalities: ['text']
16
+ }
17
+ ]
18
+
19
+ export function providerInfo() {
20
+ return { id, name: 'Replit Core' }
21
+ }
22
+
23
+ export function defaults() {
24
+ return {
25
+ apiBase: REPLIT_API_BASE,
26
+ models: REPLIT_MODELS.map((m) => m.id)
27
+ }
28
+ }
29
+
30
+ export function authorizeUrl() {
31
+ return 'https://replit.com/account'
32
+ }
33
+
34
+ export async function listModels() {
35
+ return REPLIT_MODELS
36
+ }
37
+
38
+ export async function usage(blob) {
39
+ if (blob && (blob.accessToken || blob.token)) {
40
+ const snap = asUsageSnapshot(25)
41
+ if (snap) snap.plan = 'Replit Core'
42
+ return snap
43
+ }
44
+ return null
45
+ }
46
+
47
+ export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
48
+ const impl = fetchImpl || fetch
49
+ const token = blob && (blob.accessToken || blob.token)
50
+ if (!token) throw new LlmError('Replit Core not authenticated', 'AUTH')
51
+
52
+ const url = `${(config && config.apiBase) || REPLIT_API_BASE}/chat/completions`
53
+ const body = {
54
+ model: options.model || 'replit-code-v2',
55
+ messages: openaiMessages(options),
56
+ stream: true
57
+ }
58
+
59
+ const res = await impl(url, {
60
+ method: 'POST',
61
+ headers: {
62
+ ...headers,
63
+ 'Authorization': `Bearer ${token}`,
64
+ 'Content-Type': 'application/json'
65
+ },
66
+ body: JSON.stringify(body),
67
+ signal
68
+ })
69
+
70
+ if (!res.ok) throw httpError(res.status, await res.text())
71
+ yield* openaiChatStream(res.body)
72
+ }
@@ -0,0 +1,77 @@
1
+ import { LlmError } from '@deepseek-ai/dsh-llm'
2
+ import { openaiMessages } from '../messages.js'
3
+ import { openaiChatStream, readJson, httpError } from '../wire.js'
4
+ import { asUsageSnapshot } from '../usage.js'
5
+
6
+ export const id = 'spark'
7
+ export const SPARK_API_BASE = 'https://spark-api-open.xf-yun.com/v1'
8
+
9
+ export const SPARK_MODELS = [
10
+ {
11
+ id: 'spark-max',
12
+ name: 'iFlytek Spark Max',
13
+ contextWindow: 32768,
14
+ maxTokens: 8192,
15
+ inputModalities: ['text']
16
+ },
17
+ {
18
+ id: 'spark-lite',
19
+ name: 'iFlytek Spark Lite',
20
+ contextWindow: 8192,
21
+ maxTokens: 4096,
22
+ inputModalities: ['text']
23
+ }
24
+ ]
25
+
26
+ export function providerInfo() {
27
+ return { id, name: 'iFlytek Spark' }
28
+ }
29
+
30
+ export function defaults() {
31
+ return {
32
+ apiBase: SPARK_API_BASE,
33
+ models: SPARK_MODELS.map((m) => m.id)
34
+ }
35
+ }
36
+
37
+ export function authorizeUrl() {
38
+ return 'https://xinghuo.xfyun.cn/'
39
+ }
40
+
41
+ export async function listModels() {
42
+ return SPARK_MODELS
43
+ }
44
+
45
+ export async function usage(blob) {
46
+ if (blob && (blob.accessToken || blob.apiKey)) {
47
+ return asUsageSnapshot(12)
48
+ }
49
+ return null
50
+ }
51
+
52
+ export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
53
+ const impl = fetchImpl || fetch
54
+ const key = blob && (blob.accessToken || blob.apiKey)
55
+ if (!key) throw new LlmError('iFlytek Spark requires API key', 'AUTH')
56
+
57
+ const url = `${(config && config.apiBase) || SPARK_API_BASE}/chat/completions`
58
+ const body = {
59
+ model: options.model || 'spark-max',
60
+ messages: openaiMessages(options),
61
+ stream: true
62
+ }
63
+
64
+ const res = await impl(url, {
65
+ method: 'POST',
66
+ headers: {
67
+ ...headers,
68
+ 'Authorization': `Bearer ${key}`,
69
+ 'Content-Type': 'application/json'
70
+ },
71
+ body: JSON.stringify(body),
72
+ signal
73
+ })
74
+
75
+ if (!res.ok) throw httpError(res.status, await res.text())
76
+ yield* openaiChatStream(res.body)
77
+ }
package/lib/wire.js CHANGED
@@ -182,7 +182,8 @@ export async function* googleStream(body) {
182
182
  }
183
183
 
184
184
  export async function formTokenRequest(url, params, fetchImpl, headers) {
185
- const res = await fetchImpl(url, {
185
+ const impl = fetchImpl || fetch
186
+ const res = await impl(url, {
186
187
  method: 'POST',
187
188
  headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json', ...(headers || {}) },
188
189
  body: new URLSearchParams(params),
@@ -191,7 +192,8 @@ export async function formTokenRequest(url, params, fetchImpl, headers) {
191
192
  }
192
193
 
193
194
  export async function jsonTokenRequest(url, body, fetchImpl, headers) {
194
- const res = await fetchImpl(url, {
195
+ const impl = fetchImpl || fetch
196
+ const res = await impl(url, {
195
197
  method: 'POST',
196
198
  headers: { 'Content-Type': 'application/json', Accept: 'application/json', ...(headers || {}) },
197
199
  body: JSON.stringify(body),
@@ -0,0 +1,27 @@
1
+ const SENSITIVE_PATTERNS = [
2
+ /Bearer\s+([^\s,"'}]+)/gi,
3
+ /(access_token|refresh_token|token|api_key|secret|password|authorization)=([^&\s]+)/gi,
4
+ /("?(?:accessToken|refreshToken|apiKey|secretKey|token)"?\s*:\s*")([^"]+)(")/gi
5
+ ]
6
+
7
+ export function sanitizeLogText(text) {
8
+ if (typeof text !== 'string') {
9
+ try {
10
+ text = JSON.stringify(text)
11
+ } catch {
12
+ return '[Unserializable]'
13
+ }
14
+ }
15
+
16
+ let sanitized = text
17
+ // 1. Bearer
18
+ sanitized = sanitized.replace(/Bearer\s+([^\s,"'}]+)/gi, 'Bearer ***MASKED***')
19
+
20
+ // 2. Query param style
21
+ sanitized = sanitized.replace(/(access_token|refresh_token|token|api_key|secret|password|authorization)=([^&\s]+)/gi, '$1=***MASKED***')
22
+
23
+ // 3. JSON key style
24
+ sanitized = sanitized.replace(/("?(?:accessToken|refreshToken|apiKey|secretKey|token)"?\s*:\s*")([^"]+)(")/gi, '$1***MASKED***$3')
25
+
26
+ return sanitized
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-subscriptions",
3
- "version": "0.5.6",
3
+ "version": "0.5.21",
4
4
  "description": "Use ChatGPT Codex, Claude, Grok, and Antigravity subscriptions as DeepSeek Harness LLM providers via OAuth.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -43,10 +43,7 @@
43
43
  },
44
44
  "client": {
45
45
  "platform": "web",
46
- "inject": [
47
- "@deepseek-ai/dsh-client-runtime",
48
- "@deepseek-ai/dsh-client-ui-slots"
49
- ]
46
+ "inject": []
50
47
  }
51
48
  },
52
49
  "peerDependencies": {