deepfish-ai 1.0.27 → 1.0.28

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.
@@ -269,7 +269,7 @@ async function getGenerateClawSkillRules(goal) {
269
269
  3. 文档文件:目录中需新增2个说明文档:
270
270
  - README_CN.md(中文说明文档)
271
271
  - README.md(英文说明文档)
272
- 4. 辅助文件:如果Skill涉及复杂逻辑,可在目录中创建辅助脚本文件(如 .js、.sh、.py 等),并在 SKILL.md 中说明其用途和调用方式
272
+ 4. 辅助文件:如果Skill涉及复杂逻辑,可在目录中创建辅助脚本文件(如 .js、.sh、.py 等),并在 SKILL.md 中说明其用途和调用方式;如果是js文件,可以在函数中通过this.Tools调用内置函数,如直接使用this.Tools.createSubAgent(workGoal)。
273
273
 
274
274
  #### 标准目录结构
275
275
  \`\`\`
@@ -199,6 +199,14 @@ const descriptions = [
199
199
  type: 'array',
200
200
  description:
201
201
  '选项数组,适用于list/checkbox/rawlist/expand类型',
202
+ items: {
203
+ type: 'object',
204
+ properties: {
205
+ name: { type: 'string' },
206
+ value: { type: 'string' },
207
+ short: { type: 'string' },
208
+ },
209
+ },
202
210
  },
203
211
  validate: {
204
212
  type: 'string',
@@ -1,9 +1,49 @@
1
1
  const { OpenAI } = require('openai')
2
+ const {
3
+ refreshGithubModelsTokenIfNeeded,
4
+ buildDefaultHeaders,
5
+ } = require('./copilot.js')
6
+
7
+ function normalizeAiRequestConfig(aiConfig = {}) {
8
+ const max_tokens = (!aiConfig.maxTokens || aiConfig.maxTokens === -1)
9
+ ? undefined
10
+ : aiConfig.maxTokens * 1024 // 兼容旧配置,按 1KB ~= 1024 tokens 估算
11
+
12
+ const requestConfig = {
13
+ model: aiConfig.model,
14
+ temperature: aiConfig.temperature,
15
+ stream: aiConfig.stream,
16
+ max_tokens,
17
+ }
18
+
19
+ // 仅 DeepSeek 兼容端注入扩展字段,避免其它网关因未知参数报错
20
+ if (aiConfig.type === 'deepseek') {
21
+ requestConfig.extra_body = { reasoning_split: true }
22
+ }
23
+
24
+ return requestConfig
25
+ }
2
26
 
3
27
  function creatClient(aiConfig) {
28
+ if (aiConfig.type === 'github-models') {
29
+ if (!aiConfig.apiKey || !String(aiConfig.apiKey).trim()) {
30
+ throw new Error(
31
+ 'GitHub Models requires apiKey. Please set a GitHub token with models:read permission in your current AI config.',
32
+ )
33
+ }
34
+ }
35
+ if (aiConfig.type === 'copilot') {
36
+ if (!aiConfig.apiKey || !String(aiConfig.apiKey).trim()) {
37
+ throw new Error(
38
+ 'GitHub Copilot requires apiKey. Please set a GitHub Copilot access token in your current AI config.',
39
+ )
40
+ }
41
+ }
42
+
4
43
  return new OpenAI({
5
44
  baseURL: aiConfig.baseUrl,
6
45
  apiKey: aiConfig.apiKey || '',
46
+ defaultHeaders: buildDefaultHeaders(aiConfig),
7
47
  })
8
48
  }
9
49
 
@@ -28,13 +68,16 @@ async function think(
28
68
  streamEnd,
29
69
  ) {
30
70
  try {
71
+ await refreshGithubModelsTokenIfNeeded(aiConfig)
72
+ const requestClient = creatClient(aiConfig)
73
+ const requestConfig = normalizeAiRequestConfig(aiConfig)
31
74
  const opt = {
32
75
  messages: messages,
33
- extra_body: {"reasoning_split": true},
34
- ...aiConfig,
76
+ ...requestConfig,
35
77
  }
36
78
  await thinkBefore()
37
- const response = await openAiClient.chat.completions.create(opt)
79
+ const extraHeaders = aiConfig.type === 'copilot' ? { 'x-initiator': 'user' } : undefined
80
+ const response = await requestClient.chat.completions.create(opt, extraHeaders ? { headers: extraHeaders } : undefined)
38
81
  if (aiConfig.stream) {
39
82
  const messageRes = await _streamToNonStream(
40
83
  response,
@@ -73,14 +116,20 @@ async function thinkByTool(
73
116
  streamEnd,
74
117
  ) {
75
118
  try {
119
+ await refreshGithubModelsTokenIfNeeded(aiConfig)
120
+ const requestClient = creatClient(aiConfig)
121
+ const requestConfig = normalizeAiRequestConfig(aiConfig)
76
122
  await thinkBefore()
77
- const response = await openAiClient.chat.completions.create({
78
- messages: messages,
79
- tools: functionDescriptions,
80
- tool_choice: 'auto',
81
- extra_body: {"reasoning_split": true},
82
- ...aiConfig,
83
- })
123
+ const extraHeaders = aiConfig.type === 'copilot' ? { 'x-initiator': 'agent' } : undefined
124
+ const response = await requestClient.chat.completions.create(
125
+ {
126
+ messages: messages,
127
+ tools: functionDescriptions,
128
+ tool_choice: 'auto',
129
+ ...requestConfig,
130
+ },
131
+ extraHeaders ? { headers: extraHeaders } : undefined,
132
+ )
84
133
  if (aiConfig.stream) {
85
134
  const messageRes = await _streamToNonStream(
86
135
  response,
@@ -0,0 +1,117 @@
1
+ const axios = require('axios')
2
+ const { GlobalVariable } = require('../../../cli/GlobalVariable.js')
3
+
4
+ function isExpiring(isoTime, thresholdSec = 120) {
5
+ if (!isoTime) {
6
+ return false
7
+ }
8
+ const target = new Date(isoTime).getTime()
9
+ if (!Number.isFinite(target)) {
10
+ return false
11
+ }
12
+ return target - Date.now() <= thresholdSec * 1000
13
+ }
14
+
15
+ function secondsToISO(seconds) {
16
+ if (!seconds || Number(seconds) <= 0) {
17
+ return ''
18
+ }
19
+ return new Date(Date.now() + Number(seconds) * 1000).toISOString()
20
+ }
21
+
22
+ async function refreshGithubModelsTokenIfNeeded(aiConfig = {}) {
23
+ if (aiConfig.type !== 'github-models' && aiConfig.type !== 'copilot') {
24
+ return
25
+ }
26
+ const githubAuth = aiConfig.githubAuth || {}
27
+ if (!isExpiring(githubAuth.accessTokenExpiresAt)) {
28
+ return
29
+ }
30
+ if (!githubAuth.refreshToken) {
31
+ throw new Error(
32
+ 'GitHub access token expired and no refresh token is available. Please run "ai auth github-login" again.',
33
+ )
34
+ }
35
+ if (!githubAuth.clientId || !githubAuth.clientSecret) {
36
+ throw new Error(
37
+ 'GitHub token refresh requires clientId and clientSecret. Please run "ai auth github-login" and provide both values.',
38
+ )
39
+ }
40
+ if (isExpiring(githubAuth.refreshTokenExpiresAt, 0)) {
41
+ throw new Error(
42
+ 'GitHub refresh token expired. Please run "ai auth github-login" again.',
43
+ )
44
+ }
45
+
46
+ const form = new URLSearchParams()
47
+ form.set('client_id', githubAuth.clientId)
48
+ form.set('client_secret', githubAuth.clientSecret)
49
+ form.set('grant_type', 'refresh_token')
50
+ form.set('refresh_token', githubAuth.refreshToken)
51
+
52
+ const response = await axios.post(
53
+ 'https://github.com/login/oauth/access_token',
54
+ form.toString(),
55
+ {
56
+ headers: {
57
+ Accept: 'application/json',
58
+ 'Content-Type': 'application/x-www-form-urlencoded',
59
+ },
60
+ timeout: 20000,
61
+ },
62
+ )
63
+
64
+ const tokenData = response.data || {}
65
+ if (!tokenData.access_token) {
66
+ const errMessage = tokenData.error_description || tokenData.error || 'Unknown token refresh error'
67
+ throw new Error(`GitHub token refresh failed: ${errMessage}`)
68
+ }
69
+
70
+ const newAuth = {
71
+ ...githubAuth,
72
+ tokenType: tokenData.token_type || githubAuth.tokenType || 'bearer',
73
+ accessTokenExpiresAt: secondsToISO(tokenData.expires_in),
74
+ refreshToken: tokenData.refresh_token || githubAuth.refreshToken,
75
+ refreshTokenExpiresAt: tokenData.refresh_token_expires_in
76
+ ? secondsToISO(tokenData.refresh_token_expires_in)
77
+ : githubAuth.refreshTokenExpiresAt,
78
+ lastUpdatedAt: new Date().toISOString(),
79
+ }
80
+
81
+ aiConfig.apiKey = tokenData.access_token
82
+ aiConfig.githubAuth = newAuth
83
+
84
+ const configManager = GlobalVariable.configManager
85
+ if (configManager && aiConfig.name && configManager.updateAiConfigByName) {
86
+ configManager.updateAiConfigByName(aiConfig.name, (current) => {
87
+ return {
88
+ ...current,
89
+ apiKey: tokenData.access_token,
90
+ githubAuth: {
91
+ ...(current.githubAuth || {}),
92
+ ...newAuth,
93
+ },
94
+ }
95
+ })
96
+ }
97
+ }
98
+
99
+ function buildDefaultHeaders(aiConfig = {}) {
100
+ if (aiConfig.type === 'github-models') {
101
+ return {
102
+ Accept: 'application/vnd.github+json',
103
+ 'X-GitHub-Api-Version': '2026-03-10',
104
+ }
105
+ }
106
+ if (aiConfig.type === 'copilot') {
107
+ return {
108
+ 'Openai-Intent': 'conversation-edits',
109
+ }
110
+ }
111
+ return undefined
112
+ }
113
+
114
+ module.exports = {
115
+ refreshGithubModelsTokenIfNeeded,
116
+ buildDefaultHeaders,
117
+ }
@@ -130,6 +130,29 @@ class ConfigManager {
130
130
  return this.config.ai.find((item) => item.name === aiName)
131
131
  }
132
132
 
133
+ getAiConfig(aiName) {
134
+ return this._getAiConfig(aiName)
135
+ }
136
+
137
+ updateAiConfigByName(aiName, updater) {
138
+ const existingIndex = this.config.ai.findIndex(
139
+ (item) => item.name === aiName,
140
+ )
141
+ if (existingIndex === -1) {
142
+ aiConsole.logError(`Configuration with name "${aiName}" not found.`)
143
+ return null
144
+ }
145
+
146
+ const current = this.config.ai[existingIndex]
147
+ const next = typeof updater === 'function'
148
+ ? updater(lodash.cloneDeep(current))
149
+ : lodash.merge(lodash.cloneDeep(current), updater)
150
+
151
+ this.config.ai[existingIndex] = next
152
+ this.writeConfig(this.config)
153
+ return next
154
+ }
155
+
133
156
  // 获取Ai列表
134
157
  getAiList() {
135
158
  console.log('AI Configurations')
@@ -188,7 +211,8 @@ class ConfigManager {
188
211
  aiConsole.logInfo(`API Base URL: ${aiConfig.baseUrl}`)
189
212
  aiConsole.logInfo(`Model: ${aiConfig.model}`)
190
213
  if (aiConfig.apiKey) {
191
- aiConsole.logInfo(`API Key: ${aiConfig.apiKey}`)
214
+ const maskPrefix = String(aiConfig.apiKey).slice(0, 6)
215
+ aiConsole.logInfo(`API Key: ${maskPrefix}... (masked)`)
192
216
  }
193
217
  aiConsole.logInfo(`Temperature: ${aiConfig.temperature}`)
194
218
  aiConsole.logInfo(`Max Tokens: ${aiConfig.maxTokens}`)
@@ -15,13 +15,13 @@ const aiCliConfig = {
15
15
  DeepSeek: {
16
16
  baseUrl: 'https://api.deepseek.com',
17
17
  model: {
18
- list: ['deepseek-chat', 'deepseek-reasoner', 'other'],
18
+ list: ['deepseek-chat', 'deepseek-reasoner', 'deepseek-v4-flash', 'deepseek-v4-pro', 'other'],
19
19
  defaultValue: '',
20
20
  },
21
21
  type: 'deepseek',
22
22
  apiKey: '',
23
23
  temperature: 0.7,
24
- maxTokens: 8, // 单位KB
24
+ maxTokens: -1, // 单位KB
25
25
  maxContextLength: 64, // 单位KB
26
26
  stream: true,
27
27
  },
@@ -34,7 +34,7 @@ const aiCliConfig = {
34
34
  type: 'minimax',
35
35
  apiKey: '',
36
36
  temperature: 0.7,
37
- maxTokens: 8, // 单位KB
37
+ maxTokens: -1, // 单位KB
38
38
  maxContextLength: 64, // 单位KB
39
39
  stream: true,
40
40
  },
@@ -47,7 +47,7 @@ const aiCliConfig = {
47
47
  type: 'qwen',
48
48
  apiKey: '',
49
49
  temperature: 0.7,
50
- maxTokens: 8, // 单位KB
50
+ maxTokens: -1, // 单位KB
51
51
  maxContextLength: 64, // 单位KB
52
52
  stream: true,
53
53
  },
@@ -60,7 +60,7 @@ const aiCliConfig = {
60
60
  type: 'ollama',
61
61
  apiKey: 'ollama',
62
62
  temperature: 0.7,
63
- maxTokens: 8, // 单位KB
63
+ maxTokens: -1, // 单位KB
64
64
  maxContextLength: 64, // 单位KB
65
65
  stream: true,
66
66
  },
@@ -73,9 +73,25 @@ const aiCliConfig = {
73
73
  type: 'openai',
74
74
  apiKey: '',
75
75
  temperature: 0.7,
76
- maxTokens: 8, // 单位KB
76
+ maxTokens: -1, // 单位KB
77
77
  maxContextLength: 64, // 单位KB
78
78
  stream: true,
79
+ },
80
+ Copilot: {
81
+ baseUrl: 'https://api.githubcopilot.com',
82
+ model: {
83
+ list: [
84
+ 'gpt-4o',
85
+ 'gpt-4o-mini',
86
+ ],
87
+ defaultValue: 'gpt-4o',
88
+ },
89
+ type: 'copilot',
90
+ apiKey: '',
91
+ temperature: 0.7,
92
+ maxTokens: -1, // 单位KB
93
+ maxContextLength: 400, // 单位KB
94
+ stream: true,
79
95
  }
80
96
  }
81
97
 
@@ -0,0 +1,231 @@
1
+ const axios = require('axios')
2
+ const { program } = require('commander')
3
+ const aiInquirer = require('../AgentRobot/BaseAgentRobot/utils/aiInquirer.js')
4
+ const aiConsole = require('../AgentRobot/BaseAgentRobot/utils/aiConsole.js')
5
+ const ConfigManager = require('./ConfigManager.js')
6
+ const { GlobalVariable } = require('./GlobalVariable.js')
7
+
8
+ function sleep(ms) {
9
+ return new Promise((resolve) => setTimeout(resolve, ms))
10
+ }
11
+
12
+ function secondsToISO(seconds) {
13
+ if (!seconds || Number(seconds) <= 0) {
14
+ return ''
15
+ }
16
+ return new Date(Date.now() + Number(seconds) * 1000).toISOString()
17
+ }
18
+
19
+ function getConfigManager() {
20
+ return GlobalVariable.configManager || new ConfigManager()
21
+ }
22
+
23
+ async function githubDeviceLogin({
24
+ configManager,
25
+ targetName,
26
+ currentAuth = {},
27
+ saveToConfig = true,
28
+ } = {}) {
29
+ const manager = configManager || getConfigManager()
30
+ let aiConfig = null
31
+
32
+ if (saveToConfig) {
33
+ if (!targetName) {
34
+ aiConsole.logError('No target AI configuration. Please pass a config name or set current AI first.')
35
+ return null
36
+ }
37
+ aiConfig = manager.getAiConfig(targetName)
38
+ if (!aiConfig) {
39
+ aiConsole.logError(`Configuration with name "${targetName}" not found.`)
40
+ return null
41
+ }
42
+ if (aiConfig.type !== 'copilot') {
43
+ aiConsole.logError(
44
+ `Configuration "${targetName}" is type "${aiConfig.type}". GitHub login only supports "copilot" type.`,
45
+ )
46
+ return null
47
+ }
48
+ }
49
+
50
+ const authSeed = saveToConfig ? (aiConfig.githubAuth || {}) : (currentAuth || {})
51
+ const questions = [
52
+ {
53
+ type: 'input',
54
+ name: 'clientId',
55
+ message: 'Enter GitHub OAuth App Client ID:',
56
+ default: authSeed.clientId || process.env.GITHUB_OAUTH_CLIENT_ID || '',
57
+ validate: (value) => !!String(value || '').trim() || 'Client ID is required',
58
+ },
59
+ {
60
+ type: 'password',
61
+ name: 'clientSecret',
62
+ message: 'Enter GitHub OAuth App Client Secret (optional, needed for refresh in some OAuth app settings):',
63
+ mask: '*',
64
+ default: authSeed.clientSecret || process.env.GITHUB_OAUTH_CLIENT_SECRET || '',
65
+ },
66
+ {
67
+ type: 'input',
68
+ name: 'scope',
69
+ message: 'Enter OAuth scope:',
70
+ default: authSeed.scope || 'models:read',
71
+ },
72
+ ]
73
+
74
+ const answers = await aiInquirer.askAny(questions)
75
+ const clientId = String(answers.clientId || '').trim()
76
+ const clientSecret = String(answers.clientSecret || '').trim()
77
+ const scope = String(answers.scope || 'models:read').trim()
78
+
79
+ let deviceCodeResponse
80
+ try {
81
+ const form = new URLSearchParams()
82
+ form.set('client_id', clientId)
83
+ form.set('scope', scope)
84
+
85
+ const res = await axios.post(
86
+ 'https://github.com/login/device/code',
87
+ form.toString(),
88
+ {
89
+ headers: {
90
+ Accept: 'application/json',
91
+ 'Content-Type': 'application/x-www-form-urlencoded',
92
+ },
93
+ timeout: 20000,
94
+ },
95
+ )
96
+ deviceCodeResponse = res.data
97
+ } catch (error) {
98
+ const message = error?.response?.data?.error_description || error.message
99
+ aiConsole.logError(`Failed to request GitHub device code: ${message}`)
100
+ return null
101
+ }
102
+
103
+ const deviceCode = deviceCodeResponse.device_code
104
+ const userCode = deviceCodeResponse.user_code
105
+ const verificationUri = deviceCodeResponse.verification_uri || 'https://github.com/login/device'
106
+ const expiresIn = Number(deviceCodeResponse.expires_in || 900)
107
+ let intervalSec = Number(deviceCodeResponse.interval || 5)
108
+
109
+ aiConsole.logSuccess('Please complete GitHub login in browser:')
110
+ aiConsole.logInfo(`URL: ${verificationUri}`)
111
+ aiConsole.logInfo(`Code: ${userCode}`)
112
+
113
+ const deadline = Date.now() + expiresIn * 1000
114
+
115
+ while (Date.now() < deadline) {
116
+ await sleep(intervalSec * 1000)
117
+ try {
118
+ const form = new URLSearchParams()
119
+ form.set('client_id', clientId)
120
+ form.set('device_code', deviceCode)
121
+ form.set('grant_type', 'urn:ietf:params:oauth:grant-type:device_code')
122
+
123
+ const res = await axios.post(
124
+ 'https://github.com/login/oauth/access_token',
125
+ form.toString(),
126
+ {
127
+ headers: {
128
+ Accept: 'application/json',
129
+ 'Content-Type': 'application/x-www-form-urlencoded',
130
+ },
131
+ timeout: 20000,
132
+ },
133
+ )
134
+ const tokenData = res.data || {}
135
+
136
+ if (tokenData.error) {
137
+ if (tokenData.error === 'authorization_pending') {
138
+ continue
139
+ }
140
+ if (tokenData.error === 'slow_down') {
141
+ intervalSec = Number(tokenData.interval || intervalSec + 5)
142
+ continue
143
+ }
144
+ if (tokenData.error === 'expired_token') {
145
+ aiConsole.logError('Device code expired. Please run the login command again.')
146
+ return null
147
+ }
148
+ if (tokenData.error === 'access_denied') {
149
+ aiConsole.logError('GitHub authorization denied by user.')
150
+ return null
151
+ }
152
+ aiConsole.logError(tokenData.error_description || tokenData.error)
153
+ return null
154
+ }
155
+
156
+ if (!tokenData.access_token) {
157
+ aiConsole.logError('GitHub login failed: no access_token returned.')
158
+ return null
159
+ }
160
+
161
+ const githubAuthPatch = {
162
+ provider: 'github-oauth-device',
163
+ clientId,
164
+ clientSecret,
165
+ scope,
166
+ tokenType: tokenData.token_type || 'bearer',
167
+ accessTokenExpiresAt: secondsToISO(tokenData.expires_in),
168
+ refreshToken: tokenData.refresh_token || '',
169
+ refreshTokenExpiresAt: secondsToISO(tokenData.refresh_token_expires_in),
170
+ lastUpdatedAt: new Date().toISOString(),
171
+ }
172
+
173
+ if (saveToConfig) {
174
+ manager.updateAiConfigByName(targetName, (current) => {
175
+ return {
176
+ ...current,
177
+ apiKey: tokenData.access_token,
178
+ githubAuth: {
179
+ ...(current.githubAuth || {}),
180
+ ...githubAuthPatch,
181
+ },
182
+ }
183
+ })
184
+ }
185
+
186
+ aiConsole.logSuccess('GitHub login success.')
187
+ if (githubAuthPatch.accessTokenExpiresAt) {
188
+ aiConsole.logInfo(`Access token expires at: ${githubAuthPatch.accessTokenExpiresAt}`)
189
+ }
190
+ if (!githubAuthPatch.refreshToken) {
191
+ aiConsole.logInfo('No refresh token returned. Re-login may be required after access token expires.')
192
+ }
193
+
194
+ return {
195
+ accessToken: tokenData.access_token,
196
+ githubAuth: githubAuthPatch,
197
+ }
198
+ } catch (error) {
199
+ const message = error?.response?.data?.error_description || error.message
200
+ aiConsole.logError(`Polling GitHub token failed: ${message}`)
201
+ return null
202
+ }
203
+ }
204
+
205
+ aiConsole.logError('GitHub login timeout. Please run the command again.')
206
+ return null
207
+ }
208
+
209
+ const authCommand = program
210
+ .command('auth')
211
+ .description('Authenticate third-party model providers')
212
+
213
+ authCommand
214
+ .command('github-login [name]')
215
+ .description('Login with GitHub account (Device Flow) and store short-lived token into a copilot config')
216
+ .action(async (name) => {
217
+ const configManager = getConfigManager()
218
+ const targetName = name || configManager.getCurrentAi()
219
+ const res = await githubDeviceLogin({
220
+ configManager,
221
+ targetName,
222
+ saveToConfig: true,
223
+ })
224
+ if (res) {
225
+ aiConsole.logSuccess(`Token has been saved to AI config "${targetName}".`)
226
+ }
227
+ })
228
+
229
+ module.exports = {
230
+ githubDeviceLogin,
231
+ }
@@ -11,6 +11,8 @@ const { program } = require('commander')
11
11
  const { aiCliConfig } = require('./DefaultConfig.js')
12
12
  const ConfigManager = require('./ConfigManager.js')
13
13
  const aiInquirer = require('../AgentRobot/BaseAgentRobot/utils/aiInquirer.js')
14
+ const aiConsole = require('../AgentRobot/BaseAgentRobot/utils/aiConsole.js')
15
+ const { githubDeviceLogin } = require('./ai-auth.js')
14
16
 
15
17
  const configManager = new ConfigManager()
16
18
  const configCommand = program
@@ -87,6 +89,7 @@ configCommand
87
89
  type: 'input',
88
90
  name: 'baseUrl',
89
91
  message: 'Enter API base URL:',
92
+ when: (answers) => answers.Type !== 'Copilot',
90
93
  default: (answers) => {
91
94
  return aiCliConfig[answers.Type].baseUrl
92
95
  },
@@ -122,6 +125,7 @@ configCommand
122
125
  type: 'input',
123
126
  name: 'apiKey',
124
127
  message: 'Enter API key:',
128
+ when: (answers) => answers.Type !== 'Copilot',
125
129
  default: (answers) => {
126
130
  return aiCliConfig[answers.Type].apiKey
127
131
  },
@@ -143,7 +147,6 @@ configCommand
143
147
  default: (answers) => {
144
148
  return aiCliConfig[answers.Type].maxTokens
145
149
  },
146
- validate: (value) => value > 0 || 'Max tokens must be greater than 0',
147
150
  },
148
151
  {
149
152
  type: 'number',
@@ -167,14 +170,29 @@ configCommand
167
170
  const aiConfig = {
168
171
  name: answers.name,
169
172
  type: aiCliConfig[answers.Type].type,
170
- baseUrl: answers.baseUrl,
173
+ baseUrl: answers.baseUrl || aiCliConfig[answers.Type].baseUrl,
171
174
  model: answers.model,
172
- apiKey: answers.apiKey,
175
+ apiKey: answers.apiKey || aiCliConfig[answers.Type].apiKey,
173
176
  temperature: answers.temperature,
174
177
  maxTokens: answers.maxTokens,
175
178
  maxContextLength: answers.maxContextLength,
176
179
  stream: answers.stream,
177
180
  }
181
+
182
+ if (answers.Type === 'Copilot') {
183
+ const loginRes = await githubDeviceLogin({
184
+ configManager,
185
+ targetName: answers.name,
186
+ saveToConfig: false,
187
+ })
188
+ if (!loginRes) {
189
+ aiConsole.logError('Copilot login failed. Configuration was not added.')
190
+ return
191
+ }
192
+ aiConfig.apiKey = loginRes.accessToken
193
+ aiConfig.githubAuth = loginRes.githubAuth
194
+ }
195
+
178
196
  return configManager.addAiConfig(aiConfig)
179
197
  })
180
198
 
package/src/cli/index.js CHANGED
@@ -1,10 +1,11 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  const { program } = require('commander')
3
3
  const { DeepFishAI } = require('../index.js')
4
4
  const { GlobalVariable } = require('./GlobalVariable.js')
5
5
  require('./ai-config.js')
6
6
  require('./ai-skill.js')
7
7
  require('./ai-memory.js')
8
+ require('./ai-auth.js')
8
9
  const aiConsole = require('../AgentRobot/BaseAgentRobot/utils/aiConsole.js')
9
10
  program
10
11
  .version('1.0.0')
@@ -23,7 +24,8 @@ async function main() {
23
24
  if (
24
25
  (program.args &&
25
26
  (program.args[0] === 'config' ||
26
- program.args[0] === 'skill')) ||
27
+ program.args[0] === 'skill' ||
28
+ program.args[0] === 'auth')) ||
27
29
  program.args[0] === 'memory'
28
30
  ) {
29
31
  return