@goodandready/dsh-subscriptions 0.6.19 → 0.6.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.
- package/lib/accounts.js +11 -5
- package/lib/adapter.js +5 -4
- package/lib/config-schema.js +1 -1
- package/lib/index.js +73 -16
- package/lib/messages.js +221 -45
- package/lib/vendors/codex.js +5 -1
- package/package.json +1 -1
package/lib/accounts.js
CHANGED
|
@@ -163,7 +163,8 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
async function listAccounts(provider) {
|
|
166
|
-
const
|
|
166
|
+
const curConfig = getConfig()
|
|
167
|
+
const slots = normalizeSlots(curConfig.slots).filter((s) => s.provider === provider)
|
|
167
168
|
const out = []
|
|
168
169
|
const now = Date.now()
|
|
169
170
|
for (const slot of slots) {
|
|
@@ -187,8 +188,10 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
async function loggedInProviders() {
|
|
191
|
+
const curConfig = getConfig()
|
|
192
|
+
const slots = normalizeSlots(curConfig.slots)
|
|
190
193
|
const found = new Set()
|
|
191
|
-
for (const slot of
|
|
194
|
+
for (const slot of slots) {
|
|
192
195
|
const info = await describeRef(slot.ref)
|
|
193
196
|
if (info.configured) found.add(slot.provider)
|
|
194
197
|
}
|
|
@@ -202,7 +205,8 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
202
205
|
if (refreshLocks.has(lockKey)) return refreshLocks.get(lockKey)
|
|
203
206
|
const promise = (async () => {
|
|
204
207
|
try {
|
|
205
|
-
const
|
|
208
|
+
const curConfig = getConfig()
|
|
209
|
+
const cfg = vendorConfig(provider, curConfig)
|
|
206
210
|
const next = await executeWithRetry(
|
|
207
211
|
() => getVendor(provider).refresh(cfg, blob, fetchFor(ref)),
|
|
208
212
|
{
|
|
@@ -238,9 +242,11 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
async function refreshUsage(provider) {
|
|
241
|
-
const
|
|
245
|
+
const curConfig = getConfig()
|
|
246
|
+
const cfg = vendorConfig(provider, curConfig)
|
|
242
247
|
const vendor = getVendor(provider)
|
|
243
|
-
|
|
248
|
+
const slots = normalizeSlots(curConfig.slots).filter((s) => s.provider === provider)
|
|
249
|
+
for (const slot of slots) {
|
|
244
250
|
const last = usageFetched.get(slot.ref) || 0
|
|
245
251
|
if (Date.now() - last < USAGE_TTL_MS) continue
|
|
246
252
|
let raw = ''
|
package/lib/adapter.js
CHANGED
|
@@ -43,15 +43,16 @@ export class SubscriptionAdapter extends LlmAdapter {
|
|
|
43
43
|
|
|
44
44
|
async listModels(provider) {
|
|
45
45
|
const accounts = await this.deps.listAccounts(provider)
|
|
46
|
-
|
|
46
|
+
const targetAccount = accounts.find((a) => a.hasToken)
|
|
47
|
+
if (!targetAccount) return []
|
|
47
48
|
const cfg = this.deps.vendorConfig(provider)
|
|
48
|
-
const fetchImpl = this.deps.
|
|
49
|
+
const fetchImpl = pickFetch(this.deps, targetAccount.ref)
|
|
49
50
|
let models = null
|
|
50
51
|
try {
|
|
51
52
|
const blob = await this.deps.ensureFresh(
|
|
52
53
|
provider,
|
|
53
|
-
await this.deps.loadBlob(
|
|
54
|
-
|
|
54
|
+
await this.deps.loadBlob(targetAccount.ref),
|
|
55
|
+
targetAccount.ref,
|
|
55
56
|
)
|
|
56
57
|
const live = await getVendor(provider).listModels(blob, cfg, fetchImpl)
|
|
57
58
|
if (Array.isArray(live) && live.length) models = live
|
package/lib/config-schema.js
CHANGED
|
@@ -82,7 +82,7 @@ export const Config = z.object({
|
|
|
82
82
|
})
|
|
83
83
|
|
|
84
84
|
export function publicConfig(cfg) {
|
|
85
|
-
const clone =
|
|
85
|
+
const clone = { ...cfg }
|
|
86
86
|
// #242: redact all *ClientSecret fields before exposing config via GET /config.
|
|
87
87
|
// Secrets must never leave the server; the UI does not need them.
|
|
88
88
|
for (const key of Object.keys(clone)) {
|
package/lib/index.js
CHANGED
|
@@ -64,22 +64,30 @@ const OK_HTML = '<!doctype html><meta charset="utf-8"><title>Subscriptions</titl
|
|
|
64
64
|
|
|
65
65
|
export function apply(ctx, config) {
|
|
66
66
|
const logger = ctx.logger ? ctx.logger('subscriptions') : console
|
|
67
|
-
let getConfig = () => config
|
|
68
67
|
let settingsApi
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
// #367: maintain a single immutable config snapshot without structuredClone on hot path
|
|
69
|
+
let cachedConfig = Config(config ?? {}) ?? config
|
|
70
|
+
|
|
71
|
+
function syncSnapshot(raw) {
|
|
72
|
+
if (raw !== undefined && raw !== null) {
|
|
73
|
+
try {
|
|
74
|
+
cachedConfig = Config(raw ?? {}) ?? config
|
|
75
|
+
} catch {
|
|
76
|
+
cachedConfig = config
|
|
77
|
+
}
|
|
78
|
+
} else if (settingsApi && typeof settingsApi.get === 'function') {
|
|
79
|
+
try {
|
|
80
|
+
const rawSettings = settingsApi.get()
|
|
81
|
+
cachedConfig = Config(rawSettings ?? {}) ?? config
|
|
82
|
+
} catch {
|
|
83
|
+
cachedConfig = config
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
syncCustomVendors()
|
|
87
|
+
return cachedConfig
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const live = () => cachedConfig
|
|
83
91
|
|
|
84
92
|
// Registering custom vendors from Config. Runs at startup and on
|
|
85
93
|
// every config change (replace) - the registry is rebuilt from scratch.
|
|
@@ -104,9 +112,58 @@ export function apply(ctx, config) {
|
|
|
104
112
|
}
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
const live = () => Config(structuredClone(getConfig() ?? {})) ?? config
|
|
108
115
|
syncCustomVendors()
|
|
109
116
|
|
|
117
|
+
ctx.inject(['settings'], (sctx) => {
|
|
118
|
+
const scope = sctx.settings.register(NS, Config, { base: config })
|
|
119
|
+
settingsApi = {
|
|
120
|
+
...scope,
|
|
121
|
+
get: () => (typeof scope.get === 'function' ? scope.get() : cachedConfig),
|
|
122
|
+
async replace(next) {
|
|
123
|
+
syncSnapshot(next)
|
|
124
|
+
if (typeof scope.replace === 'function') {
|
|
125
|
+
return await scope.replace(next)
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (typeof scope.get === 'function') {
|
|
131
|
+
syncSnapshot(scope.get())
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (typeof scope.watch === 'function') {
|
|
135
|
+
try {
|
|
136
|
+
const unwatch = scope.watch((next) => syncSnapshot(next))
|
|
137
|
+
if (typeof unwatch === 'function') {
|
|
138
|
+
sctx.effect(() => unwatch)
|
|
139
|
+
}
|
|
140
|
+
} catch { /* ignore watch error */ }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Promise.resolve().then(() => {
|
|
144
|
+
reconcileSlots().catch((err) => {
|
|
145
|
+
if (logger && logger.warn) logger.warn('[dsh-subscriptions] reconcileSlots initial error: ' + (err && err.message || err))
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const onDocUpdated = (ns) => {
|
|
150
|
+
if (!ns || ns === NS) {
|
|
151
|
+
syncSnapshot()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (typeof sctx.on === 'function') {
|
|
155
|
+
sctx.on('settings/document-updated', onDocUpdated)
|
|
156
|
+
} else if (typeof ctx.on === 'function') {
|
|
157
|
+
ctx.on('settings/document-updated', onDocUpdated)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
sctx.effect(() => () => {
|
|
161
|
+
settingsApi = undefined
|
|
162
|
+
cachedConfig = Config(config ?? {}) ?? config
|
|
163
|
+
syncCustomVendors()
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
110
167
|
// #88: fetch bound to the per-account proxy (slot.proxyUrl), or null for direct.
|
|
111
168
|
const fetchForRef = (ref) => {
|
|
112
169
|
const parsed = parseOauthRef(ref)
|
package/lib/messages.js
CHANGED
|
@@ -10,11 +10,13 @@ function flattenText(content) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function toolResults(content) {
|
|
13
|
-
|
|
13
|
+
if (!Array.isArray(content)) return []
|
|
14
|
+
return content.filter((b) => b && (b.type === 'tool-result' || b.type === 'tool_result' || b.type === 'function_call_output'))
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
function toolCalls(content) {
|
|
17
|
-
|
|
18
|
+
if (!Array.isArray(content)) return []
|
|
19
|
+
return content.filter((b) => b && (b.type === 'tool-call' || b.type === 'tool_use' || b.type === 'function_call'))
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export function openaiMessages(options) {
|
|
@@ -26,11 +28,21 @@ export function openaiMessages(options) {
|
|
|
26
28
|
continue
|
|
27
29
|
}
|
|
28
30
|
if (message.role === 'assistant') {
|
|
29
|
-
const calls =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const calls = [
|
|
32
|
+
...toolCalls(message.content).map((block) => ({
|
|
33
|
+
id: block.id,
|
|
34
|
+
type: 'function',
|
|
35
|
+
function: { name: block.name, arguments: block.arguments || '{}' },
|
|
36
|
+
})),
|
|
37
|
+
...((message.tool_calls || message.toolCalls || []).map((c) => ({
|
|
38
|
+
id: c.id || c.call_id,
|
|
39
|
+
type: 'function',
|
|
40
|
+
function: {
|
|
41
|
+
name: c.name || (c.function && c.function.name),
|
|
42
|
+
arguments: typeof c.arguments === 'string' ? c.arguments : (c.function && c.function.arguments) || '{}',
|
|
43
|
+
},
|
|
44
|
+
}))),
|
|
45
|
+
]
|
|
34
46
|
out.push({
|
|
35
47
|
role: 'assistant',
|
|
36
48
|
content: flattenText(message.content) || (calls.length ? null : ''),
|
|
@@ -38,14 +50,26 @@ export function openaiMessages(options) {
|
|
|
38
50
|
})
|
|
39
51
|
continue
|
|
40
52
|
}
|
|
53
|
+
if (message.role === 'tool') {
|
|
54
|
+
const toolCallId = String(message.toolCallId || message.tool_call_id || message.call_id || message.id || '')
|
|
55
|
+
const content = typeof message.content === 'string'
|
|
56
|
+
? message.content
|
|
57
|
+
: (flattenText(message.content) || JSON.stringify(message.content || ''))
|
|
58
|
+
out.push({
|
|
59
|
+
role: 'tool',
|
|
60
|
+
tool_call_id: toolCallId,
|
|
61
|
+
content: content || '(no output)',
|
|
62
|
+
})
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
41
65
|
const results = toolResults(message.content)
|
|
42
66
|
const text = flattenText(message.content)
|
|
43
67
|
if (text || !results.length) out.push({ role: 'user', content: text })
|
|
44
68
|
for (const result of results) {
|
|
45
69
|
out.push({
|
|
46
70
|
role: 'tool',
|
|
47
|
-
tool_call_id: result.toolCallId,
|
|
48
|
-
content: flattenText(result.content) || '(no output)',
|
|
71
|
+
tool_call_id: String(result.toolCallId || result.tool_call_id || result.call_id || result.id || ''),
|
|
72
|
+
content: typeof result.content === 'string' ? result.content : (flattenText(result.content) || '(no output)'),
|
|
49
73
|
})
|
|
50
74
|
}
|
|
51
75
|
}
|
|
@@ -65,16 +89,74 @@ export function openaiTools(options) {
|
|
|
65
89
|
}))
|
|
66
90
|
}
|
|
67
91
|
|
|
92
|
+
export function reconcileResponsesInput(items) {
|
|
93
|
+
const result = []
|
|
94
|
+
const callsSeen = new Set()
|
|
95
|
+
const outputsSeen = new Set()
|
|
96
|
+
|
|
97
|
+
for (const it of items) {
|
|
98
|
+
if (it && it.type === 'function_call_output' && it.call_id) {
|
|
99
|
+
outputsSeen.add(it.call_id)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const pendingCalls = []
|
|
104
|
+
for (const it of items) {
|
|
105
|
+
if (!it) continue
|
|
106
|
+
if (it.type === 'function_call') {
|
|
107
|
+
if (it.call_id) {
|
|
108
|
+
callsSeen.add(it.call_id)
|
|
109
|
+
if (!outputsSeen.has(it.call_id)) {
|
|
110
|
+
pendingCalls.push(it.call_id)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
result.push(it)
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
if (it.type === 'function_call_output') {
|
|
117
|
+
// Drop orphan output if no matching preceding call
|
|
118
|
+
if (it.call_id && callsSeen.has(it.call_id)) {
|
|
119
|
+
result.push(it)
|
|
120
|
+
}
|
|
121
|
+
continue
|
|
122
|
+
}
|
|
123
|
+
// Flush synthetic outputs for any calls that did not get output before user/assistant turn
|
|
124
|
+
while (pendingCalls.length) {
|
|
125
|
+
const orphanId = pendingCalls.shift()
|
|
126
|
+
result.push({
|
|
127
|
+
type: 'function_call_output',
|
|
128
|
+
call_id: orphanId,
|
|
129
|
+
output: '{"status":"interrupted"}',
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
result.push(it)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
while (pendingCalls.length) {
|
|
136
|
+
const orphanId = pendingCalls.shift()
|
|
137
|
+
result.push({
|
|
138
|
+
type: 'function_call_output',
|
|
139
|
+
call_id: orphanId,
|
|
140
|
+
output: '{"status":"interrupted"}',
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!result.length) {
|
|
145
|
+
result.push({ role: 'user', content: [{ type: 'input_text', text: '' }] })
|
|
146
|
+
}
|
|
147
|
+
return result
|
|
148
|
+
}
|
|
149
|
+
|
|
68
150
|
export function codexResponsesBody(options, fallbackInstructions, vendorCfg) {
|
|
69
151
|
const systemParts = []
|
|
70
152
|
if (options.system) systemParts.push(options.system)
|
|
71
|
-
const
|
|
153
|
+
const rawInput = []
|
|
72
154
|
let pendingRole = null
|
|
73
155
|
let pendingContent = []
|
|
74
156
|
function flush() {
|
|
75
157
|
if (!pendingRole) return
|
|
76
158
|
const type = pendingRole === 'assistant' ? 'output_text' : 'input_text'
|
|
77
|
-
|
|
159
|
+
rawInput.push({
|
|
78
160
|
role: pendingRole,
|
|
79
161
|
content: pendingContent.length ? pendingContent : [{ type, text: '' }],
|
|
80
162
|
})
|
|
@@ -95,30 +177,61 @@ export function codexResponsesBody(options, fallbackInstructions, vendorCfg) {
|
|
|
95
177
|
if (message.role === 'assistant') {
|
|
96
178
|
const text = flattenText(message.content)
|
|
97
179
|
if (text) addText('assistant', text)
|
|
98
|
-
|
|
180
|
+
const calls = [
|
|
181
|
+
...toolCalls(message.content),
|
|
182
|
+
...((message.tool_calls || message.toolCalls || []).map((c) => ({
|
|
183
|
+
id: c.id || c.call_id,
|
|
184
|
+
name: c.name || (c.function && c.function.name),
|
|
185
|
+
arguments: typeof c.arguments === 'string' ? c.arguments : (c.function && c.function.arguments) || c.arguments || '{}',
|
|
186
|
+
}))),
|
|
187
|
+
]
|
|
188
|
+
for (const call of calls) {
|
|
189
|
+
if (!call.id) continue
|
|
99
190
|
flush()
|
|
100
|
-
|
|
191
|
+
rawInput.push({
|
|
101
192
|
type: 'function_call',
|
|
102
|
-
call_id: String(call.id
|
|
193
|
+
call_id: String(call.id),
|
|
103
194
|
name: call.name,
|
|
104
195
|
arguments: typeof call.arguments === 'string' ? call.arguments : JSON.stringify(call.arguments || {}),
|
|
105
196
|
})
|
|
106
197
|
}
|
|
107
198
|
continue
|
|
108
199
|
}
|
|
200
|
+
if (message.role === 'tool') {
|
|
201
|
+
flush()
|
|
202
|
+
const callId = String(message.toolCallId || message.tool_call_id || message.call_id || message.id || '')
|
|
203
|
+
const outText = typeof message.content === 'string'
|
|
204
|
+
? message.content
|
|
205
|
+
: (flattenText(message.content) || JSON.stringify(message.content || ''))
|
|
206
|
+
if (callId) {
|
|
207
|
+
rawInput.push({
|
|
208
|
+
type: 'function_call_output',
|
|
209
|
+
call_id: callId,
|
|
210
|
+
output: outText,
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
continue
|
|
214
|
+
}
|
|
215
|
+
const results = toolResults(message.content)
|
|
109
216
|
const text = flattenText(message.content)
|
|
110
217
|
if (text) addText('user', text)
|
|
111
|
-
for (const result of
|
|
218
|
+
for (const result of results) {
|
|
112
219
|
flush()
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
220
|
+
const callId = String(result.toolCallId || result.tool_call_id || result.call_id || result.id || '')
|
|
221
|
+
const outText = typeof result.content === 'string'
|
|
222
|
+
? result.content
|
|
223
|
+
: (flattenText(result.content) || (result.output != null ? (typeof result.output === 'string' ? result.output : JSON.stringify(result.output)) : ''))
|
|
224
|
+
if (callId) {
|
|
225
|
+
rawInput.push({
|
|
226
|
+
type: 'function_call_output',
|
|
227
|
+
call_id: callId,
|
|
228
|
+
output: outText,
|
|
229
|
+
})
|
|
230
|
+
}
|
|
118
231
|
}
|
|
119
232
|
}
|
|
120
233
|
flush()
|
|
121
|
-
|
|
234
|
+
const input = reconcileResponsesInput(rawInput)
|
|
122
235
|
const tools = openaiTools(options)
|
|
123
236
|
const responsesTools = tools && tools.map((tool) => ({
|
|
124
237
|
type: 'function',
|
|
@@ -160,7 +273,16 @@ export function anthropicPayload(options, extraSystem) {
|
|
|
160
273
|
const blocks = []
|
|
161
274
|
const text = flattenText(message.content)
|
|
162
275
|
if (text) blocks.push({ type: 'text', text })
|
|
163
|
-
|
|
276
|
+
const calls = [
|
|
277
|
+
...toolCalls(message.content),
|
|
278
|
+
...((message.tool_calls || message.toolCalls || []).map((c) => ({
|
|
279
|
+
id: c.id || c.call_id,
|
|
280
|
+
name: c.name || (c.function && c.function.name),
|
|
281
|
+
arguments: c.arguments || (c.function && c.function.arguments) || '{}',
|
|
282
|
+
}))),
|
|
283
|
+
]
|
|
284
|
+
for (const call of calls) {
|
|
285
|
+
if (!call.id) continue
|
|
164
286
|
blocks.push({
|
|
165
287
|
type: 'tool_use',
|
|
166
288
|
id: call.id,
|
|
@@ -171,14 +293,33 @@ export function anthropicPayload(options, extraSystem) {
|
|
|
171
293
|
messages.push({ role: 'assistant', content: blocks.length ? blocks : [{ type: 'text', text: '' }] })
|
|
172
294
|
continue
|
|
173
295
|
}
|
|
296
|
+
if (message.role === 'tool') {
|
|
297
|
+
const toolUseId = String(message.toolCallId || message.tool_call_id || message.tool_use_id || message.id || '')
|
|
298
|
+
const content = typeof message.content === 'string'
|
|
299
|
+
? message.content
|
|
300
|
+
: (flattenText(message.content) || JSON.stringify(message.content || ''))
|
|
301
|
+
messages.push({
|
|
302
|
+
role: 'user',
|
|
303
|
+
content: [{
|
|
304
|
+
type: 'tool_result',
|
|
305
|
+
tool_use_id: toolUseId,
|
|
306
|
+
content: content || '',
|
|
307
|
+
}],
|
|
308
|
+
})
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
174
311
|
const blocks = []
|
|
175
312
|
const text = flattenText(message.content)
|
|
176
313
|
if (text) blocks.push({ type: 'text', text })
|
|
177
314
|
for (const result of toolResults(message.content)) {
|
|
315
|
+
const toolUseId = String(result.toolCallId || result.tool_call_id || result.tool_use_id || result.id || '')
|
|
316
|
+
const content = typeof result.content === 'string'
|
|
317
|
+
? result.content
|
|
318
|
+
: (flattenText(result.content) || (result.output != null ? (typeof result.output === 'string' ? result.output : JSON.stringify(result.output)) : ''))
|
|
178
319
|
blocks.push({
|
|
179
320
|
type: 'tool_result',
|
|
180
|
-
tool_use_id:
|
|
181
|
-
content:
|
|
321
|
+
tool_use_id: toolUseId,
|
|
322
|
+
content: content || '',
|
|
182
323
|
})
|
|
183
324
|
}
|
|
184
325
|
messages.push({ role: 'user', content: blocks.length ? blocks : [{ type: 'text', text: '' }] })
|
|
@@ -210,18 +351,45 @@ export function googleContents(options) {
|
|
|
210
351
|
systemParts.push({ text: flattenText(message.content) })
|
|
211
352
|
continue
|
|
212
353
|
}
|
|
354
|
+
if (message.role === 'tool') {
|
|
355
|
+
const name = message.toolName || message.name || 'tool'
|
|
356
|
+
const output = typeof message.content === 'string'
|
|
357
|
+
? message.content
|
|
358
|
+
: (flattenText(message.content) || JSON.stringify(message.content || ''))
|
|
359
|
+
contents.push({
|
|
360
|
+
role: 'user',
|
|
361
|
+
parts: [{
|
|
362
|
+
functionResponse: {
|
|
363
|
+
name,
|
|
364
|
+
response: { output },
|
|
365
|
+
},
|
|
366
|
+
}],
|
|
367
|
+
})
|
|
368
|
+
continue
|
|
369
|
+
}
|
|
213
370
|
const role = message.role === 'assistant' ? 'model' : 'user'
|
|
214
371
|
const parts = []
|
|
215
372
|
const text = flattenText(message.content)
|
|
216
373
|
if (text) parts.push({ text })
|
|
217
|
-
|
|
374
|
+
const calls = [
|
|
375
|
+
...toolCalls(message.content),
|
|
376
|
+
...((message.tool_calls || message.toolCalls || []).map((c) => ({
|
|
377
|
+
name: c.name || (c.function && c.function.name),
|
|
378
|
+
arguments: c.arguments || (c.function && c.function.arguments) || '{}',
|
|
379
|
+
}))),
|
|
380
|
+
]
|
|
381
|
+
for (const call of calls) {
|
|
218
382
|
parts.push({ functionCall: { name: call.name, args: safeJson(call.arguments) } })
|
|
219
383
|
}
|
|
220
384
|
for (const result of toolResults(message.content)) {
|
|
385
|
+
const name = result.toolName || result.name || 'tool'
|
|
386
|
+
const output = typeof result.content === 'string'
|
|
387
|
+
? result.content
|
|
388
|
+
: (flattenText(result.content) || (result.output != null ? (typeof result.output === 'string' ? result.output : JSON.stringify(result.output)) : ''))
|
|
221
389
|
parts.push({
|
|
222
390
|
functionResponse: {
|
|
223
|
-
name
|
|
224
|
-
response: { output
|
|
391
|
+
name,
|
|
392
|
+
response: { output },
|
|
225
393
|
},
|
|
226
394
|
})
|
|
227
395
|
}
|
|
@@ -242,24 +410,32 @@ export function googleContents(options) {
|
|
|
242
410
|
}
|
|
243
411
|
|
|
244
412
|
function safeJson(text) {
|
|
245
|
-
if (
|
|
246
|
-
try { return JSON.parse(text
|
|
413
|
+
if (typeof text === 'object' && text !== null) return text
|
|
414
|
+
try { return JSON.parse(text) } catch { return {} }
|
|
247
415
|
}
|
|
248
416
|
|
|
249
|
-
export function modelCatalog(provider,
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
id,
|
|
258
|
-
name: entry.name || entry.display_name || id,
|
|
259
|
-
...(entry.description ? { description: entry.description } : {}),
|
|
260
|
-
...(entry.contextWindow ? { contextWindow: entry.contextWindow } : {}),
|
|
261
|
-
...(entry.inputModalities ? { inputModalities: entry.inputModalities } : {}),
|
|
262
|
-
...(entry.reasoning ? { reasoning: entry.reasoning } : {}),
|
|
417
|
+
export function modelCatalog(provider, list) {
|
|
418
|
+
if (!Array.isArray(list)) return []
|
|
419
|
+
const out = []
|
|
420
|
+
for (const entry of list) {
|
|
421
|
+
if (!entry) continue
|
|
422
|
+
if (typeof entry === 'string') {
|
|
423
|
+
out.push({ provider, id: entry, name: entry })
|
|
424
|
+
continue
|
|
263
425
|
}
|
|
264
|
-
|
|
265
|
-
|
|
426
|
+
if (typeof entry === 'object') {
|
|
427
|
+
const id = entry.id || entry.slug || entry.name
|
|
428
|
+
if (!id) continue
|
|
429
|
+
out.push({
|
|
430
|
+
provider,
|
|
431
|
+
id,
|
|
432
|
+
name: entry.name || entry.display_name || id,
|
|
433
|
+
...(entry.description ? { description: entry.description } : {}),
|
|
434
|
+
...(entry.contextWindow || entry.context_window ? { contextWindow: entry.contextWindow || entry.context_window } : {}),
|
|
435
|
+
...(entry.reasoning != null ? { reasoning: entry.reasoning } : {}),
|
|
436
|
+
...(entry.inputModalities || entry.input_modalities ? { inputModalities: entry.inputModalities || entry.input_modalities } : {}),
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return out
|
|
441
|
+
}
|
package/lib/vendors/codex.js
CHANGED
|
@@ -30,6 +30,9 @@ export function defaults() {
|
|
|
30
30
|
baseUrl: 'https://chatgpt.com/backend-api/codex',
|
|
31
31
|
originator: 'codex_cli_rs',
|
|
32
32
|
models: [
|
|
33
|
+
{ id: 'gpt-5.6-luna', name: 'GPT-5.6 Luna' },
|
|
34
|
+
{ id: 'gpt-5.3-codex', name: 'GPT-5.3 Codex' },
|
|
35
|
+
{ id: 'gpt-5.2-codex', name: 'GPT-5.2 Codex' },
|
|
33
36
|
{ id: 'gpt-5.1-codex', name: 'GPT-5.1 Codex' },
|
|
34
37
|
{ id: 'gpt-5.1-codex-mini', name: 'GPT-5.1 Codex Mini' },
|
|
35
38
|
{ id: 'gpt-5.1', name: 'GPT-5.1' },
|
|
@@ -141,7 +144,8 @@ export async function refresh(cfg, blob, fetchImpl) {
|
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
export async function listModels(blob, cfg, fetchImpl) {
|
|
144
|
-
const
|
|
147
|
+
const rawModels = (cfg && cfg.models && cfg.models.length) ? cfg.models : defaults().models
|
|
148
|
+
const catalog = modelCatalog(id, rawModels)
|
|
145
149
|
const impl = fetchImpl || fetch
|
|
146
150
|
try {
|
|
147
151
|
const base = (cfg.baseUrl || defaults().baseUrl).replace(/\/$/, '')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.21",
|
|
4
4
|
"description": "Use ChatGPT Codex, Claude, Grok, Antigravity, Kimi, GLM, Cursor, Kiro, Copilot, Qwen, ERNIE, Spark, JetBrains, Perplexity, Replit, and Cody subscriptions as DeepSeek Harness LLM providers via OAuth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|