@goodandready/dsh-subscriptions 0.5.4 → 0.5.6
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/import-auth.js +60 -0
- package/lib/refs.js +4 -0
- package/lib/vendors/cursor.js +133 -0
- package/lib/vendors/index.js +3 -1
- package/lib/vendors/kiro.js +116 -0
- package/package.json +1 -1
package/lib/import-auth.js
CHANGED
|
@@ -97,6 +97,44 @@ export async function discoverLocalCliSessions() {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
|
|
101
|
+
// 6. Cursor Token (env or CLI)
|
|
102
|
+
if (process.env.CURSOR_ACCESS_TOKEN) {
|
|
103
|
+
detected.cursor = {
|
|
104
|
+
provider: 'cursor',
|
|
105
|
+
path: 'CURSOR_ACCESS_TOKEN env',
|
|
106
|
+
email: 'Cursor IDE User',
|
|
107
|
+
hasRefreshToken: false,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
// 7. AWS Kiro
|
|
113
|
+
const kiroPaths = [
|
|
114
|
+
homeFile('.kiro', 'credentials.json'),
|
|
115
|
+
homeFile('.aws', 'sso', 'cache', 'kiro-auth-token.json'),
|
|
116
|
+
]
|
|
117
|
+
for (const p of kiroPaths) {
|
|
118
|
+
const k = await readJson(p)
|
|
119
|
+
if (k && (k.accessToken || k.access_token || k.token)) {
|
|
120
|
+
detected.kiro = {
|
|
121
|
+
provider: 'kiro',
|
|
122
|
+
path: p,
|
|
123
|
+
email: k.email || k.account || 'AWS Kiro User',
|
|
124
|
+
hasRefreshToken: !!(k.refreshToken || k.refresh_token),
|
|
125
|
+
}
|
|
126
|
+
break
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (!detected.kiro && process.env.KIRO_API_KEY) {
|
|
130
|
+
detected.kiro = {
|
|
131
|
+
provider: 'kiro',
|
|
132
|
+
path: 'KIRO_API_KEY env',
|
|
133
|
+
email: 'AWS Kiro API Key',
|
|
134
|
+
hasRefreshToken: false,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
100
138
|
return detected
|
|
101
139
|
}
|
|
102
140
|
|
|
@@ -159,5 +197,27 @@ export async function loadLocalCliBlob(provider) {
|
|
|
159
197
|
}
|
|
160
198
|
}
|
|
161
199
|
|
|
200
|
+
|
|
201
|
+
if (provider === 'cursor') {
|
|
202
|
+
const token = process.env.CURSOR_ACCESS_TOKEN || (raw && (raw.accessToken || raw.access_token)) || ''
|
|
203
|
+
return {
|
|
204
|
+
accessToken: token,
|
|
205
|
+
refreshToken: '',
|
|
206
|
+
expiresAt: Date.now() + 30 * 86400 * 1000,
|
|
207
|
+
email: 'Cursor User',
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
if (provider === 'kiro') {
|
|
213
|
+
const token = process.env.KIRO_API_KEY || (raw && (raw.accessToken || raw.access_token || raw.token)) || ''
|
|
214
|
+
return {
|
|
215
|
+
accessToken: token,
|
|
216
|
+
refreshToken: (raw && (raw.refreshToken || raw.refresh_token)) || '',
|
|
217
|
+
expiresAt: (raw && raw.expiresAt) || (Date.now() + 30 * 86400 * 1000),
|
|
218
|
+
email: (raw && raw.email) || 'AWS Kiro User',
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
162
222
|
throw new Error(`unsupported local CLI import for provider ${provider}`)
|
|
163
223
|
}
|
package/lib/refs.js
CHANGED
|
@@ -6,6 +6,8 @@ export const BUILTIN_PROVIDERS = Object.freeze([
|
|
|
6
6
|
'antigravity',
|
|
7
7
|
'kimi',
|
|
8
8
|
'glm',
|
|
9
|
+
'cursor',
|
|
10
|
+
'kiro',
|
|
9
11
|
])
|
|
10
12
|
|
|
11
13
|
const dynamicIds = new Set()
|
|
@@ -43,6 +45,8 @@ const DISPLAY = {
|
|
|
43
45
|
antigravity: 'Antigravity',
|
|
44
46
|
kimi: 'Moonshot Kimi',
|
|
45
47
|
glm: 'Zhipu GLM',
|
|
48
|
+
cursor: 'Cursor',
|
|
49
|
+
kiro: 'AWS Kiro',
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
export function displayName(provider) {
|
|
@@ -0,0 +1,133 @@
|
|
|
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 = 'cursor'
|
|
7
|
+
|
|
8
|
+
export const CURSOR_API_BASE = 'https://api2.cursor.sh'
|
|
9
|
+
export const CURSOR_USAGE_URL = 'https://api2.cursor.sh/aiserver.v1.DashboardService/GetCurrentPeriodUsage'
|
|
10
|
+
export const CURSOR_AGENT_URL = 'https://agentn.us.api5.cursor.sh/agent.v1.AgentService/Run'
|
|
11
|
+
export const CURSOR_CLIENT_VERSION = 'cli-2026.05.01-eea359f'
|
|
12
|
+
|
|
13
|
+
export const CURSOR_MODELS = [
|
|
14
|
+
{
|
|
15
|
+
id: 'composer-2',
|
|
16
|
+
name: 'Composer 2',
|
|
17
|
+
contextWindow: 200000,
|
|
18
|
+
maxTokens: 64000,
|
|
19
|
+
inputModalities: ['text', 'image'],
|
|
20
|
+
reasoning: { efforts: [{ id: 'low', name: 'Low' }, { id: 'medium', name: 'Medium' }, { id: 'high', name: 'High' }, { id: 'max', name: 'Max' }] },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'composer-1.5',
|
|
24
|
+
name: 'Composer 1.5',
|
|
25
|
+
contextWindow: 200000,
|
|
26
|
+
maxTokens: 64000,
|
|
27
|
+
inputModalities: ['text', 'image'],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'claude-sonnet-5',
|
|
31
|
+
name: 'Claude Sonnet 5 (Cursor)',
|
|
32
|
+
contextWindow: 200000,
|
|
33
|
+
maxTokens: 64000,
|
|
34
|
+
inputModalities: ['text', 'image'],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'gpt-5.5',
|
|
38
|
+
name: 'GPT-5.5 (Cursor)',
|
|
39
|
+
contextWindow: 200000,
|
|
40
|
+
maxTokens: 128000,
|
|
41
|
+
inputModalities: ['text'],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'grok-4.5',
|
|
45
|
+
name: 'Grok 4.5 (Cursor)',
|
|
46
|
+
contextWindow: 200000,
|
|
47
|
+
maxTokens: 64000,
|
|
48
|
+
inputModalities: ['text'],
|
|
49
|
+
},
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
export function providerInfo() {
|
|
53
|
+
return { id, name: 'Cursor' }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function defaults() {
|
|
57
|
+
return {
|
|
58
|
+
apiBase: CURSOR_API_BASE,
|
|
59
|
+
models: CURSOR_MODELS.map((m) => m.id),
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function authorizeUrl() {
|
|
64
|
+
return 'https://cursor.com/loginDeepControl'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function listModels() {
|
|
68
|
+
return CURSOR_MODELS
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function usage(blob, config, fetchImpl) {
|
|
72
|
+
try {
|
|
73
|
+
const impl = fetchImpl || fetch
|
|
74
|
+
const token = blob && (blob.accessToken || blob.access_token)
|
|
75
|
+
if (!token) return null
|
|
76
|
+
const res = await impl(CURSOR_USAGE_URL, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: {
|
|
79
|
+
Authorization: `Bearer ${token}`,
|
|
80
|
+
'x-cursor-client-version': CURSOR_CLIENT_VERSION,
|
|
81
|
+
'User-Agent': CURSOR_CLIENT_VERSION,
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
},
|
|
84
|
+
body: '{}',
|
|
85
|
+
})
|
|
86
|
+
if (!res.ok) return null
|
|
87
|
+
const json = await readJson(res)
|
|
88
|
+
// Cursor returns { numRequests, maxRequestUsage, plan }
|
|
89
|
+
if (json && json.numRequests != null && json.maxRequestUsage != null && json.maxRequestUsage > 0) {
|
|
90
|
+
const pct = Math.round((json.numRequests / json.maxRequestUsage) * 100)
|
|
91
|
+
const snap = asUsageSnapshot(pct)
|
|
92
|
+
if (snap) snap.plan = json.plan || 'Pro'
|
|
93
|
+
return snap
|
|
94
|
+
}
|
|
95
|
+
return null
|
|
96
|
+
} catch {
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
|
|
102
|
+
const impl = fetchImpl || fetch
|
|
103
|
+
const token = blob && (blob.accessToken || blob.access_token)
|
|
104
|
+
if (!token) throw new LlmError('Cursor not authenticated', 'AUTH')
|
|
105
|
+
|
|
106
|
+
const body = {
|
|
107
|
+
model: options.model || 'composer-2',
|
|
108
|
+
messages: openaiMessages(options),
|
|
109
|
+
stream: true,
|
|
110
|
+
...(options.maxTokens != null ? { max_tokens: options.maxTokens } : {}),
|
|
111
|
+
...(options.temperature != null ? { temperature: options.temperature } : {}),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const tools = openaiTools(options)
|
|
115
|
+
if (tools && tools.length) body.tools = tools
|
|
116
|
+
|
|
117
|
+
const url = (config && config.agentUrl) || CURSOR_AGENT_URL
|
|
118
|
+
const res = await impl(url, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: {
|
|
121
|
+
...headers,
|
|
122
|
+
Authorization: `Bearer ${token}`,
|
|
123
|
+
'x-cursor-client-version': CURSOR_CLIENT_VERSION,
|
|
124
|
+
'User-Agent': CURSOR_CLIENT_VERSION,
|
|
125
|
+
'Content-Type': 'application/json',
|
|
126
|
+
},
|
|
127
|
+
body: JSON.stringify(body),
|
|
128
|
+
signal,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
if (!res.ok) throw httpError(res.status, await res.text())
|
|
132
|
+
yield* openaiChatStream(res.body)
|
|
133
|
+
}
|
package/lib/vendors/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as kiro from './kiro.js'
|
|
2
|
+
import * as cursor from './cursor.js'
|
|
1
3
|
import * as codex from './codex.js'
|
|
2
4
|
import * as claude from './claude.js'
|
|
3
5
|
import * as grok from './grok.js'
|
|
@@ -6,7 +8,7 @@ import * as kimi from './kimi.js'
|
|
|
6
8
|
import * as glm from './glm.js'
|
|
7
9
|
import { createVendorFromProfile } from '../vendor-factory.js'
|
|
8
10
|
|
|
9
|
-
const builtins = { codex, claude, grok, antigravity, kimi, glm }
|
|
11
|
+
const builtins = { codex, claude, grok, antigravity, kimi, glm, cursor, kiro }
|
|
10
12
|
|
|
11
13
|
const customVendors = new Map()
|
|
12
14
|
|
|
@@ -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
|
+
}
|
package/package.json
CHANGED