@goodandready/dsh-subscriptions 0.5.5 → 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 (50) 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 +110 -5
  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/refs.js +2 -0
  24. package/lib/reset-toast.js +38 -0
  25. package/lib/revocation.js +25 -0
  26. package/lib/rotate.js +70 -17
  27. package/lib/savings-calculator.js +14 -0
  28. package/lib/session-pin.js +29 -0
  29. package/lib/state-recovery.js +33 -0
  30. package/lib/stream-rotate.js +34 -11
  31. package/lib/telemetry.js +28 -0
  32. package/lib/token-speedometer.js +26 -0
  33. package/lib/vendors/antigravity.js +18 -5
  34. package/lib/vendors/claude-cli.js +24 -0
  35. package/lib/vendors/cody.js +72 -0
  36. package/lib/vendors/copilot.js +194 -0
  37. package/lib/vendors/cursor.js +1 -1
  38. package/lib/vendors/ernie.js +93 -0
  39. package/lib/vendors/glm.js +1 -1
  40. package/lib/vendors/index.js +27 -1
  41. package/lib/vendors/jetbrains.js +79 -0
  42. package/lib/vendors/kimi.js +1 -1
  43. package/lib/vendors/kiro.js +116 -0
  44. package/lib/vendors/perplexity.js +79 -0
  45. package/lib/vendors/qwen.js +89 -0
  46. package/lib/vendors/replit.js +72 -0
  47. package/lib/vendors/spark.js +77 -0
  48. package/lib/wire.js +4 -2
  49. package/lib/zero-trace-logger.js +27 -0
  50. package/package.json +2 -5
@@ -1,4 +1,13 @@
1
+ import * as kiro from './kiro.js'
1
2
  import * as cursor from './cursor.js'
3
+ import * as copilot from './copilot.js'
4
+ import * as qwen from './qwen.js'
5
+ import * as ernie from './ernie.js'
6
+ import * as spark from './spark.js'
7
+ import * as jetbrains from './jetbrains.js'
8
+ import * as perplexity from './perplexity.js'
9
+ import * as replit from './replit.js'
10
+ import * as cody from './cody.js'
2
11
  import * as codex from './codex.js'
3
12
  import * as claude from './claude.js'
4
13
  import * as grok from './grok.js'
@@ -7,7 +16,24 @@ import * as kimi from './kimi.js'
7
16
  import * as glm from './glm.js'
8
17
  import { createVendorFromProfile } from '../vendor-factory.js'
9
18
 
10
- const builtins = { codex, claude, grok, antigravity, kimi, glm, cursor }
19
+ const builtins = {
20
+ codex,
21
+ claude,
22
+ grok,
23
+ antigravity,
24
+ kimi,
25
+ glm,
26
+ cursor,
27
+ kiro,
28
+ copilot,
29
+ qwen,
30
+ ernie,
31
+ spark,
32
+ jetbrains,
33
+ perplexity,
34
+ replit,
35
+ cody
36
+ }
11
37
 
12
38
  const customVendors = new Map()
13
39
 
@@ -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,116 @@
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 = 'kiro'
7
+
8
+ export const KIRO_PORTAL_URL = 'https://app.kiro.dev'
9
+ export const KIRO_API_BASE = 'https://prod.us-east-1.auth.desktop.kiro.dev'
10
+ export const KIRO_MODELS = [
11
+ {
12
+ id: 'claude-sonnet-5',
13
+ name: 'Claude Sonnet 5 (Kiro)',
14
+ contextWindow: 1000000,
15
+ maxTokens: 64000,
16
+ inputModalities: ['text', 'image'],
17
+ reasoning: { efforts: [{ id: 'off', name: 'Off' }, { id: 'low', name: 'Low' }, { id: 'medium', name: 'Medium' }, { id: 'high', name: 'High' }, { id: 'max', name: 'Max' }] },
18
+ },
19
+ {
20
+ id: 'claude-opus-5',
21
+ name: 'Claude Opus 5 (Kiro)',
22
+ contextWindow: 1000000,
23
+ maxTokens: 64000,
24
+ inputModalities: ['text', 'image'],
25
+ },
26
+ {
27
+ id: 'gpt-5.6-luna',
28
+ name: 'GPT-5.6 Luna (Kiro)',
29
+ contextWindow: 272000,
30
+ maxTokens: 64000,
31
+ inputModalities: ['text', 'image'],
32
+ },
33
+ {
34
+ id: 'claude-sonnet-4.6',
35
+ name: 'Claude Sonnet 4.6 (Kiro)',
36
+ contextWindow: 200000,
37
+ maxTokens: 64000,
38
+ inputModalities: ['text'],
39
+ },
40
+ ]
41
+
42
+ export function providerInfo() {
43
+ return { id, name: 'AWS Kiro' }
44
+ }
45
+
46
+ export function defaults() {
47
+ return {
48
+ apiBase: KIRO_API_BASE,
49
+ models: KIRO_MODELS.map((m) => m.id),
50
+ }
51
+ }
52
+
53
+ export function authorizeUrl() {
54
+ return `${KIRO_PORTAL_URL}/oauth/authorize`
55
+ }
56
+
57
+ export async function listModels() {
58
+ return KIRO_MODELS
59
+ }
60
+
61
+ export async function usage(blob, config, fetchImpl) {
62
+ try {
63
+ const impl = fetchImpl || fetch
64
+ const token = blob && (blob.accessToken || blob.access_token)
65
+ if (!token) return null
66
+ const res = await impl(`${(config && config.apiBase) || KIRO_API_BASE}/api/usage`, {
67
+ headers: {
68
+ Authorization: `Bearer ${token}`,
69
+ Accept: 'application/json',
70
+ },
71
+ })
72
+ if (!res.ok) return null
73
+ const json = await readJson(res)
74
+ if (json && json.usedPercent != null) {
75
+ const snap = asUsageSnapshot(Math.round(json.usedPercent))
76
+ if (snap) snap.plan = json.plan || 'Pro'
77
+ return snap
78
+ }
79
+ return null
80
+ } catch {
81
+ return null
82
+ }
83
+ }
84
+
85
+ export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
86
+ const impl = fetchImpl || fetch
87
+ const token = blob && (blob.accessToken || blob.access_token)
88
+ if (!token) throw new LlmError('AWS Kiro not authenticated', 'AUTH')
89
+
90
+ const body = {
91
+ model: options.model || 'claude-sonnet-5',
92
+ messages: openaiMessages(options),
93
+ stream: true,
94
+ ...(options.maxTokens != null ? { max_tokens: options.maxTokens } : {}),
95
+ ...(options.temperature != null ? { temperature: options.temperature } : {}),
96
+ }
97
+
98
+ const tools = openaiTools(options)
99
+ if (tools && tools.length) body.tools = tools
100
+
101
+ const url = (config && config.chatUrl) || `${(config && config.apiBase) || KIRO_API_BASE}/v1/chat/completions`
102
+ const res = await impl(url, {
103
+ method: 'POST',
104
+ headers: {
105
+ ...headers,
106
+ Authorization: `Bearer ${token}`,
107
+ 'Content-Type': 'application/json',
108
+ 'User-Agent': 'dsh-plugin-oauth-subs',
109
+ },
110
+ body: JSON.stringify(body),
111
+ signal,
112
+ })
113
+
114
+ if (!res.ok) throw httpError(res.status, await res.text())
115
+ yield* openaiChatStream(res.body)
116
+ }
@@ -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.5",
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": {