traw 0.2.6 → 0.2.7
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/package.json +1 -1
- package/readme.md +1 -1
- package/src/cli/help.ts +18 -9
- package/src/cli/index.ts +109 -12
package/package.json
CHANGED
package/readme.md
CHANGED
package/src/cli/help.ts
CHANGED
|
@@ -6,32 +6,41 @@ traw v${VERSION} - AI browser agent
|
|
|
6
6
|
|
|
7
7
|
Usage:
|
|
8
8
|
traw run "your goal here"
|
|
9
|
-
traw auth
|
|
9
|
+
traw auth <provider>
|
|
10
10
|
traw upd
|
|
11
11
|
|
|
12
12
|
Commands:
|
|
13
|
-
run
|
|
14
|
-
auth
|
|
15
|
-
|
|
13
|
+
run execute browser agent with a goal
|
|
14
|
+
auth qwen register qwen account (chat.qwen.ai)
|
|
15
|
+
auth glm register glm account (z.ai)
|
|
16
|
+
auth list [provider] list tokens (all or by provider)
|
|
17
|
+
auth activate <id> activate token by id
|
|
18
|
+
upd check for updates
|
|
19
|
+
|
|
20
|
+
Models:
|
|
21
|
+
qwen: coder-model (default), vision-model (--fast)
|
|
22
|
+
glm: GLM-4-Plus, GLM-4-Flash, GLM-4-Air, GLM-4-6-API-V1
|
|
16
23
|
|
|
17
24
|
Options:
|
|
18
|
-
--fast use fast model (
|
|
25
|
+
--fast use fast model (vision-model for qwen, GLM-4-Flash for glm)
|
|
19
26
|
--headless run without visible browser (default)
|
|
20
27
|
--headed show browser window
|
|
21
28
|
--video enable video recording
|
|
22
|
-
--vision send screenshots to AI (visual mode)
|
|
23
29
|
--steps=N max steps (default: 20)
|
|
24
30
|
--mo=URL mo server url (default: http://localhost:8804)
|
|
25
31
|
--api=URL custom OpenAI-compatible API url (bypasses mo)
|
|
26
32
|
--api-key=KEY API key for custom endpoint (or use OPENAI_API_KEY env)
|
|
27
|
-
--model=NAME model name (default:
|
|
33
|
+
--model=NAME model name (default: coder-model)
|
|
28
34
|
-v, --version show version
|
|
29
35
|
|
|
30
36
|
Examples:
|
|
31
|
-
traw auth
|
|
37
|
+
traw auth qwen
|
|
38
|
+
traw auth glm
|
|
39
|
+
traw auth list
|
|
40
|
+
traw auth activate abc123
|
|
32
41
|
traw run "find the weather in Moscow"
|
|
42
|
+
traw run --model=GLM-4-Plus "search for news"
|
|
33
43
|
traw run --fast "quick search for bun.js"
|
|
34
44
|
traw run --video "search for documentation"
|
|
35
|
-
traw run --api=https://api.openai.com --model=gpt-4o "search for news"
|
|
36
45
|
`)
|
|
37
46
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -26,11 +26,18 @@ const cliOptions = {
|
|
|
26
26
|
model: { type: "string" as const },
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
type Provider = "qwen" | "glm"
|
|
30
|
+
|
|
31
|
+
const providerModels: Record<Provider, { default: string; fast: string }> = {
|
|
32
|
+
qwen: { default: "coder-model", fast: "vision-model" },
|
|
33
|
+
glm: { default: "GLM-4-Plus", fast: "GLM-4-Flash" },
|
|
34
|
+
}
|
|
35
|
+
|
|
29
36
|
const defaultConfig: AgentConfig = {
|
|
30
37
|
moUrl: `http://localhost:${DEFAULT_MO_PORT}`,
|
|
31
38
|
apiUrl: undefined,
|
|
32
39
|
apiKey: process.env.OPENAI_API_KEY || process.env.API_KEY,
|
|
33
|
-
model: "
|
|
40
|
+
model: "coder-model",
|
|
34
41
|
thinking: true,
|
|
35
42
|
headless: true,
|
|
36
43
|
recordVideo: false,
|
|
@@ -48,12 +55,10 @@ async function prompt(question: string): Promise<string> {
|
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
async function ensureMo(moUrl: string): Promise<boolean> {
|
|
51
|
-
// check if mo is already running
|
|
52
58
|
if (await pingMo(moUrl)) {
|
|
53
59
|
return true
|
|
54
60
|
}
|
|
55
61
|
|
|
56
|
-
// mo not running, check if installed
|
|
57
62
|
const installed = await isMoInstalled()
|
|
58
63
|
|
|
59
64
|
if (!installed) {
|
|
@@ -71,7 +76,6 @@ async function ensureMo(moUrl: string): Promise<boolean> {
|
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
78
|
|
|
74
|
-
// start mo
|
|
75
79
|
try {
|
|
76
80
|
const port = parseInt(new URL(moUrl).port) || DEFAULT_MO_PORT
|
|
77
81
|
await startMo(port)
|
|
@@ -82,11 +86,13 @@ async function ensureMo(moUrl: string): Promise<boolean> {
|
|
|
82
86
|
}
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
async function registerAccount(moUrl: string): Promise<void> {
|
|
86
|
-
log.info(
|
|
89
|
+
async function registerAccount(moUrl: string, provider: Provider): Promise<void> {
|
|
90
|
+
log.info(`registering new ${provider} account...`)
|
|
87
91
|
log.info("browser will open for captcha solving")
|
|
88
92
|
|
|
89
|
-
const
|
|
93
|
+
const endpoint = provider === "qwen" ? "/auth/qwen/register" : "/auth/glm/register"
|
|
94
|
+
|
|
95
|
+
const resp = await fetch(`${moUrl}${endpoint}`, {
|
|
90
96
|
method: "POST",
|
|
91
97
|
headers: { "Content-Type": "application/json" },
|
|
92
98
|
})
|
|
@@ -98,7 +104,7 @@ async function registerAccount(moUrl: string): Promise<void> {
|
|
|
98
104
|
|
|
99
105
|
const data = await resp.json() as {
|
|
100
106
|
success: boolean
|
|
101
|
-
token: { id: string; email: string;
|
|
107
|
+
token: { id: string; email: string; is_active: boolean }
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
if (!data.success) {
|
|
@@ -107,9 +113,66 @@ async function registerAccount(moUrl: string): Promise<void> {
|
|
|
107
113
|
|
|
108
114
|
log.success(`registered: ${data.token.email}`)
|
|
109
115
|
log.info(`token id: ${data.token.id}`)
|
|
116
|
+
log.info(`provider: ${provider}`)
|
|
110
117
|
log.info("token is now active and ready to use")
|
|
111
118
|
}
|
|
112
119
|
|
|
120
|
+
async function listTokens(moUrl: string, provider: Provider): Promise<void> {
|
|
121
|
+
const endpoint = `/auth/${provider}/tokens`
|
|
122
|
+
|
|
123
|
+
const resp = await fetch(`${moUrl}${endpoint}`)
|
|
124
|
+
if (!resp.ok) {
|
|
125
|
+
throw new Error(`failed to list tokens: ${resp.status}`)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const data = await resp.json() as {
|
|
129
|
+
tokens: Array<{
|
|
130
|
+
id: string
|
|
131
|
+
email: string
|
|
132
|
+
provider: string
|
|
133
|
+
is_active: boolean
|
|
134
|
+
created_at: string
|
|
135
|
+
}>
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!data.tokens || data.tokens.length === 0) {
|
|
139
|
+
log.info(`no ${provider} tokens found`)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log()
|
|
144
|
+
log.info(`${provider} tokens:`)
|
|
145
|
+
for (const t of data.tokens) {
|
|
146
|
+
const active = t.is_active ? " [active]" : ""
|
|
147
|
+
console.log(` ${t.id} - ${t.email}${active}`)
|
|
148
|
+
}
|
|
149
|
+
console.log()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function activateToken(moUrl: string, tokenId: string): Promise<void> {
|
|
153
|
+
const providers: Provider[] = ["qwen", "glm"]
|
|
154
|
+
|
|
155
|
+
for (const provider of providers) {
|
|
156
|
+
const resp = await fetch(`${moUrl}/auth/${provider}/tokens/${tokenId}/activate`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
if (resp.ok) {
|
|
161
|
+
log.success(`token ${tokenId} activated`)
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
throw new Error(`token ${tokenId} not found`)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function detectProviderFromModel(model: string): Provider {
|
|
170
|
+
if (model.startsWith("coder-") || model.startsWith("vision-")) {
|
|
171
|
+
return "qwen"
|
|
172
|
+
}
|
|
173
|
+
return "glm"
|
|
174
|
+
}
|
|
175
|
+
|
|
113
176
|
async function main() {
|
|
114
177
|
const { values, positionals } = parseArgs({
|
|
115
178
|
args: Bun.argv.slice(2),
|
|
@@ -157,13 +220,40 @@ async function main() {
|
|
|
157
220
|
|
|
158
221
|
if (cmd === "auth") {
|
|
159
222
|
const moUrl = typeof values.mo === "string" ? values.mo : defaultConfig.moUrl
|
|
223
|
+
const subCmd = positionals[1]
|
|
160
224
|
|
|
161
225
|
if (!await ensureMo(moUrl)) {
|
|
162
226
|
process.exit(1)
|
|
163
227
|
}
|
|
164
228
|
|
|
165
229
|
try {
|
|
166
|
-
|
|
230
|
+
if (subCmd === "qwen") {
|
|
231
|
+
await registerAccount(moUrl, "qwen")
|
|
232
|
+
} else if (subCmd === "glm") {
|
|
233
|
+
await registerAccount(moUrl, "glm")
|
|
234
|
+
} else if (subCmd === "list") {
|
|
235
|
+
const provider = positionals[2] as Provider | undefined
|
|
236
|
+
if (provider && (provider === "qwen" || provider === "glm")) {
|
|
237
|
+
await listTokens(moUrl, provider)
|
|
238
|
+
} else {
|
|
239
|
+
await listTokens(moUrl, "qwen")
|
|
240
|
+
await listTokens(moUrl, "glm")
|
|
241
|
+
}
|
|
242
|
+
} else if (subCmd === "activate") {
|
|
243
|
+
const tokenId = positionals[2]
|
|
244
|
+
if (!tokenId) {
|
|
245
|
+
log.error("provide token id: traw auth activate <id>")
|
|
246
|
+
process.exit(1)
|
|
247
|
+
}
|
|
248
|
+
await activateToken(moUrl, tokenId)
|
|
249
|
+
} else {
|
|
250
|
+
log.error("usage: traw auth <qwen|glm|list|activate>")
|
|
251
|
+
log.info(" traw auth qwen - register qwen account")
|
|
252
|
+
log.info(" traw auth glm - register glm/z.ai account")
|
|
253
|
+
log.info(" traw auth list - list all tokens")
|
|
254
|
+
log.info(" traw auth activate <id> - activate token")
|
|
255
|
+
process.exit(1)
|
|
256
|
+
}
|
|
167
257
|
} catch (err: any) {
|
|
168
258
|
log.error(err.message)
|
|
169
259
|
process.exit(1)
|
|
@@ -181,7 +271,15 @@ async function main() {
|
|
|
181
271
|
const moUrl = typeof values.mo === "string" ? values.mo : defaultConfig.moUrl
|
|
182
272
|
const apiUrl = typeof values.api === "string" ? values.api : undefined
|
|
183
273
|
const apiKey = typeof values["api-key"] === "string" ? values["api-key"] : defaultConfig.apiKey
|
|
184
|
-
|
|
274
|
+
|
|
275
|
+
let model: string
|
|
276
|
+
if (typeof values.model === "string") {
|
|
277
|
+
model = values.model
|
|
278
|
+
} else {
|
|
279
|
+
const provider: Provider = "qwen"
|
|
280
|
+
model = values.fast ? providerModels[provider].fast : providerModels[provider].default
|
|
281
|
+
}
|
|
282
|
+
|
|
185
283
|
const steps = typeof values.steps === "string" ? parseInt(values.steps) : defaultConfig.maxSteps
|
|
186
284
|
|
|
187
285
|
const config: AgentConfig = {
|
|
@@ -199,7 +297,7 @@ async function main() {
|
|
|
199
297
|
|
|
200
298
|
const goal = positionals.slice(1).join(" ")
|
|
201
299
|
if (!goal) {
|
|
202
|
-
log.error("provide a goal:
|
|
300
|
+
log.error("provide a goal: traw run \"your goal\"")
|
|
203
301
|
process.exit(1)
|
|
204
302
|
}
|
|
205
303
|
|
|
@@ -217,7 +315,6 @@ async function main() {
|
|
|
217
315
|
setSilent(true)
|
|
218
316
|
}
|
|
219
317
|
|
|
220
|
-
// skip mo setup if using custom api
|
|
221
318
|
if (!config.apiUrl && !await ensureMo(config.moUrl)) {
|
|
222
319
|
process.exit(1)
|
|
223
320
|
}
|