@trydecember/cli 0.2.4 → 0.2.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/dist/december.js +40237 -26391
- package/package.json +1 -1
- package/src/{auth.ts → auth/browser.ts} +0 -71
- package/src/auth/device.ts +70 -0
- package/src/auth/index.ts +2 -0
- package/src/index.ts +3 -0
- package/src/local-operations.ts +9 -1
package/package.json
CHANGED
|
@@ -132,74 +132,3 @@ export async function loginViaBrowser(
|
|
|
132
132
|
)
|
|
133
133
|
})
|
|
134
134
|
}
|
|
135
|
-
|
|
136
|
-
export async function loginViaDeviceCode(
|
|
137
|
-
baseUrl: string = 'http://localhost:4000',
|
|
138
|
-
onCodeGenerated: (userCode: string, verificationUri: string) => void
|
|
139
|
-
): Promise<{ token: string; email: string | null }> {
|
|
140
|
-
return new Promise(async (resolve, reject) => {
|
|
141
|
-
try {
|
|
142
|
-
// 1. Generate Code
|
|
143
|
-
const genRes = await fetch(`${baseUrl}/api/v1/auth/device/code`, {
|
|
144
|
-
method: 'POST',
|
|
145
|
-
headers: { 'Content-Type': 'application/json' },
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
if (!genRes.ok) {
|
|
149
|
-
throw new Error('Failed to generate device code')
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const genData = await genRes.json()
|
|
153
|
-
if (!genData.success) {
|
|
154
|
-
throw new Error(genData.message || 'Failed to generate device code')
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const { deviceCode, userCode, verificationUri, expiresIn, interval } = genData.data
|
|
158
|
-
|
|
159
|
-
onCodeGenerated(userCode, verificationUri)
|
|
160
|
-
|
|
161
|
-
// 2. Poll for token
|
|
162
|
-
const startTime = Date.now()
|
|
163
|
-
const pollInterval = interval * 1000 || 5000
|
|
164
|
-
|
|
165
|
-
const poll = async () => {
|
|
166
|
-
if (Date.now() - startTime > expiresIn * 1000) {
|
|
167
|
-
reject(new Error('Device code expired'))
|
|
168
|
-
return
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
const tokenRes = await fetch(`${baseUrl}/api/v1/auth/device/token`, {
|
|
173
|
-
method: 'POST',
|
|
174
|
-
headers: { 'Content-Type': 'application/json' },
|
|
175
|
-
body: JSON.stringify({ deviceCode }),
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
const tokenData = await tokenRes.json()
|
|
179
|
-
|
|
180
|
-
if (tokenRes.ok && tokenData.success) {
|
|
181
|
-
resolve({
|
|
182
|
-
token: tokenData.data.token,
|
|
183
|
-
email: tokenData.data.email,
|
|
184
|
-
})
|
|
185
|
-
return
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// If not ok, check if it's authorization_pending
|
|
189
|
-
if (tokenData.message === 'authorization_pending') {
|
|
190
|
-
setTimeout(poll, pollInterval)
|
|
191
|
-
} else {
|
|
192
|
-
reject(new Error(tokenData.message || 'Polling failed'))
|
|
193
|
-
}
|
|
194
|
-
} catch (err) {
|
|
195
|
-
// Network errors during polling, just keep trying
|
|
196
|
-
setTimeout(poll, pollInterval)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
setTimeout(poll, pollInterval)
|
|
201
|
-
} catch (err) {
|
|
202
|
-
reject(err)
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export async function loginViaDeviceCode(
|
|
2
|
+
baseUrl: string = 'http://localhost:4000',
|
|
3
|
+
onCodeGenerated: (userCode: string, verificationUri: string) => void
|
|
4
|
+
): Promise<{ token: string; email: string | null }> {
|
|
5
|
+
return new Promise(async (resolve, reject) => {
|
|
6
|
+
try {
|
|
7
|
+
// 1. Generate Code
|
|
8
|
+
const genRes = await fetch(`${baseUrl}/api/v1/auth/device/code`, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: { 'Content-Type': 'application/json' },
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
if (!genRes.ok) {
|
|
14
|
+
throw new Error('Failed to generate device code')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const genData = await genRes.json()
|
|
18
|
+
if (!genData.success) {
|
|
19
|
+
throw new Error(genData.message || 'Failed to generate device code')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { deviceCode, userCode, verificationUri, expiresIn, interval } = genData.data
|
|
23
|
+
|
|
24
|
+
onCodeGenerated(userCode, verificationUri)
|
|
25
|
+
|
|
26
|
+
// 2. Poll for token
|
|
27
|
+
const startTime = Date.now()
|
|
28
|
+
const pollInterval = interval * 1000 || 5000
|
|
29
|
+
|
|
30
|
+
const poll = async () => {
|
|
31
|
+
if (Date.now() - startTime > expiresIn * 1000) {
|
|
32
|
+
reject(new Error('Device code expired'))
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const tokenRes = await fetch(`${baseUrl}/api/v1/auth/device/token`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'Content-Type': 'application/json' },
|
|
40
|
+
body: JSON.stringify({ deviceCode }),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const tokenData = await tokenRes.json()
|
|
44
|
+
|
|
45
|
+
if (tokenRes.ok && tokenData.success) {
|
|
46
|
+
resolve({
|
|
47
|
+
token: tokenData.data.token,
|
|
48
|
+
email: tokenData.data.email,
|
|
49
|
+
})
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// If not ok, check if it's authorization_pending
|
|
54
|
+
if (tokenData.message === 'authorization_pending') {
|
|
55
|
+
setTimeout(poll, pollInterval)
|
|
56
|
+
} else {
|
|
57
|
+
reject(new Error(tokenData.message || 'Polling failed'))
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
// Network errors during polling, just keep trying
|
|
61
|
+
setTimeout(poll, pollInterval)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setTimeout(poll, pollInterval)
|
|
66
|
+
} catch (err) {
|
|
67
|
+
reject(err)
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,9 @@ import { localOperations } from './local-operations'
|
|
|
29
29
|
const originalConsoleError = console.error
|
|
30
30
|
|
|
31
31
|
async function main() {
|
|
32
|
+
process.title = 'december'
|
|
33
|
+
process.stdout.write('\x1b]0;december\x07')
|
|
34
|
+
|
|
32
35
|
// Suppress noisy SDK console logs that corrupt the Ink TUI layout
|
|
33
36
|
console.warn = () => {}
|
|
34
37
|
console.error = () => {}
|
package/src/local-operations.ts
CHANGED
|
@@ -3,7 +3,7 @@ import fs from 'node:fs/promises'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { promisify } from 'node:util'
|
|
5
5
|
|
|
6
|
-
import { AgentOperations } from '@december/agent'
|
|
6
|
+
import { AgentOperations, taskManager } from '@december/agent'
|
|
7
7
|
import fg from 'fast-glob'
|
|
8
8
|
|
|
9
9
|
const execAsync = promisify(exec)
|
|
@@ -21,29 +21,37 @@ export const localOperations: AgentOperations = {
|
|
|
21
21
|
stdio: 'pipe',
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
+
const task = taskManager.addTask(command, child)
|
|
25
|
+
|
|
24
26
|
child.stdout?.on('data', (data) => {
|
|
25
27
|
const chunk = data.toString()
|
|
26
28
|
stdout += chunk
|
|
29
|
+
taskManager.appendOutput(task.id, chunk)
|
|
27
30
|
onData(chunk)
|
|
28
31
|
})
|
|
29
32
|
|
|
30
33
|
child.stderr?.on('data', (data) => {
|
|
31
34
|
const chunk = data.toString()
|
|
32
35
|
stderr += chunk
|
|
36
|
+
taskManager.appendOutput(task.id, chunk)
|
|
33
37
|
onData(chunk)
|
|
34
38
|
})
|
|
35
39
|
|
|
36
40
|
let isFinished = false
|
|
37
41
|
|
|
38
42
|
child.on('close', (code) => {
|
|
43
|
+
taskManager.markCompleted(task.id, code)
|
|
39
44
|
if (isFinished) return
|
|
40
45
|
isFinished = true
|
|
46
|
+
taskManager.removeTask(task.id)
|
|
41
47
|
resolve({ exitCode: code, output: stdout + '\n' + stderr })
|
|
42
48
|
})
|
|
43
49
|
|
|
44
50
|
child.on('error', (err) => {
|
|
51
|
+
taskManager.markCompleted(task.id, 1)
|
|
45
52
|
if (isFinished) return
|
|
46
53
|
isFinished = true
|
|
54
|
+
taskManager.removeTask(task.id)
|
|
47
55
|
resolve({ exitCode: 1, output: `Failed to start command: ${err.message}` })
|
|
48
56
|
})
|
|
49
57
|
|