wtagent 0.1.0 → 0.2.0
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/README.md +50 -36
- package/package.json +2 -2
- package/src/browser/base-web-adapter.js +1178 -0
- package/src/browser/cdp-browser.js +62 -0
- package/src/browser/cdp-state.js +52 -4
- package/src/browser/chatgpt-web-adapter.js +301 -992
- package/src/browser/claude-web-adapter.js +205 -0
- package/src/browser/deepseek-web-adapter.js +337 -0
- package/src/browser/gemini-web-adapter.js +465 -0
- package/src/browser/glm-web-adapter.js +246 -0
- package/src/browser/kimi-web-adapter.js +285 -0
- package/src/browser/provider-registry.js +199 -0
- package/src/cli/main.js +276 -59
- package/src/cli/notice-store.js +125 -0
- package/src/cli/prompt-input.js +504 -170
- package/src/cli/render-events.js +14 -8
- package/src/cli/self-update.js +145 -0
- package/src/cli/startup-notices.js +161 -0
- package/src/platform/paths.js +7 -0
- package/src/protocol/markers.js +3 -2
- package/src/protocol/prompt-builder.js +9 -5
- package/src/protocol/xml-protocol.js +193 -11
- package/src/runtime/agent-runtime.js +131 -18
- package/src/session/agent-session.js +4 -1
- package/src/session/canonical-transcript.js +2 -2
- package/src/session/session-export.js +2 -2
- package/src/shared/fetch-json.js +24 -0
- package/src/shared/limits.js +11 -1
- package/src/shared/package-info.js +23 -0
- package/src/shared/usage-limit.js +2 -2
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { ChatGPTWebAdapter } from "./chatgpt-web-adapter.js";
|
|
2
|
+
import { ClaudeWebAdapter } from "./claude-web-adapter.js";
|
|
3
|
+
import { DeepSeekWebAdapter } from "./deepseek-web-adapter.js";
|
|
4
|
+
import { GeminiWebAdapter } from "./gemini-web-adapter.js";
|
|
5
|
+
import { KimiWebAdapter } from "./kimi-web-adapter.js";
|
|
6
|
+
import { GLMWebAdapter } from "./glm-web-adapter.js";
|
|
7
|
+
import { getProfileDir } from "../platform/paths.js";
|
|
8
|
+
|
|
9
|
+
// Single source of truth for the web-AI providers WTAgent can drive. The CLI,
|
|
10
|
+
// per-provider Chrome profile resolution, and `resume` all read from here so a
|
|
11
|
+
// new provider is added in exactly one place.
|
|
12
|
+
//
|
|
13
|
+
// Fields:
|
|
14
|
+
// id - stable CLI/session identifier (the `--model` value)
|
|
15
|
+
// label - human display name
|
|
16
|
+
// baseUrl - origin the adapter operates on (login + conversation)
|
|
17
|
+
// profileBasename - dedicated Chrome profile directory name under the app
|
|
18
|
+
// data dir; each provider logs in once, independently
|
|
19
|
+
// status - "active" (has a working adapter) | "planned" (named
|
|
20
|
+
// but not implemented yet)
|
|
21
|
+
// promptsForMode - whether the CLI shows an interactive mode picker at
|
|
22
|
+
// conversation start (ChatGPT has Pro/Current; most
|
|
23
|
+
// providers do not prompt)
|
|
24
|
+
// defaultMode - mode applied silently at conversation start when the
|
|
25
|
+
// provider does not prompt (null = keep the site's
|
|
26
|
+
// current setting). The adapter's selectMode()
|
|
27
|
+
// interprets this value.
|
|
28
|
+
// adapter - the adapter class, or null until implemented
|
|
29
|
+
//
|
|
30
|
+
// ChatGPT keeps the historical "chrome-profile" basename so existing logins,
|
|
31
|
+
// the logout guard, and cli.test.js keep working unchanged.
|
|
32
|
+
export const PROVIDERS = Object.freeze({
|
|
33
|
+
chatgpt: Object.freeze({
|
|
34
|
+
id: "chatgpt",
|
|
35
|
+
label: "ChatGPT",
|
|
36
|
+
baseUrl: "https://chatgpt.com/",
|
|
37
|
+
profileBasename: "chrome-profile",
|
|
38
|
+
status: "active",
|
|
39
|
+
promptsForMode: true,
|
|
40
|
+
defaultMode: null,
|
|
41
|
+
adapter: ChatGPTWebAdapter,
|
|
42
|
+
}),
|
|
43
|
+
deepseek: Object.freeze({
|
|
44
|
+
id: "deepseek",
|
|
45
|
+
label: "DeepSeek",
|
|
46
|
+
baseUrl: "https://chat.deepseek.com/",
|
|
47
|
+
profileBasename: "deepseek-profile",
|
|
48
|
+
status: "active",
|
|
49
|
+
// DeepSeek does not prompt; every new conversation silently switches to
|
|
50
|
+
// 专家模式 (Expert) + 深度思考 (Deep Thinking) — see DeepSeekWebAdapter.selectMode.
|
|
51
|
+
promptsForMode: false,
|
|
52
|
+
defaultMode: "expert-thinking",
|
|
53
|
+
adapter: DeepSeekWebAdapter,
|
|
54
|
+
}),
|
|
55
|
+
claude: Object.freeze({
|
|
56
|
+
id: "claude",
|
|
57
|
+
label: "Claude",
|
|
58
|
+
baseUrl: "https://claude.ai/",
|
|
59
|
+
profileBasename: "claude-profile",
|
|
60
|
+
status: "active",
|
|
61
|
+
promptsForMode: false,
|
|
62
|
+
// Keep whatever model claude.ai selects in this browser profile. WTAgent
|
|
63
|
+
// does not open the model menu or override the account/site default.
|
|
64
|
+
defaultMode: null,
|
|
65
|
+
adapter: ClaudeWebAdapter,
|
|
66
|
+
}),
|
|
67
|
+
grok: Object.freeze({
|
|
68
|
+
id: "grok",
|
|
69
|
+
label: "Grok",
|
|
70
|
+
baseUrl: "https://grok.com/",
|
|
71
|
+
profileBasename: "grok-profile",
|
|
72
|
+
status: "planned",
|
|
73
|
+
promptsForMode: false,
|
|
74
|
+
defaultMode: null,
|
|
75
|
+
adapter: null,
|
|
76
|
+
}),
|
|
77
|
+
kimi: Object.freeze({
|
|
78
|
+
id: "kimi",
|
|
79
|
+
label: "Kimi",
|
|
80
|
+
baseUrl: "https://www.kimi.com/",
|
|
81
|
+
profileBasename: "kimi-profile",
|
|
82
|
+
status: "active",
|
|
83
|
+
// Kimi does not prompt; every new conversation silently switches to the K3
|
|
84
|
+
// flagship model — see KimiWebAdapter.selectMode.
|
|
85
|
+
promptsForMode: false,
|
|
86
|
+
defaultMode: "k3",
|
|
87
|
+
adapter: KimiWebAdapter,
|
|
88
|
+
}),
|
|
89
|
+
glm: Object.freeze({
|
|
90
|
+
id: "glm",
|
|
91
|
+
label: "GLM",
|
|
92
|
+
baseUrl: "https://chat.z.ai/",
|
|
93
|
+
profileBasename: "glm-profile",
|
|
94
|
+
status: "active",
|
|
95
|
+
// GLM does not prompt; every new conversation silently selects the newest
|
|
96
|
+
// available model (GLM-5.3, else GLM-5.2) — see GLMWebAdapter.selectMode.
|
|
97
|
+
promptsForMode: false,
|
|
98
|
+
defaultMode: "latest",
|
|
99
|
+
adapter: GLMWebAdapter,
|
|
100
|
+
}),
|
|
101
|
+
gemini: Object.freeze({
|
|
102
|
+
id: "gemini",
|
|
103
|
+
label: "Gemini",
|
|
104
|
+
baseUrl: "https://gemini.google.com/app",
|
|
105
|
+
profileBasename: "gemini-profile",
|
|
106
|
+
status: "active",
|
|
107
|
+
promptsForMode: false,
|
|
108
|
+
// Preserve the model currently selected by Gemini in this profile.
|
|
109
|
+
defaultMode: null,
|
|
110
|
+
adapter: GeminiWebAdapter,
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
export const DEFAULT_PROVIDER = "chatgpt";
|
|
115
|
+
|
|
116
|
+
export function listActiveProviderIds() {
|
|
117
|
+
return Object.values(PROVIDERS)
|
|
118
|
+
.filter((provider) => provider.status === "active")
|
|
119
|
+
.map((provider) => provider.id);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Returns the provider record without asserting it is usable. Useful for
|
|
123
|
+
// profile-directory resolution, which must work for any known id (e.g. so
|
|
124
|
+
// `logout` can reset a profile). Throws only for a genuinely unknown id.
|
|
125
|
+
export function findProvider(providerId) {
|
|
126
|
+
if (providerId == null || providerId === "") {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return PROVIDERS[String(providerId).toLowerCase()] ?? null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function getProvider(providerId) {
|
|
133
|
+
const provider = findProvider(providerId);
|
|
134
|
+
if (!provider) {
|
|
135
|
+
const known = Object.keys(PROVIDERS).join(", ");
|
|
136
|
+
throw new Error(`Unknown model "${providerId}". Known providers: ${known}.`);
|
|
137
|
+
}
|
|
138
|
+
return provider;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// `--mode` is ChatGPT's Pro/Current switch, but people often type
|
|
142
|
+
// `--mode kimi` meaning the Kimi provider. If the value is a known provider
|
|
143
|
+
// id, treat it as `--model` and clear `--mode` so ChatGPT mode parsing is not
|
|
144
|
+
// applied. Conflicting `--model` + `--mode <provider>` is rejected.
|
|
145
|
+
export function resolveCliProviderSelection({ model, mode } = {}) {
|
|
146
|
+
const modeAsProvider = findProvider(mode);
|
|
147
|
+
if (!modeAsProvider) {
|
|
148
|
+
return { model, mode };
|
|
149
|
+
}
|
|
150
|
+
if (model != null && getProvider(model).id !== modeAsProvider.id) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`--mode ${mode} selects ${modeAsProvider.label}, but --model ${model} is already set. `
|
|
153
|
+
+ `Use --model ${modeAsProvider.id}.`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return { model: modeAsProvider.id, mode: undefined };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Resolves the dedicated Chrome profile directory for a provider under the
|
|
160
|
+
// given app data dir. ChatGPT resolves to the historical "chrome-profile".
|
|
161
|
+
export function getProviderProfileDir(appDataDir, providerId) {
|
|
162
|
+
return getProfileDir(appDataDir, getProvider(providerId).profileBasename);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Whether `basename` is the profile directory name of any known provider. The
|
|
166
|
+
// logout guard uses this so it will only ever delete a directory that matches a
|
|
167
|
+
// real provider profile.
|
|
168
|
+
export function isProviderProfileBasename(basename) {
|
|
169
|
+
return Object.values(PROVIDERS)
|
|
170
|
+
.some((provider) => provider.profileBasename === basename);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Resolves a provider that has a working adapter, or throws a clear,
|
|
174
|
+
// actionable error. Used everywhere WTAgent is about to actually drive a
|
|
175
|
+
// browser (run, resume, login).
|
|
176
|
+
export function resolveProvider(providerId = DEFAULT_PROVIDER) {
|
|
177
|
+
const provider = getProvider(providerId);
|
|
178
|
+
if (provider.status !== "active" || !provider.adapter) {
|
|
179
|
+
const active = listActiveProviderIds().join(", ");
|
|
180
|
+
throw new Error(
|
|
181
|
+
`Model "${provider.id}" is not supported yet. Active providers: ${active}. `
|
|
182
|
+
+ "Support for more providers is planned.",
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return provider;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Builds the adapter instance for a provider. `provider` may be an id or a
|
|
189
|
+
// provider record. All remaining options are forwarded to the adapter
|
|
190
|
+
// constructor (profileDir, chromePath, debug, minimized, cancelOnEsc).
|
|
191
|
+
export function createWebAdapter({ provider, ...adapterOptions }) {
|
|
192
|
+
const record = typeof provider === "string"
|
|
193
|
+
? resolveProvider(provider)
|
|
194
|
+
: (provider?.adapter ? provider : resolveProvider(provider?.id));
|
|
195
|
+
return new record.adapter({
|
|
196
|
+
...adapterOptions,
|
|
197
|
+
baseUrl: record.baseUrl,
|
|
198
|
+
});
|
|
199
|
+
}
|