pi-cursor-bridge 0.1.11 → 0.1.12
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/.codex-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/cursor-bridge.mjs +283 -81
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-bridge",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.2+codex.20260904092304",
|
|
4
4
|
"description": "Evidence-backed Cursor Context Engine search and bounded Cursor Agent execution, including a UI-suppressed minimal runtime.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vanyangyang"
|
package/README.md
CHANGED
|
@@ -10,6 +10,6 @@ pi install npm:pi-cursor-bridge
|
|
|
10
10
|
|
|
11
11
|
Restart Pi after installation. Initialize the current project by asking Pi to initialize Cursor Bridge for the absolute project path, then use it normally. The package includes the `cce-routing` and `cursor-delegate` Skills and registers the native Cursor Bridge MCP tools directly in Pi.
|
|
12
12
|
|
|
13
|
-
This Pi package embeds Cursor Bridge 5.8.
|
|
13
|
+
This Pi package embeds Cursor Bridge 5.8.2. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
|
|
14
14
|
|
|
15
15
|
Full documentation: [English](https://github.com/Vanyangyang/cursor-bridge#readme) · [简体中文](https://github.com/Vanyangyang/cursor-bridge/blob/main/README.zh-CN.md)
|
package/dist/cursor-bridge.mjs
CHANGED
|
@@ -22604,7 +22604,7 @@ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
|
|
|
22604
22604
|
// server.mjs
|
|
22605
22605
|
init_cursor_ensure_core();
|
|
22606
22606
|
init_lifecycle_paths();
|
|
22607
|
-
var PLUGIN_VERSION = "5.8.
|
|
22607
|
+
var PLUGIN_VERSION = "5.8.2";
|
|
22608
22608
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
22609
22609
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
22610
22610
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -22963,10 +22963,7 @@ function createChatPanelUnavailableError(snapshot) {
|
|
|
22963
22963
|
error2.uiDiagnostic = uiDiagnostic;
|
|
22964
22964
|
return error2;
|
|
22965
22965
|
}
|
|
22966
|
-
function
|
|
22967
|
-
const js = JSON.stringify(text);
|
|
22968
|
-
return `(function(){${INPUT_PICKER_BODY}const inp=pickInput();if(!inp)return 'NO_INPUT';inp.focus();try{const s=getSelection();const r=document.createRange();r.selectNodeContents(inp);s.removeAllRanges();s.addRange(r);}catch(e){}const ok=document.execCommand('insertText',false,${js});try{inp.dispatchEvent(new InputEvent('input',{bubbles:true,inputType:'insertText',data:${js}}));}catch(e){inp.dispatchEvent(new Event('input',{bubbles:true}));}return ok?(inp.innerText||'').slice(0,30):'EXEC_FAIL';})()`;
|
|
22969
|
-
}
|
|
22966
|
+
var EXPR_PREPARE_INPUT = `(function(){${INPUT_PICKER_BODY}const inp=pickInput();if(!inp)return 'NO_INPUT';inp.focus();try{const s=getSelection();const r=document.createRange();r.selectNodeContents(inp);s.removeAllRanges();s.addRange(r);}catch(e){}return 'READY';})()`;
|
|
22970
22967
|
var EXPR_SNAP = `(function(){
|
|
22971
22968
|
const md=[...document.querySelectorAll('.markdown-root,.aichat-container [class*=markdown]')]
|
|
22972
22969
|
.filter(e=>e.offsetParent!==null&&!e.closest('.ui-model-picker__trigger,[class*=model-picker]'));
|
|
@@ -22977,14 +22974,17 @@ var EXPR_SNAP = `(function(){
|
|
|
22977
22974
|
${INPUT_PICKER_BODY}
|
|
22978
22975
|
const input=pickInput();
|
|
22979
22976
|
const inputText=String(input&&(input.innerText||input.textContent)||'').trim();
|
|
22980
|
-
|
|
22977
|
+
const composer=input&&(input.closest('.composer-bar,.ui-prompt-input,.agent-prompt-input-root')||input.parentElement);
|
|
22978
|
+
const sendButtons=composer?[...composer.querySelectorAll('button.ui-prompt-input-submit-button[data-state="send"],button.ui-prompt-input-submit-button[aria-label="Send message"],button[aria-label="Send"]')]
|
|
22979
|
+
.filter(button=>button.offsetParent!==null&&!button.disabled&&button.closest('.composer-bar,.ui-prompt-input,.agent-prompt-input-root')===composer):[];
|
|
22980
|
+
return JSON.stringify({messageCount:texts.length,replyLength:last.length,replyHash:hash,stop,inputTextLength:inputText.length,sendReady:sendButtons.length===1});
|
|
22981
22981
|
})()`;
|
|
22982
22982
|
var EXPR_CLICK_SEND = `(function(){${INPUT_PICKER_BODY}
|
|
22983
22983
|
const input=pickInput();if(!input)return 'NO_INPUT';
|
|
22984
|
-
const composer=input.closest('.composer-bar')||input.parentElement;
|
|
22984
|
+
const composer=input.closest('.composer-bar,.ui-prompt-input,.agent-prompt-input-root')||input.parentElement;
|
|
22985
22985
|
if(!composer)return 'NO_COMPOSER';
|
|
22986
|
-
const buttons=[...composer.querySelectorAll('button.ui-prompt-input-submit-button[data-state="send"],button[aria-label="Send"]')]
|
|
22987
|
-
.filter(button=>button.offsetParent!==null&&!button.disabled&&button.closest('.composer-bar')===
|
|
22986
|
+
const buttons=[...composer.querySelectorAll('button.ui-prompt-input-submit-button[data-state="send"],button.ui-prompt-input-submit-button[aria-label="Send message"],button[aria-label="Send"]')]
|
|
22987
|
+
.filter(button=>button.offsetParent!==null&&!button.disabled&&button.closest('.composer-bar,.ui-prompt-input,.agent-prompt-input-root')===composer);
|
|
22988
22988
|
if(buttons.length!==1)return buttons.length?'AMBIGUOUS_SEND':'NO_SEND';
|
|
22989
22989
|
buttons[0].click();return 'CLICKED';
|
|
22990
22990
|
})()`;
|
|
@@ -23102,7 +23102,7 @@ function exprClickBoundComposerStop(agentId) {
|
|
|
23102
23102
|
var EXPR_FIND_NEWAGENT = `(function(){const b=[...document.querySelectorAll('button,[role=button],a.action-label,.codicon')].find(e=>{if(e.offsetParent===null||e.closest('.glass-sidebar-agent-menu-btn'))return false;const s=(e.getAttribute('aria-label')||'')+' '+(e.getAttribute('title')||'')+' '+(e.innerText||'');return /(?:^|\\s)New (?:Agent|Chat)(?:\\s|$)/i.test(s);});if(!b)return '';const r=b.getBoundingClientRect();return JSON.stringify({x:Math.round(r.x+r.width/2),y:Math.round(r.y+r.height/2)});})()`;
|
|
23103
23103
|
var MODEL_PICKER_VISIBLE_BODY = `
|
|
23104
23104
|
const visible=(node)=>!!(node&&(node.offsetParent!==null||(node.getClientRects&&node.getClientRects().length>0)));
|
|
23105
|
-
const composers=[...document.querySelectorAll('.composer-bar[data-composer-id],.composer-bar,.ui-prompt-input-root')].filter(visible);
|
|
23105
|
+
const composers=[...document.querySelectorAll('.composer-bar[data-composer-id],.composer-bar,.ui-prompt-input-root,.agent-prompt-input-root,.ui-prompt-input')].filter(visible);
|
|
23106
23106
|
const composer=composers[composers.length-1]||document;
|
|
23107
23107
|
`;
|
|
23108
23108
|
var EXPR_MODEL_PICKER_TRIGGER = `(function(){
|
|
@@ -23158,15 +23158,23 @@ function selectModelPickerRow(rows, requested, kind = "model") {
|
|
|
23158
23158
|
const candidates = (Array.isArray(rows) ? rows : []).filter((row) => row && row.disabled !== true && (kind === "any" || row.kind === kind)).map((row) => {
|
|
23159
23159
|
const normalized = normalizeModelPickerText(row.text);
|
|
23160
23160
|
let score = normalized === wanted ? 1e3 : 0;
|
|
23161
|
-
if (
|
|
23162
|
-
|
|
23163
|
-
|
|
23161
|
+
if (kind !== "parameter") {
|
|
23162
|
+
if (!score && normalized.startsWith(wanted)) score = 800;
|
|
23163
|
+
if (!score && wanted.startsWith(normalized)) score = 700;
|
|
23164
|
+
if (!score && normalized.includes(wanted)) score = 600;
|
|
23165
|
+
}
|
|
23164
23166
|
return { row, score, distance: Math.abs(normalized.length - wanted.length) };
|
|
23165
23167
|
}).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.distance - b.distance);
|
|
23166
23168
|
if (candidates.length === 0) return null;
|
|
23167
23169
|
if (candidates.length > 1 && candidates[0].score === candidates[1].score && candidates[0].distance === candidates[1].distance) return null;
|
|
23168
23170
|
return candidates[0].row;
|
|
23169
23171
|
}
|
|
23172
|
+
function createModelSelectionError(message, failureClass, retryable, diagnostic = {}) {
|
|
23173
|
+
const error2 = new Error(message);
|
|
23174
|
+
error2.code = `CURSOR_MODEL_${String(failureClass || "PROBE_ERROR").toUpperCase()}`;
|
|
23175
|
+
error2.modelSelectionFailure = { failureClass, retryable, ...diagnostic };
|
|
23176
|
+
return error2;
|
|
23177
|
+
}
|
|
23170
23178
|
var WORKSPACE_SECTION_BODY = `
|
|
23171
23179
|
const headText=(el)=>String(el&&(el.innerText||el.textContent)||'').trim();
|
|
23172
23180
|
const isNewAgentButton=(node)=>{
|
|
@@ -24821,7 +24829,7 @@ var CursorBridge = class {
|
|
|
24821
24829
|
this._throwIfCancelledBeforeSend(options);
|
|
24822
24830
|
await this._applyModelPreference(c, options.modelPreference, options);
|
|
24823
24831
|
this._throwIfCancelledBeforeSend(options);
|
|
24824
|
-
const filled = await
|
|
24832
|
+
const filled = await this._fillPrompt(c, prompt, options);
|
|
24825
24833
|
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
24826
24834
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") throw new Error("Failed to enter the query because the input state was invalid");
|
|
24827
24835
|
await sleep2(450);
|
|
@@ -24941,25 +24949,83 @@ var CursorBridge = class {
|
|
|
24941
24949
|
modelRow = selectModelPickerRow(expanded && expanded.rows, requestedModel, "model");
|
|
24942
24950
|
return { snapshot: expanded, modelRow };
|
|
24943
24951
|
}
|
|
24944
|
-
|
|
24945
|
-
|
|
24946
|
-
|
|
24947
|
-
|
|
24948
|
-
|
|
24949
|
-
|
|
24952
|
+
_inspectModelPickerRows(snapshot, requested, kind) {
|
|
24953
|
+
const rows = (snapshot && Array.isArray(snapshot.rows) ? snapshot.rows : []).filter((row) => row && row.disabled !== true && row.kind === kind);
|
|
24954
|
+
const wanted = normalizeModelPickerText(requested);
|
|
24955
|
+
const exact = rows.filter((row) => normalizeModelPickerText(row.text) === wanted);
|
|
24956
|
+
return {
|
|
24957
|
+
snapshot,
|
|
24958
|
+
row: exact.length === 1 ? exact[0] : null,
|
|
24959
|
+
ambiguous: exact.length > 1,
|
|
24960
|
+
available: rows.map((row) => row.text)
|
|
24961
|
+
};
|
|
24962
|
+
}
|
|
24963
|
+
async _waitForModelPickerMatch(c, requested, kind, job, options = {}) {
|
|
24964
|
+
const timeoutMs = Math.max(0, Number(options.timeoutMs ?? 700));
|
|
24965
|
+
const pollMs = Math.max(10, Number(options.pollMs ?? 100));
|
|
24966
|
+
const stableMs = Math.max(0, Number(options.stableMs ?? 250));
|
|
24967
|
+
const deadline = Date.now() + timeoutMs;
|
|
24968
|
+
let stableSignature = null;
|
|
24969
|
+
let stableSince = 0;
|
|
24970
|
+
let last = this._inspectModelPickerRows({ open: false, rows: [] }, requested, kind);
|
|
24971
|
+
do {
|
|
24972
|
+
this._throwIfCancelledBeforeSend(job);
|
|
24973
|
+
last = this._inspectModelPickerRows(await this._readModelPickerRows(c), requested, kind);
|
|
24974
|
+
if (last.row) return { ...last, state: "matched" };
|
|
24975
|
+
const signature = JSON.stringify(last.available);
|
|
24976
|
+
if (last.available.length > 0) {
|
|
24977
|
+
if (signature !== stableSignature) {
|
|
24978
|
+
stableSignature = signature;
|
|
24979
|
+
stableSince = Date.now();
|
|
24980
|
+
} else if (Date.now() - stableSince >= stableMs) {
|
|
24981
|
+
return { ...last, state: last.ambiguous ? "ambiguous" : "unsupported" };
|
|
24982
|
+
}
|
|
24983
|
+
} else {
|
|
24984
|
+
stableSignature = null;
|
|
24985
|
+
stableSince = 0;
|
|
24986
|
+
}
|
|
24987
|
+
if (Date.now() >= deadline) break;
|
|
24988
|
+
await sleep2(Math.min(pollMs, Math.max(0, deadline - Date.now())));
|
|
24989
|
+
} while (Date.now() <= deadline);
|
|
24990
|
+
return { ...last, state: last.ambiguous ? "ambiguous" : "not_rendered" };
|
|
24991
|
+
}
|
|
24992
|
+
async _selectedEffortRow(c, modelRow, effort, job) {
|
|
24993
|
+
const requested = cursorEffortUiValue(effort);
|
|
24994
|
+
const snapshot = await this._readModelPickerRows(c);
|
|
24995
|
+
const immediate = this._inspectModelPickerRows(snapshot, requested, "parameter");
|
|
24996
|
+
if (immediate.row) return { ...immediate, state: "matched", attempts: ["visible"] };
|
|
24997
|
+
const attempts = [];
|
|
24998
|
+
const effortControl = (snapshot.rows || []).find((row) => row.kind === "effort_control" && row.disabled !== true);
|
|
24999
|
+
if (effortControl) {
|
|
25000
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25001
|
+
attempts.push("effort_control");
|
|
25002
|
+
await this._clickModelPickerPoint(c, effortControl);
|
|
25003
|
+
const result2 = await this._waitForModelPickerMatch(c, requested, "parameter", job, { timeoutMs: 700 });
|
|
25004
|
+
if (result2.state !== "not_rendered") return { ...result2, attempts };
|
|
24950
25005
|
}
|
|
24951
|
-
|
|
25006
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25007
|
+
attempts.push("model_hover");
|
|
24952
25008
|
await this._hoverModelPickerPoint(c, modelRow);
|
|
24953
|
-
await
|
|
24954
|
-
|
|
24955
|
-
|
|
24956
|
-
|
|
25009
|
+
let result = await this._waitForModelPickerMatch(c, requested, "parameter", job, { timeoutMs: 700 });
|
|
25010
|
+
if (result.state !== "not_rendered") return { ...result, attempts };
|
|
25011
|
+
if (modelRow && modelRow.hasSubmenu) {
|
|
25012
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25013
|
+
attempts.push("model_click");
|
|
24957
25014
|
await this._clickModelPickerPoint(c, modelRow);
|
|
24958
|
-
await
|
|
24959
|
-
snapshot = await this._readModelPickerRows(c);
|
|
24960
|
-
effortRow = selectModelPickerRow(snapshot.rows, cursorEffortUiValue(effort), "parameter");
|
|
25015
|
+
result = await this._waitForModelPickerMatch(c, requested, "parameter", job, { timeoutMs: 1100 });
|
|
24961
25016
|
}
|
|
24962
|
-
return
|
|
25017
|
+
return { ...result, attempts };
|
|
25018
|
+
}
|
|
25019
|
+
async _waitForSelectedModelPickerRow(c, requested, kind, job, timeoutMs = 1500) {
|
|
25020
|
+
const deadline = Date.now() + timeoutMs;
|
|
25021
|
+
do {
|
|
25022
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25023
|
+
const inspected = this._inspectModelPickerRows(await this._readModelPickerRows(c), requested, kind);
|
|
25024
|
+
if (inspected.row && inspected.row.selected) return inspected.row;
|
|
25025
|
+
if (Date.now() >= deadline) break;
|
|
25026
|
+
await sleep2(Math.min(100, Math.max(0, deadline - Date.now())));
|
|
25027
|
+
} while (Date.now() <= deadline);
|
|
25028
|
+
return null;
|
|
24963
25029
|
}
|
|
24964
25030
|
async _applyModelPreference(c, preference, job) {
|
|
24965
25031
|
if (!preference) {
|
|
@@ -24968,63 +25034,167 @@ var CursorBridge = class {
|
|
|
24968
25034
|
}
|
|
24969
25035
|
const requestedModel = String(preference.model || "").trim();
|
|
24970
25036
|
const requestedEffort = preference.effort ? normalizeCursorModelEffort(preference.effort, "") : null;
|
|
24971
|
-
|
|
24972
|
-
let located = await this._findModelPickerModel(c, opened, requestedModel);
|
|
24973
|
-
let modelRow = located.modelRow;
|
|
24974
|
-
if (!modelRow) {
|
|
24975
|
-
throw new Error(`Configured Cursor model is unavailable or ambiguous: ${requestedModel}`);
|
|
24976
|
-
}
|
|
24977
|
-
let selectedEffortRow = null;
|
|
24978
|
-
if (requestedEffort && modelRow.hasSubmenu) {
|
|
24979
|
-
selectedEffortRow = await this._selectedEffortRow(c, modelRow, requestedEffort);
|
|
24980
|
-
if (!selectedEffortRow) {
|
|
24981
|
-
throw new Error(`Cursor model ${requestedModel} does not expose effort ${requestedEffort}`);
|
|
24982
|
-
}
|
|
24983
|
-
if (!selectedEffortRow.selected || !modelRow.selected) {
|
|
24984
|
-
await this._clickModelPickerPoint(c, selectedEffortRow);
|
|
24985
|
-
await sleep2(550);
|
|
24986
|
-
}
|
|
24987
|
-
} else if (!modelRow.selected) {
|
|
24988
|
-
await this._clickModelPickerPoint(c, modelRow);
|
|
24989
|
-
await sleep2(550);
|
|
24990
|
-
}
|
|
24991
|
-
let trigger = await this._readModelPickerTrigger(c);
|
|
24992
|
-
if (!trigger.found || !normalizeModelPickerText(trigger.text).includes(normalizeModelPickerText(requestedModel))) {
|
|
24993
|
-
const reopened = await this._openModelPicker(c);
|
|
24994
|
-
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
24995
|
-
const selected = selectModelPickerRow(located.snapshot.rows.filter((row) => row.selected), requestedModel, "model");
|
|
24996
|
-
if (!selected) throw new Error(`Cursor did not confirm configured model ${requestedModel}`);
|
|
24997
|
-
modelRow = selected;
|
|
24998
|
-
}
|
|
25037
|
+
let modelRow = null;
|
|
24999
25038
|
let effectiveEffort = null;
|
|
25000
|
-
|
|
25001
|
-
|
|
25002
|
-
|
|
25039
|
+
let primaryError = null;
|
|
25040
|
+
try {
|
|
25041
|
+
const opened = await this._openModelPicker(c);
|
|
25042
|
+
let located = await this._findModelPickerModel(c, opened, requestedModel);
|
|
25003
25043
|
modelRow = located.modelRow;
|
|
25004
|
-
if (!modelRow)
|
|
25005
|
-
|
|
25006
|
-
|
|
25007
|
-
|
|
25044
|
+
if (!modelRow) {
|
|
25045
|
+
throw createModelSelectionError(
|
|
25046
|
+
`Configured Cursor model is unavailable or ambiguous: ${requestedModel}`,
|
|
25047
|
+
"model_unavailable",
|
|
25048
|
+
false,
|
|
25049
|
+
{ available: (located.snapshot.rows || []).filter((row) => row.kind === "model").map((row) => row.text) }
|
|
25050
|
+
);
|
|
25008
25051
|
}
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
await
|
|
25052
|
+
let effortPickerReopened = false;
|
|
25053
|
+
const resolveEffort = async () => {
|
|
25054
|
+
let outcome = await this._selectedEffortRow(c, modelRow, requestedEffort, job);
|
|
25055
|
+
if (!outcome.row && outcome.state === "not_rendered" && !effortPickerReopened) {
|
|
25056
|
+
effortPickerReopened = true;
|
|
25057
|
+
await this._closeModelPicker(c);
|
|
25058
|
+
const reopened = await this._openModelPicker(c);
|
|
25059
|
+
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
25060
|
+
modelRow = located.modelRow;
|
|
25061
|
+
if (!modelRow) {
|
|
25062
|
+
throw createModelSelectionError(
|
|
25063
|
+
`Cursor model row disappeared while applying effort: ${requestedModel}`,
|
|
25064
|
+
"model_unavailable",
|
|
25065
|
+
true
|
|
25066
|
+
);
|
|
25067
|
+
}
|
|
25068
|
+
outcome = await this._selectedEffortRow(c, modelRow, requestedEffort, job);
|
|
25069
|
+
outcome.attempts = ["picker_reopen", ...outcome.attempts || []];
|
|
25070
|
+
}
|
|
25071
|
+
return outcome;
|
|
25072
|
+
};
|
|
25073
|
+
const throwEffortFailure = (outcome) => {
|
|
25074
|
+
const failureClass = outcome.state === "ambiguous" ? "effort_ambiguous" : outcome.state === "unsupported" ? "effort_unsupported" : "effort_menu_not_rendered";
|
|
25075
|
+
const message = failureClass === "effort_menu_not_rendered" ? `Cursor effort menu did not render for model ${requestedModel}` : failureClass === "effort_ambiguous" ? `Cursor model ${requestedModel} exposes ambiguous effort ${requestedEffort}` : `Cursor model ${requestedModel} does not expose effort ${requestedEffort}`;
|
|
25076
|
+
throw createModelSelectionError(
|
|
25077
|
+
message,
|
|
25078
|
+
failureClass,
|
|
25079
|
+
failureClass === "effort_menu_not_rendered",
|
|
25080
|
+
{ available: outcome.available || [], attempts: outcome.attempts || [] }
|
|
25081
|
+
);
|
|
25082
|
+
};
|
|
25083
|
+
if (requestedEffort && modelRow.hasSubmenu) {
|
|
25084
|
+
const selectedEffort = await resolveEffort();
|
|
25085
|
+
if (!selectedEffort.row) throwEffortFailure(selectedEffort);
|
|
25086
|
+
if (!selectedEffort.row.selected || !modelRow.selected) {
|
|
25087
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25088
|
+
await this._clickModelPickerPoint(c, selectedEffort.row);
|
|
25089
|
+
await sleep2(550);
|
|
25090
|
+
}
|
|
25091
|
+
} else if (!modelRow.selected) {
|
|
25092
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25093
|
+
await this._clickModelPickerPoint(c, modelRow);
|
|
25094
|
+
await sleep2(550);
|
|
25012
25095
|
}
|
|
25013
|
-
|
|
25014
|
-
|
|
25015
|
-
|
|
25016
|
-
|
|
25017
|
-
const
|
|
25018
|
-
|
|
25019
|
-
|
|
25096
|
+
let trigger2 = await this._readModelPickerTrigger(c);
|
|
25097
|
+
if (!trigger2.found || !normalizeModelPickerText(trigger2.text).includes(normalizeModelPickerText(requestedModel))) {
|
|
25098
|
+
const reopened = await this._openModelPicker(c);
|
|
25099
|
+
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
25100
|
+
const selected = selectModelPickerRow(located.snapshot.rows.filter((row) => row.selected), requestedModel, "model");
|
|
25101
|
+
if (!selected) {
|
|
25102
|
+
throw createModelSelectionError(
|
|
25103
|
+
`Cursor did not confirm configured model ${requestedModel}`,
|
|
25104
|
+
"model_not_confirmed",
|
|
25105
|
+
true
|
|
25106
|
+
);
|
|
25107
|
+
}
|
|
25108
|
+
modelRow = selected;
|
|
25109
|
+
}
|
|
25110
|
+
if (requestedEffort) {
|
|
25111
|
+
const reopened = await this._openModelPicker(c);
|
|
25112
|
+
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
25113
|
+
modelRow = located.modelRow;
|
|
25114
|
+
if (!modelRow) {
|
|
25115
|
+
throw createModelSelectionError(
|
|
25116
|
+
`Cursor model row disappeared while applying effort: ${requestedModel}`,
|
|
25117
|
+
"model_unavailable",
|
|
25118
|
+
true
|
|
25119
|
+
);
|
|
25120
|
+
}
|
|
25121
|
+
const effortOutcome = await resolveEffort();
|
|
25122
|
+
if (!effortOutcome.row) throwEffortFailure(effortOutcome);
|
|
25123
|
+
let effortRow = effortOutcome.row;
|
|
25124
|
+
if (!effortRow.selected) {
|
|
25125
|
+
effortRow = await this._waitForSelectedModelPickerRow(
|
|
25126
|
+
c,
|
|
25127
|
+
cursorEffortUiValue(requestedEffort),
|
|
25128
|
+
"parameter",
|
|
25129
|
+
job
|
|
25130
|
+
);
|
|
25131
|
+
}
|
|
25020
25132
|
if (!effortRow || !effortRow.selected) {
|
|
25021
|
-
throw
|
|
25133
|
+
throw createModelSelectionError(
|
|
25134
|
+
`Cursor did not confirm effort ${requestedEffort} for model ${requestedModel}`,
|
|
25135
|
+
"effort_not_confirmed",
|
|
25136
|
+
true
|
|
25137
|
+
);
|
|
25022
25138
|
}
|
|
25139
|
+
effectiveEffort = requestedEffort;
|
|
25140
|
+
}
|
|
25141
|
+
} catch (error2) {
|
|
25142
|
+
primaryError = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
25143
|
+
let failure = primaryError.modelSelectionFailure;
|
|
25144
|
+
if (!failure) {
|
|
25145
|
+
const pickerUnavailable = /model picker (?:is unavailable|did not open)/i.test(primaryError.message);
|
|
25146
|
+
failure = {
|
|
25147
|
+
failureClass: pickerUnavailable ? "picker_did_not_open" : "probe_error",
|
|
25148
|
+
retryable: true
|
|
25149
|
+
};
|
|
25150
|
+
}
|
|
25151
|
+
const diagnostic = {
|
|
25152
|
+
configured: true,
|
|
25153
|
+
applied: false,
|
|
25154
|
+
requestedModel,
|
|
25155
|
+
requestedEffort,
|
|
25156
|
+
failureClass: failure.failureClass,
|
|
25157
|
+
retryable: failure.retryable === true,
|
|
25158
|
+
errorCode: primaryError.code || `CURSOR_MODEL_${String(failure.failureClass).toUpperCase()}`,
|
|
25159
|
+
available: failure.available || [],
|
|
25160
|
+
attempts: failure.attempts || [],
|
|
25161
|
+
runtimeMode: this.runtimeMode,
|
|
25162
|
+
lastError: primaryError.message,
|
|
25163
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25164
|
+
};
|
|
25165
|
+
primaryError.modelSelection = diagnostic;
|
|
25166
|
+
if (job) job.modelSelection = diagnostic;
|
|
25167
|
+
throw primaryError;
|
|
25168
|
+
} finally {
|
|
25169
|
+
try {
|
|
25170
|
+
await this._closeModelPicker(c);
|
|
25171
|
+
} catch (cleanupError) {
|
|
25172
|
+
const message = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
|
|
25173
|
+
if (!primaryError) {
|
|
25174
|
+
const error2 = createModelSelectionError(message, "picker_cleanup_failed", true);
|
|
25175
|
+
const diagnostic = {
|
|
25176
|
+
configured: true,
|
|
25177
|
+
applied: false,
|
|
25178
|
+
requestedModel,
|
|
25179
|
+
requestedEffort,
|
|
25180
|
+
failureClass: "picker_cleanup_failed",
|
|
25181
|
+
retryable: true,
|
|
25182
|
+
errorCode: error2.code,
|
|
25183
|
+
available: [],
|
|
25184
|
+
attempts: [],
|
|
25185
|
+
runtimeMode: this.runtimeMode,
|
|
25186
|
+
lastError: message,
|
|
25187
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25188
|
+
};
|
|
25189
|
+
error2.modelSelection = diagnostic;
|
|
25190
|
+
if (job) job.modelSelection = diagnostic;
|
|
25191
|
+
throw error2;
|
|
25192
|
+
}
|
|
25193
|
+
if (primaryError.modelSelection) primaryError.modelSelection.cleanupError = message;
|
|
25194
|
+
if (job && job.modelSelection) job.modelSelection.cleanupError = message;
|
|
25023
25195
|
}
|
|
25024
|
-
effectiveEffort = requestedEffort;
|
|
25025
25196
|
}
|
|
25026
|
-
await this.
|
|
25027
|
-
trigger = await this._readModelPickerTrigger(c);
|
|
25197
|
+
const trigger = await this._readModelPickerTrigger(c);
|
|
25028
25198
|
const result = {
|
|
25029
25199
|
configured: true,
|
|
25030
25200
|
applied: true,
|
|
@@ -25203,6 +25373,38 @@ var CursorBridge = class {
|
|
|
25203
25373
|
}
|
|
25204
25374
|
}
|
|
25205
25375
|
}
|
|
25376
|
+
async _fillPrompt(c, text, job, timeoutMs = 2500) {
|
|
25377
|
+
const prepared = await evalJS(c, EXPR_PREPARE_INPUT);
|
|
25378
|
+
if (prepared !== "READY") return prepared;
|
|
25379
|
+
try {
|
|
25380
|
+
await c.send("Input.insertText", { text: String(text) });
|
|
25381
|
+
} catch {
|
|
25382
|
+
return "EXEC_FAIL";
|
|
25383
|
+
}
|
|
25384
|
+
const deadline = Date.now() + timeoutMs;
|
|
25385
|
+
let snapshot = {};
|
|
25386
|
+
do {
|
|
25387
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25388
|
+
try {
|
|
25389
|
+
snapshot = JSON.parse(await evalJS(c, EXPR_SNAP));
|
|
25390
|
+
} catch {
|
|
25391
|
+
}
|
|
25392
|
+
if (Number(snapshot.inputTextLength || 0) > 0 && snapshot.sendReady === true) {
|
|
25393
|
+
return String(text).slice(0, 30);
|
|
25394
|
+
}
|
|
25395
|
+
if (Date.now() >= deadline) break;
|
|
25396
|
+
await sleep2(Math.min(100, Math.max(0, deadline - Date.now())));
|
|
25397
|
+
} while (Date.now() <= deadline);
|
|
25398
|
+
const error2 = new Error("Cursor composer did not become send-ready after trusted input");
|
|
25399
|
+
error2.code = "CURSOR_COMPOSER_NOT_SEND_READY";
|
|
25400
|
+
error2.confirmedNotSent = true;
|
|
25401
|
+
error2.composerDiagnostic = {
|
|
25402
|
+
inputTextLength: Number(snapshot.inputTextLength || 0),
|
|
25403
|
+
sendReady: snapshot.sendReady === true,
|
|
25404
|
+
runtimeMode: this.runtimeMode
|
|
25405
|
+
};
|
|
25406
|
+
throw error2;
|
|
25407
|
+
}
|
|
25206
25408
|
async _confirmSubmission(c, baselineCount = 0, providerErrorBaseline = "") {
|
|
25207
25409
|
const accepted = async () => {
|
|
25208
25410
|
await this._throwIfNewProviderError(c, providerErrorBaseline);
|
|
@@ -25292,7 +25494,7 @@ var CursorBridge = class {
|
|
|
25292
25494
|
this._throwIfCancelledBeforeSend(job);
|
|
25293
25495
|
await this._applyModelPreference(c, job.modelPreference, job);
|
|
25294
25496
|
this._throwIfCancelledBeforeSend(job);
|
|
25295
|
-
const filled = await
|
|
25497
|
+
const filled = await this._fillPrompt(c, job.prompt, job);
|
|
25296
25498
|
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
25297
25499
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") throw new Error("Failed to enter the parallel_agent task");
|
|
25298
25500
|
await sleep2(350);
|
|
@@ -25412,7 +25614,7 @@ var CursorBridge = class {
|
|
|
25412
25614
|
this._throwIfCancelledBeforeSend(job);
|
|
25413
25615
|
job.responseBaseline = await this._captureSessionResponseBaseline(c);
|
|
25414
25616
|
const providerErrorBaseline = providerErrorSignature(await this._readProviderError(c));
|
|
25415
|
-
const filled = await
|
|
25617
|
+
const filled = await this._fillPrompt(c, job.prompt, job);
|
|
25416
25618
|
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
25417
25619
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") {
|
|
25418
25620
|
throw cursorSessionError("SESSION_INPUT_FAILED", "failed to enter the continued task");
|
|
@@ -26629,6 +26831,7 @@ export {
|
|
|
26629
26831
|
EXPR_MODEL_PICKER_ROWS,
|
|
26630
26832
|
EXPR_MODEL_PICKER_TRIGGER,
|
|
26631
26833
|
EXPR_PAGE_CAPABILITIES,
|
|
26834
|
+
EXPR_PREPARE_INPUT,
|
|
26632
26835
|
EXPR_PROVIDER_ERROR,
|
|
26633
26836
|
EXPR_VISIBLE,
|
|
26634
26837
|
EXPR_VISIBLE_COMPOSER,
|
|
@@ -26644,7 +26847,6 @@ export {
|
|
|
26644
26847
|
exprClickBoundComposerStop,
|
|
26645
26848
|
exprClickSelectedAgentStop,
|
|
26646
26849
|
exprCreateAgentForWorkspace,
|
|
26647
|
-
exprFill,
|
|
26648
26850
|
exprInspectWorkspaceRepository,
|
|
26649
26851
|
exprOpenAgent,
|
|
26650
26852
|
isConfirmedCompletedReply,
|
package/extensions/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ const hostWorkspaceId = hostCwd.replace(/\\/g, "/").toLowerCase();
|
|
|
10
10
|
export default createStdioMcpExtension({
|
|
11
11
|
label: "Cursor Bridge",
|
|
12
12
|
clientName: "pi-cursor-bridge",
|
|
13
|
-
packageVersion: "0.1.
|
|
13
|
+
packageVersion: "0.1.12",
|
|
14
14
|
serverName: "cursor-bridge",
|
|
15
15
|
serverScript: join(packageRoot, "dist", "cursor-bridge.mjs"),
|
|
16
16
|
cwd: hostCwd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cursor-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Use Cursor Context Engine and bounded, explicitly continuous Cursor Agent execution from the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,6 @@
|
|
|
47
47
|
},
|
|
48
48
|
"piPackage": {
|
|
49
49
|
"embeddedProduct": "Cursor Bridge",
|
|
50
|
-
"embeddedProductVersion": "5.8.
|
|
50
|
+
"embeddedProductVersion": "5.8.2"
|
|
51
51
|
}
|
|
52
52
|
}
|