@goodandready/dsh-subscriptions 0.1.0
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/LICENSE +21 -0
- package/README.md +102 -0
- package/cordis.patch.yml +8 -0
- package/lib/accounts.js +198 -0
- package/lib/adapter.js +110 -0
- package/lib/blob.js +43 -0
- package/lib/client.js +248 -0
- package/lib/code-assist.js +250 -0
- package/lib/gemini-schema.js +67 -0
- package/lib/google-validation.js +117 -0
- package/lib/http.js +44 -0
- package/lib/index.js +422 -0
- package/lib/jwt.js +28 -0
- package/lib/messages.js +258 -0
- package/lib/oauth.js +68 -0
- package/lib/pkce.js +12 -0
- package/lib/refs.js +59 -0
- package/lib/responses-stream.js +175 -0
- package/lib/rotate.js +31 -0
- package/lib/sse.js +44 -0
- package/lib/stream-rotate.js +41 -0
- package/lib/usage.js +51 -0
- package/lib/vendors/antigravity-oauth.js +10 -0
- package/lib/vendors/antigravity.js +152 -0
- package/lib/vendors/claude.js +119 -0
- package/lib/vendors/codex.js +163 -0
- package/lib/vendors/grok.js +223 -0
- package/lib/vendors/index.js +17 -0
- package/lib/wire.js +200 -0
- package/package.json +63 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { buildAuthorizeUrl } from '../oauth.js'
|
|
2
|
+
import { modelCatalog, codexResponsesBody } from '../messages.js'
|
|
3
|
+
import { formTokenRequest, codexResponsesStream, httpError, tokenBlobFromOAuth, readJson } from '../wire.js'
|
|
4
|
+
import { emailFromToken } from '../jwt.js'
|
|
5
|
+
import { asUsageSnapshot, grokBillingPercent } from '../usage.js'
|
|
6
|
+
|
|
7
|
+
export const id = 'grok'
|
|
8
|
+
|
|
9
|
+
const AUTH = 'https://auth.x.ai/oauth2/authorize'
|
|
10
|
+
const TOKEN = 'https://auth.x.ai/oauth2/token'
|
|
11
|
+
const SCOPE = 'openid profile email offline_access grok-cli:access api:access conversations:read conversations:write'
|
|
12
|
+
const BILLING = 'https://cli-chat-proxy.grok.com/v1/billing?format=credits'
|
|
13
|
+
const MODELS = 'https://api.x.ai/v1/models'
|
|
14
|
+
const CLI_MODELS = 'https://cli-chat-proxy.grok.com/v1/models'
|
|
15
|
+
const CATALOG_TTL_MS = 5 * 60 * 1000
|
|
16
|
+
const catalogCache = new Map()
|
|
17
|
+
|
|
18
|
+
async function cliCatalogCached(blob, cfg, fetchImpl) {
|
|
19
|
+
const key = String(blob.accessToken || '').slice(-24)
|
|
20
|
+
const hit = catalogCache.get(key)
|
|
21
|
+
if (hit && Date.now() - hit.at < CATALOG_TTL_MS) return hit.map
|
|
22
|
+
const map = await cliCatalog(blob, cfg, fetchImpl)
|
|
23
|
+
if (map.size > 0) catalogCache.set(key, { at: Date.now(), map })
|
|
24
|
+
return map
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function grokReasoningBody(modelId, effort, catalogMap) {
|
|
28
|
+
const entry = catalogMap && catalogMap.get(modelId)
|
|
29
|
+
const reasoning = entry && entry.reasoning
|
|
30
|
+
if (!reasoning || !reasoning.efforts || !reasoning.efforts.length) return undefined
|
|
31
|
+
const allowed = new Set(reasoning.efforts.map((row) => row.id))
|
|
32
|
+
const pick = effort != null && effort !== '' ? String(effort) : ''
|
|
33
|
+
if (pick && allowed.has(pick)) return { effort: pick }
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function grokBodyForModel(modelId, body) {
|
|
38
|
+
if (!/multi-agent/i.test(String(modelId || ''))) return body
|
|
39
|
+
const next = { ...body }
|
|
40
|
+
delete next.tools
|
|
41
|
+
return next
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
export function providerInfo() {
|
|
46
|
+
return { id, name: 'Grok' }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function defaults() {
|
|
50
|
+
return {
|
|
51
|
+
clientId: 'b1a00492-073a-47ea-816f-4c329264a828',
|
|
52
|
+
redirectUri: 'http://127.0.0.1:56121/callback',
|
|
53
|
+
baseUrl: 'https://api.x.ai/v1',
|
|
54
|
+
clientVersion: '0.2.103',
|
|
55
|
+
models: [
|
|
56
|
+
{ id: 'grok-4', name: 'Grok 4' },
|
|
57
|
+
{ id: 'grok-4-fast-reasoning', name: 'Grok 4 Fast Reasoning' },
|
|
58
|
+
{ id: 'grok-code-fast-1', name: 'Grok Code Fast 1' },
|
|
59
|
+
],
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isChatModel(modelId) {
|
|
64
|
+
return !/imagine|image-|video|embed/i.test(String(modelId || ''))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function grokModalities(modelId) {
|
|
68
|
+
return /code|embed/i.test(String(modelId || '')) ? ['text'] : ['text', 'image']
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function identityHeaders(blob, config) {
|
|
72
|
+
return {
|
|
73
|
+
Authorization: `Bearer ${blob.accessToken}`,
|
|
74
|
+
'X-XAI-Token-Auth': 'xai-grok-cli',
|
|
75
|
+
'x-grok-client-identifier': 'grok-shell',
|
|
76
|
+
'x-grok-client-version': config.clientVersion || defaults().clientVersion,
|
|
77
|
+
'User-Agent': 'xai-grok-cli',
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function streamHeaders(blob, config, extra) {
|
|
82
|
+
const base = String(config.baseUrl || defaults().baseUrl)
|
|
83
|
+
const headers = {
|
|
84
|
+
Authorization: `Bearer ${blob.accessToken}`,
|
|
85
|
+
...(extra || {}),
|
|
86
|
+
}
|
|
87
|
+
if (/grok\.com/i.test(base)) Object.assign(headers, identityHeaders(blob, config))
|
|
88
|
+
return headers
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function authorizeUrl(cfg, pkce) {
|
|
92
|
+
return buildAuthorizeUrl({
|
|
93
|
+
authUrl: AUTH,
|
|
94
|
+
clientId: cfg.clientId,
|
|
95
|
+
redirectUri: cfg.redirectUri,
|
|
96
|
+
challenge: pkce.challenge,
|
|
97
|
+
state: pkce.state,
|
|
98
|
+
scope: SCOPE,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function exchangeCode(cfg, pkce, code, fetchImpl) {
|
|
103
|
+
const json = await formTokenRequest(TOKEN, {
|
|
104
|
+
grant_type: 'authorization_code',
|
|
105
|
+
client_id: cfg.clientId,
|
|
106
|
+
code,
|
|
107
|
+
redirect_uri: cfg.redirectUri,
|
|
108
|
+
code_verifier: pkce.verifier,
|
|
109
|
+
}, fetchImpl)
|
|
110
|
+
const blob = tokenBlobFromOAuth(json)
|
|
111
|
+
return { ...blob, email: blob.email || emailFromToken(blob.accessToken), label: blob.label || 'Grok' }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function refresh(cfg, blob, fetchImpl) {
|
|
115
|
+
const json = await formTokenRequest(TOKEN, {
|
|
116
|
+
grant_type: 'refresh_token',
|
|
117
|
+
client_id: cfg.clientId,
|
|
118
|
+
refresh_token: blob.refreshToken,
|
|
119
|
+
}, fetchImpl)
|
|
120
|
+
return tokenBlobFromOAuth(json, { label: blob.label, email: blob.email })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function cliCatalog(blob, cfg, fetchImpl) {
|
|
124
|
+
const res = await fetchImpl(CLI_MODELS, {
|
|
125
|
+
headers: { ...identityHeaders(blob, cfg), Accept: 'application/json' },
|
|
126
|
+
})
|
|
127
|
+
const json = await readJson(res)
|
|
128
|
+
const map = new Map()
|
|
129
|
+
for (const entry of json.data || []) {
|
|
130
|
+
if (!entry || !entry.id) continue
|
|
131
|
+
const efforts = (entry.reasoning_efforts || [])
|
|
132
|
+
.map((level) => (typeof level === 'string' ? { id: level, name: level } : (level && level.value && {
|
|
133
|
+
id: level.value,
|
|
134
|
+
name: level.label || level.value,
|
|
135
|
+
})))
|
|
136
|
+
.filter(Boolean)
|
|
137
|
+
const reasoning = entry.supports_reasoning_effort === true && efforts.length
|
|
138
|
+
? {
|
|
139
|
+
efforts,
|
|
140
|
+
...(entry.reasoning_effort && efforts.some((row) => row.id === entry.reasoning_effort)
|
|
141
|
+
? { defaultEffort: entry.reasoning_effort }
|
|
142
|
+
: {}),
|
|
143
|
+
}
|
|
144
|
+
: undefined
|
|
145
|
+
map.set(entry.id, {
|
|
146
|
+
name: entry.name || entry.id,
|
|
147
|
+
...(entry.description ? { description: entry.description } : {}),
|
|
148
|
+
...(entry.context_window ? { contextWindow: entry.context_window } : {}),
|
|
149
|
+
...(reasoning ? { reasoning } : {}),
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
return map
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function listModels(blob, cfg, fetchImpl) {
|
|
156
|
+
const fallback = modelCatalog(id, (cfg.models || defaults().models).map((row) => (
|
|
157
|
+
typeof row === 'string' ? row : { ...row, inputModalities: grokModalities(row.id) }
|
|
158
|
+
)))
|
|
159
|
+
const impl = fetchImpl || fetch
|
|
160
|
+
try {
|
|
161
|
+
const [res, extra] = await Promise.all([
|
|
162
|
+
impl(MODELS, { headers: { Authorization: `Bearer ${blob.accessToken}`, Accept: 'application/json' } }),
|
|
163
|
+
cliCatalog(blob, cfg, impl).catch(() => new Map()),
|
|
164
|
+
])
|
|
165
|
+
const json = await readJson(res)
|
|
166
|
+
const rows = []
|
|
167
|
+
for (const entry of json.data || []) {
|
|
168
|
+
const modelId = entry && entry.id
|
|
169
|
+
if (!modelId || !isChatModel(modelId)) continue
|
|
170
|
+
const enrich = extra.get(modelId) || {}
|
|
171
|
+
const note = /multi-agent/i.test(modelId)
|
|
172
|
+
? 'Chat only in Harness until xAI grants multi-agent tool beta.'
|
|
173
|
+
: ''
|
|
174
|
+
rows.push({
|
|
175
|
+
id: modelId,
|
|
176
|
+
name: enrich.name || modelId,
|
|
177
|
+
inputModalities: grokModalities(modelId),
|
|
178
|
+
...enrich,
|
|
179
|
+
...(note ? { description: enrich.description ? `${enrich.description} ${note}` : note } : {}),
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
if (!rows.length) throw new Error('empty grok catalog')
|
|
183
|
+
return modelCatalog(id, rows)
|
|
184
|
+
} catch {
|
|
185
|
+
return fallback
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function usage(blob, cfg, fetchImpl) {
|
|
190
|
+
try {
|
|
191
|
+
const res = await (fetchImpl || fetch)(BILLING, {
|
|
192
|
+
headers: { ...identityHeaders(blob, cfg), Accept: 'application/json' },
|
|
193
|
+
})
|
|
194
|
+
const json = await readJson(res)
|
|
195
|
+
return asUsageSnapshot(grokBillingPercent(json))
|
|
196
|
+
} catch {
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
|
|
202
|
+
const base = (config.baseUrl || defaults().baseUrl).replace(/\/$/, '')
|
|
203
|
+
let body = codexResponsesBody(options, '')
|
|
204
|
+
if (!body.instructions) delete body.instructions
|
|
205
|
+
const catalog = await cliCatalogCached(blob, config, fetchImpl).catch(() => new Map())
|
|
206
|
+
const reasoning = grokReasoningBody(options.model, options.reasoningEffort, catalog)
|
|
207
|
+
if (reasoning) body.reasoning = reasoning
|
|
208
|
+
body = grokBodyForModel(options.model, body)
|
|
209
|
+
const res = await fetchImpl(`${base}/responses`, {
|
|
210
|
+
method: 'POST',
|
|
211
|
+
headers: {
|
|
212
|
+
...headers,
|
|
213
|
+
...streamHeaders(blob, config, {
|
|
214
|
+
'Content-Type': 'application/json',
|
|
215
|
+
Accept: 'text/event-stream',
|
|
216
|
+
}),
|
|
217
|
+
},
|
|
218
|
+
body: JSON.stringify(body),
|
|
219
|
+
signal,
|
|
220
|
+
})
|
|
221
|
+
if (!res.ok) throw httpError(res.status, await res.text())
|
|
222
|
+
yield* codexResponsesStream(res.body)
|
|
223
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as codex from './codex.js'
|
|
2
|
+
import * as claude from './claude.js'
|
|
3
|
+
import * as grok from './grok.js'
|
|
4
|
+
import * as antigravity from './antigravity.js'
|
|
5
|
+
|
|
6
|
+
export const vendors = {
|
|
7
|
+
codex,
|
|
8
|
+
claude,
|
|
9
|
+
grok,
|
|
10
|
+
antigravity,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getVendor(provider) {
|
|
14
|
+
const vendor = vendors[provider]
|
|
15
|
+
if (!vendor) throw new Error(`unknown provider: ${provider}`)
|
|
16
|
+
return vendor
|
|
17
|
+
}
|
package/lib/wire.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { iterateSse, jsonSse } from './sse.js'
|
|
2
|
+
import { validationFromHttpError, validationRequiredError, googleRateLimitMessage } from './google-validation.js'
|
|
3
|
+
|
|
4
|
+
export function httpError(status, bodyText, code) {
|
|
5
|
+
const snippet = String(bodyText || '').slice(0, 400)
|
|
6
|
+
const err = new Error(snippet ? `vendor http ${status}: ${snippet}` : `vendor http ${status}`)
|
|
7
|
+
err.status = status
|
|
8
|
+
if (code) {
|
|
9
|
+
err.code = code
|
|
10
|
+
return err
|
|
11
|
+
}
|
|
12
|
+
if (status === 429) err.code = 'RATE_LIMIT'
|
|
13
|
+
else if (status === 402) err.code = 'QUOTA'
|
|
14
|
+
else if (status === 401 || status === 403) err.code = 'VENDOR'
|
|
15
|
+
else if (/quota|rate.?limit|usage.?limit|billing/i.test(snippet)) {
|
|
16
|
+
err.code = status === 400 ? 'QUOTA' : 'RATE_LIMIT'
|
|
17
|
+
} else err.code = 'VENDOR'
|
|
18
|
+
return err
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function throwHttpError(status, bodyText) {
|
|
22
|
+
const validation = validationFromHttpError(status, bodyText)
|
|
23
|
+
if (validation) throw validationRequiredError(validation)
|
|
24
|
+
const hint = googleRateLimitMessage(status, bodyText)
|
|
25
|
+
if (hint) {
|
|
26
|
+
const err = httpError(status, bodyText, status === 429 ? 'RATE_LIMIT' : undefined)
|
|
27
|
+
err.message = `${hint} (${err.message})`
|
|
28
|
+
throw err
|
|
29
|
+
}
|
|
30
|
+
throw httpError(status, bodyText)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function readJson(res) {
|
|
34
|
+
const text = await res.text()
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
const validation = validationFromHttpError(res.status, text)
|
|
37
|
+
if (validation) throw validationRequiredError(validation)
|
|
38
|
+
throw httpError(res.status, text)
|
|
39
|
+
}
|
|
40
|
+
try { return JSON.parse(text) } catch { return {} }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function tokenBlobFromOAuth(json, extra) {
|
|
44
|
+
const expiresIn = Number(json.expires_in) || 3600
|
|
45
|
+
return {
|
|
46
|
+
accessToken: json.access_token || json.accessToken || '',
|
|
47
|
+
refreshToken: json.refresh_token || json.refreshToken || '',
|
|
48
|
+
expiresAt: Date.now() + expiresIn * 1000,
|
|
49
|
+
idToken: json.id_token || json.idToken || '',
|
|
50
|
+
...(extra || {}),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function openBlock(kind, index) {
|
|
55
|
+
return { kind, index, text: '', id: '', name: '' }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function* openaiChatStream(body) {
|
|
59
|
+
let next = 0
|
|
60
|
+
let textBlock = null
|
|
61
|
+
const tools = new Map()
|
|
62
|
+
let finish = { kind: 'stop' }
|
|
63
|
+
for await (const data of iterateSse(body)) {
|
|
64
|
+
const json = jsonSse(data)
|
|
65
|
+
if (!json) continue
|
|
66
|
+
const choice = (json.choices && json.choices[0]) || {}
|
|
67
|
+
const delta = choice.delta || json.delta || {}
|
|
68
|
+
if (typeof delta.content === 'string' && delta.content) {
|
|
69
|
+
if (!textBlock) {
|
|
70
|
+
textBlock = openBlock('text', next++)
|
|
71
|
+
yield { type: 'block-start', index: textBlock.index, blockType: 'text' }
|
|
72
|
+
}
|
|
73
|
+
yield { type: 'text-delta', index: textBlock.index, text: delta.content }
|
|
74
|
+
}
|
|
75
|
+
if (typeof delta.reasoning_content === 'string' && delta.reasoning_content) {
|
|
76
|
+
yield { type: 'reasoning-delta', index: 0, text: delta.reasoning_content }
|
|
77
|
+
}
|
|
78
|
+
for (const call of delta.tool_calls || []) {
|
|
79
|
+
let block = tools.get(call.index)
|
|
80
|
+
if (!block) {
|
|
81
|
+
block = openBlock('tool-call', next++)
|
|
82
|
+
tools.set(call.index, block)
|
|
83
|
+
yield { type: 'block-start', index: block.index, blockType: 'tool-call' }
|
|
84
|
+
}
|
|
85
|
+
if (call.id) block.id = call.id
|
|
86
|
+
if (call.function && call.function.name) block.name = call.function.name
|
|
87
|
+
const fragment = (call.function && call.function.arguments) || ''
|
|
88
|
+
yield {
|
|
89
|
+
type: 'tool-call-delta',
|
|
90
|
+
index: block.index,
|
|
91
|
+
id: block.id,
|
|
92
|
+
...(block.name ? { name: block.name } : {}),
|
|
93
|
+
argumentsDelta: fragment,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (json.usage) yield { type: 'usage', usage: json.usage }
|
|
97
|
+
if (choice.finish_reason === 'length') finish = { kind: 'length' }
|
|
98
|
+
else if (choice.finish_reason === 'tool_calls') finish = { kind: 'tool' }
|
|
99
|
+
}
|
|
100
|
+
yield { type: 'finish', reason: finish }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { streamResponses as codexResponsesStream } from './responses-stream.js'
|
|
104
|
+
|
|
105
|
+
export async function* anthropicStream(body) {
|
|
106
|
+
let index = 0
|
|
107
|
+
let textIndex = 0
|
|
108
|
+
for await (const data of iterateSse(body)) {
|
|
109
|
+
const json = jsonSse(data)
|
|
110
|
+
if (!json) continue
|
|
111
|
+
if (json.type === 'content_block_start') {
|
|
112
|
+
const block = json.content_block || {}
|
|
113
|
+
if (block.type === 'text') {
|
|
114
|
+
textIndex = json.index || index++
|
|
115
|
+
yield { type: 'block-start', index: textIndex, blockType: 'text' }
|
|
116
|
+
} else if (block.type === 'tool_use') {
|
|
117
|
+
yield { type: 'block-start', index: json.index || index++, blockType: 'tool-call' }
|
|
118
|
+
yield {
|
|
119
|
+
type: 'tool-call-delta',
|
|
120
|
+
index: json.index || 0,
|
|
121
|
+
id: block.id,
|
|
122
|
+
name: block.name,
|
|
123
|
+
argumentsDelta: '',
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} else if (json.type === 'content_block_delta') {
|
|
127
|
+
const delta = json.delta || {}
|
|
128
|
+
if (delta.type === 'text_delta' && delta.text) {
|
|
129
|
+
yield { type: 'text-delta', index: json.index || textIndex, text: delta.text }
|
|
130
|
+
} else if (delta.type === 'input_json_delta' && delta.partial_json) {
|
|
131
|
+
yield {
|
|
132
|
+
type: 'tool-call-delta',
|
|
133
|
+
index: json.index || 0,
|
|
134
|
+
argumentsDelta: delta.partial_json,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} else if (json.type === 'message_delta' && json.usage) {
|
|
138
|
+
yield { type: 'usage', usage: json.usage }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
yield { type: 'finish', reason: { kind: 'stop' } }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function* googleStream(body) {
|
|
145
|
+
let index = 0
|
|
146
|
+
let started = false
|
|
147
|
+
for await (const data of iterateSse(body)) {
|
|
148
|
+
const json = jsonSse(data)
|
|
149
|
+
const wrapped = json && json.response ? json.response : json
|
|
150
|
+
if (!wrapped) continue
|
|
151
|
+
const cand = wrapped.candidates && wrapped.candidates[0]
|
|
152
|
+
const parts = cand && cand.content && Array.isArray(cand.content.parts) ? cand.content.parts : []
|
|
153
|
+
for (const part of parts) {
|
|
154
|
+
if (part.text) {
|
|
155
|
+
if (!started) {
|
|
156
|
+
yield { type: 'block-start', index, blockType: 'text' }
|
|
157
|
+
started = true
|
|
158
|
+
}
|
|
159
|
+
yield { type: 'text-delta', index, text: part.text }
|
|
160
|
+
}
|
|
161
|
+
if (part.functionCall) {
|
|
162
|
+
yield { type: 'block-start', index: index + 1, blockType: 'tool-call' }
|
|
163
|
+
yield {
|
|
164
|
+
type: 'tool-call-delta',
|
|
165
|
+
index: index + 1,
|
|
166
|
+
name: part.functionCall.name,
|
|
167
|
+
argumentsDelta: JSON.stringify(part.functionCall.args || {}),
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (wrapped.usageMetadata) {
|
|
172
|
+
yield {
|
|
173
|
+
type: 'usage',
|
|
174
|
+
usage: {
|
|
175
|
+
input: wrapped.usageMetadata.promptTokenCount,
|
|
176
|
+
output: wrapped.usageMetadata.candidatesTokenCount,
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
yield { type: 'finish', reason: { kind: 'stop' } }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export async function formTokenRequest(url, params, fetchImpl, headers) {
|
|
185
|
+
const res = await fetchImpl(url, {
|
|
186
|
+
method: 'POST',
|
|
187
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json', ...(headers || {}) },
|
|
188
|
+
body: new URLSearchParams(params),
|
|
189
|
+
})
|
|
190
|
+
return readJson(res)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export async function jsonTokenRequest(url, body, fetchImpl, headers) {
|
|
194
|
+
const res = await fetchImpl(url, {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json', ...(headers || {}) },
|
|
197
|
+
body: JSON.stringify(body),
|
|
198
|
+
})
|
|
199
|
+
return readJson(res)
|
|
200
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Use ChatGPT Codex, Claude, Grok, and Antigravity subscriptions as DeepSeek Harness LLM providers via OAuth.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./lib/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./lib/index.js",
|
|
10
|
+
"./client": "./lib/client.js",
|
|
11
|
+
"./package.json": "./package.json",
|
|
12
|
+
"./cordis.patch.yml": "./cordis.patch.yml"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"lib/",
|
|
16
|
+
"cordis.patch.yml",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"dsh",
|
|
22
|
+
"dsh-plugin",
|
|
23
|
+
"deepseek-harness",
|
|
24
|
+
"oauth",
|
|
25
|
+
"chatgpt",
|
|
26
|
+
"claude",
|
|
27
|
+
"grok"
|
|
28
|
+
],
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/GooDAnDReaDY/dsh-subscriptions.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/GooDAnDReaDY/dsh-subscriptions",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/GooDAnDReaDY/dsh-subscriptions/issues"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"test": "node --test test/*.test.mjs"
|
|
39
|
+
},
|
|
40
|
+
"dsh": {
|
|
41
|
+
"bundle": {
|
|
42
|
+
"patch": "./cordis.patch.yml"
|
|
43
|
+
},
|
|
44
|
+
"client": {
|
|
45
|
+
"platform": "web",
|
|
46
|
+
"inject": [
|
|
47
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
48
|
+
"@deepseek-ai/dsh-client-ui-slots"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
54
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
55
|
+
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.6",
|
|
56
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6",
|
|
57
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
|
|
58
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|