surf-cli 2.16.0 → 2.16.1
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.
|
@@ -50,12 +50,16 @@ function modelCandidateMatches(item, targetModel) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function effortCandidateMatches(item, targetEffort) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
.flatMap((value) => normalizedWords(value))
|
|
56
|
-
.filter((word) => CHATGPT_EFFORT_CHOICES.includes(word)),
|
|
53
|
+
const labelVariants = new Set(
|
|
54
|
+
normalizedWords(item?.label).filter((word) => CHATGPT_EFFORT_CHOICES.includes(word)),
|
|
57
55
|
);
|
|
58
|
-
|
|
56
|
+
if (labelVariants.size > 0) {
|
|
57
|
+
return labelVariants.size === 1 && labelVariants.has(targetEffort);
|
|
58
|
+
}
|
|
59
|
+
const testIdVariants = new Set(
|
|
60
|
+
normalizedWords(item?.testId).filter((word) => CHATGPT_EFFORT_CHOICES.includes(word)),
|
|
61
|
+
);
|
|
62
|
+
return testIdVariants.size === 1 && testIdVariants.has(targetEffort);
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
function uniqueMatch(items, matches) {
|
|
@@ -191,14 +191,33 @@ async function readPicker(cdp, kind, click = false) {
|
|
|
191
191
|
`(() => {
|
|
192
192
|
${buildClickDispatcher()}
|
|
193
193
|
const kind = ${JSON.stringify(kind)};
|
|
194
|
+
const effortChoices = new Set(${JSON.stringify(CHATGPT_EFFORT_CHOICES)});
|
|
195
|
+
const effortOwnerLabels = new Set([...effortChoices, 'thinking']);
|
|
196
|
+
const normalize = (value) => String(value || '').toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
|
|
197
|
+
const labelFor = (node) => {
|
|
198
|
+
const labelledBy = String(node.getAttribute?.('aria-labelledby') || '')
|
|
199
|
+
.split(/\s+/)
|
|
200
|
+
.map((id) => document.getElementById(id)?.textContent || '')
|
|
201
|
+
.join(' ');
|
|
202
|
+
const text = (node.innerText || node.textContent || '').replace(/\s+/g, ' ').trim();
|
|
203
|
+
const aria = (node.getAttribute?.('aria-label') || '').replace(/\s+/g, ' ').trim();
|
|
204
|
+
const title = (node.getAttribute?.('title') || '').replace(/\s+/g, ' ').trim();
|
|
205
|
+
return [text, aria, labelledBy, title].filter(Boolean).join(' | ');
|
|
206
|
+
};
|
|
194
207
|
let nodes = Array.from(document.querySelectorAll(${JSON.stringify(selector)})).filter((node) => {
|
|
195
|
-
const value = ((node
|
|
196
|
-
if (kind !== 'model')
|
|
208
|
+
const value = normalize(labelFor(node));
|
|
209
|
+
if (kind !== 'model') {
|
|
210
|
+
const words = value.split(/\s+/).filter(Boolean);
|
|
211
|
+
const hasEffort = words.some((word) => effortOwnerLabels.has(word));
|
|
212
|
+
const looksLikeModel = node.getAttribute?.('data-testid') === 'model-switcher-dropdown-button' ||
|
|
213
|
+
value.includes('current model') || value.includes('gpt') || value.includes('instant');
|
|
214
|
+
return hasEffort && !looksLikeModel;
|
|
215
|
+
}
|
|
197
216
|
return value.includes('gpt') || value.includes('thinking') || value.includes('instant');
|
|
198
217
|
});
|
|
199
218
|
if (kind === 'model' && nodes.length === 0) {
|
|
200
219
|
nodes = Array.from(document.querySelectorAll(${JSON.stringify(selector)})).filter((node) => {
|
|
201
|
-
const value = ((node
|
|
220
|
+
const value = normalize(labelFor(node));
|
|
202
221
|
return value.includes('pro');
|
|
203
222
|
});
|
|
204
223
|
}
|
|
@@ -208,13 +227,19 @@ async function readPicker(cdp, kind, click = false) {
|
|
|
208
227
|
);
|
|
209
228
|
}
|
|
210
229
|
const items = nodes.map((node) => {
|
|
211
|
-
const text = (node.textContent || '').replace(/\\s+/g, ' ').trim();
|
|
230
|
+
const text = (node.innerText || node.textContent || '').replace(/\\s+/g, ' ').trim();
|
|
212
231
|
const aria = (node.getAttribute?.('aria-label') || '').replace(/\\s+/g, ' ').trim();
|
|
232
|
+
const labelledBy = String(node.getAttribute?.('aria-labelledby') || '')
|
|
233
|
+
.split(/\\s+/)
|
|
234
|
+
.map((id) => document.getElementById(id)?.textContent || '')
|
|
235
|
+
.join(' ')
|
|
236
|
+
.replace(/\\s+/g, ' ')
|
|
237
|
+
.trim();
|
|
213
238
|
const title = (node.getAttribute?.('title') || '').replace(/\\s+/g, ' ').trim();
|
|
214
239
|
return {
|
|
215
240
|
role: node.getAttribute?.('role') || (node.tagName === 'BUTTON' ? 'button' : null),
|
|
216
|
-
label: [text, aria, title].filter(Boolean).join(' | ').slice(0, 240),
|
|
217
|
-
displayLabel: (text || aria || title).slice(0, 80),
|
|
241
|
+
label: [text, aria, labelledBy, title].filter(Boolean).join(' | ').slice(0, 240),
|
|
242
|
+
displayLabel: (text || aria || labelledBy || title).slice(0, 80),
|
|
218
243
|
testId: node.getAttribute?.('data-testid') || null,
|
|
219
244
|
};
|
|
220
245
|
});
|
|
@@ -370,6 +395,10 @@ async function selectEffort(cdp, desiredEffort, timeoutMs = 8000, signal) {
|
|
|
370
395
|
throwIfAborted(signal);
|
|
371
396
|
const normalizedEffort = normalizeChatGPTEffortChoice(desiredEffort);
|
|
372
397
|
if (!normalizedEffort) throw verificationError("effort", desiredEffort, [], true);
|
|
398
|
+
const current = await readPicker(cdp, "effort");
|
|
399
|
+
const currentVerified = verifyChatGPTEffortSelection(current?.items, normalizedEffort);
|
|
400
|
+
if (currentVerified) return currentVerified.displayLabel || currentVerified.label;
|
|
401
|
+
|
|
373
402
|
const picker = await readPicker(cdp, "effort", true);
|
|
374
403
|
if (picker?.items?.length !== 1) throw verificationError("effort", desiredEffort);
|
|
375
404
|
await delay(300, signal);
|
|
@@ -407,6 +436,13 @@ async function typePrompt(cdp, inputCdp, prompt, signal) {
|
|
|
407
436
|
const node = document.querySelector(selector);
|
|
408
437
|
if (!node) continue;
|
|
409
438
|
dispatchClickSequence(node);
|
|
439
|
+
if ('value' in node) {
|
|
440
|
+
node.value = '';
|
|
441
|
+
node.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'deleteContentBackward' }));
|
|
442
|
+
} else {
|
|
443
|
+
node.textContent = '';
|
|
444
|
+
node.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'deleteContentBackward' }));
|
|
445
|
+
}
|
|
410
446
|
if (typeof node.focus === 'function') node.focus();
|
|
411
447
|
const doc = node.ownerDocument;
|
|
412
448
|
const selection = doc?.getSelection?.();
|
|
@@ -152,10 +152,6 @@ async function dispatch(options) {
|
|
|
152
152
|
modelVerified = await selectModel(cdp, model, 8000, signal);
|
|
153
153
|
log(`Verified model: ${modelVerified}`);
|
|
154
154
|
}
|
|
155
|
-
if (effort) {
|
|
156
|
-
effortVerified = await selectEffort(cdp, effort, 8000, signal);
|
|
157
|
-
log(`Verified effort: ${effortVerified}`);
|
|
158
|
-
}
|
|
159
155
|
if (file) {
|
|
160
156
|
if (!uploadFile) {
|
|
161
157
|
throw new Error(
|
|
@@ -177,6 +173,10 @@ async function dispatch(options) {
|
|
|
177
173
|
}
|
|
178
174
|
await typePrompt(cdp, inputCdp, prompt, signal);
|
|
179
175
|
log("Prompt typed");
|
|
176
|
+
if (effort) {
|
|
177
|
+
effortVerified = await selectEffort(cdp, effort, 8000, signal);
|
|
178
|
+
log(`Verified effort: ${effortVerified}`);
|
|
179
|
+
}
|
|
180
180
|
const baseline = normalizeResponseSnapshot(await readChatGPTResponseSnapshot(cdp));
|
|
181
181
|
if (beforeSubmit) await raceAbort(beforeSubmit, signal);
|
|
182
182
|
await clickSend(cdp, inputCdp, signal);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "surf-cli",
|
|
3
|
-
"version": "2.16.
|
|
3
|
+
"version": "2.16.1",
|
|
4
4
|
"description": "CLI for AI agents to control Chrome. Zero config, agent-agnostic, battle-tested.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chrome",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@vitest/coverage-v8": "^4.1.9",
|
|
72
72
|
"@vitest/ui": "^4.1.9",
|
|
73
73
|
"pi-subagents": "^0.52.0",
|
|
74
|
-
"puppeteer": "25.
|
|
74
|
+
"puppeteer": "25.8.0",
|
|
75
75
|
"typebox": "^1.3.11",
|
|
76
76
|
"typescript": "^7.0.2",
|
|
77
77
|
"vite": "^8.1.4",
|