cursorconnect 0.1.6 → 0.1.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/bridge-runtime/.env.example +10 -2
- package/bridge-runtime/connector-version.json +1 -1
- package/bridge-runtime/dist/agent-completion-push.d.ts +42 -0
- package/bridge-runtime/dist/agent-completion-push.js +220 -0
- package/bridge-runtime/dist/agent-title-match.d.ts +8 -7
- package/bridge-runtime/dist/agent-title-match.js +11 -1
- package/bridge-runtime/dist/chat-display-store.d.ts +21 -9
- package/bridge-runtime/dist/chat-display-store.js +94 -23
- package/bridge-runtime/dist/chat-display.d.ts +2 -0
- package/bridge-runtime/dist/chat-display.js +197 -33
- package/bridge-runtime/dist/chat-history-mode.d.ts +5 -0
- package/bridge-runtime/dist/chat-history-mode.js +7 -0
- package/bridge-runtime/dist/command-executor.d.ts +2 -0
- package/bridge-runtime/dist/command-executor.js +44 -0
- package/bridge-runtime/dist/composer-title-index.d.ts +1 -0
- package/bridge-runtime/dist/composer-title-index.js +7 -7
- package/bridge-runtime/dist/debug-chats-page.d.ts +2 -0
- package/bridge-runtime/dist/debug-chats-page.js +491 -0
- package/bridge-runtime/dist/dom-transcript-store.d.ts +17 -0
- package/bridge-runtime/dist/dom-transcript-store.js +76 -0
- package/bridge-runtime/dist/extract-page.js +56 -85
- package/bridge-runtime/dist/history-limit.d.ts +2 -0
- package/bridge-runtime/dist/history-limit.js +2 -0
- package/bridge-runtime/dist/history-request.d.ts +8 -0
- package/bridge-runtime/dist/history-request.js +7 -0
- package/bridge-runtime/dist/index.js +1 -0
- package/bridge-runtime/dist/jsonl-index.d.ts +21 -3
- package/bridge-runtime/dist/jsonl-index.js +237 -73
- package/bridge-runtime/dist/jsonl-live-debug.d.ts +24 -0
- package/bridge-runtime/dist/jsonl-live-debug.js +175 -0
- package/bridge-runtime/dist/media-path.d.ts +2 -0
- package/bridge-runtime/dist/media-path.js +17 -0
- package/bridge-runtime/dist/message-filter.d.ts +2 -0
- package/bridge-runtime/dist/message-filter.js +21 -5
- package/bridge-runtime/dist/pairing-code.d.ts +2 -0
- package/bridge-runtime/dist/pairing-code.js +9 -2
- package/bridge-runtime/dist/relay-upstream.d.ts +2 -1
- package/bridge-runtime/dist/relay-upstream.js +4 -1
- package/bridge-runtime/dist/relay.d.ts +21 -0
- package/bridge-runtime/dist/relay.js +332 -28
- package/bridge-runtime/dist/types.d.ts +21 -0
- package/bridge-runtime/selectors.json +4 -5
- package/dist/index.js +79 -20
- package/dist/launch.js +23 -5
- package/dist/macos-autostart.js +87 -0
- package/dist/pairing-code.js +12 -3
- package/dist/print-pairing.js +2 -0
- package/dist/run-service.js +31 -0
- package/dist/startup-check.js +165 -0
- package/package.json +1 -1
- package/version-policy.json +1 -1
|
@@ -523,7 +523,7 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
523
523
|
const t = text.trim().replace(/\s+/g, ' ');
|
|
524
524
|
if (!t || t.length > 100)
|
|
525
525
|
return undefined;
|
|
526
|
-
if (/will resume when|background work finishes|background shell|resuming when|paused while|waiting for background/i.test(t)) {
|
|
526
|
+
if (/will resume when|background work finishes|background shell|resuming when|paused while|waiting for background|waiting for \d+ command/i.test(t)) {
|
|
527
527
|
return t;
|
|
528
528
|
}
|
|
529
529
|
if (/^working\.?$/i.test(t))
|
|
@@ -543,10 +543,27 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
543
543
|
return undefined;
|
|
544
544
|
return hit;
|
|
545
545
|
}
|
|
546
|
+
function isUserFacingAssistantProse(text) {
|
|
547
|
+
const t = text.trim();
|
|
548
|
+
if (!t)
|
|
549
|
+
return false;
|
|
550
|
+
if (/^##\s+\S/m.test(t))
|
|
551
|
+
return true;
|
|
552
|
+
if (/^\*\*[^*]{2,}\*\*/m.test(t) && t.length >= 60)
|
|
553
|
+
return true;
|
|
554
|
+
const tableRows = t.match(/^\|[^\n]+\|$/gm);
|
|
555
|
+
if (tableRows && tableRows.length >= 2)
|
|
556
|
+
return true;
|
|
557
|
+
if (/^[-*]\s+\S/m.test(t) && t.length >= 100)
|
|
558
|
+
return true;
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
546
561
|
function isAssistantReflectionText(text) {
|
|
547
562
|
const t = text.trim().replace(/\s+/g, ' ');
|
|
548
563
|
if (!t || isNoiseChatText(t))
|
|
549
564
|
return true;
|
|
565
|
+
if (isUserFacingAssistantProse(t))
|
|
566
|
+
return false;
|
|
550
567
|
if (/^Thought\s*for\s*\d/i.test(t) && t.length < 160)
|
|
551
568
|
return true;
|
|
552
569
|
if (/^(Exploring|Grepped|Searched|Listed|Read |Ran |Edited |Loading|Planning|Using image)/i.test(t)) {
|
|
@@ -555,10 +572,6 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
555
572
|
const toolHits = t.match(/\b(Explored|Grepped|Searched|Listed|Read )\b/gi);
|
|
556
573
|
if (toolHits && toolHits.length >= 2)
|
|
557
574
|
return true;
|
|
558
|
-
if (t.length > 160 &&
|
|
559
|
-
/\b(state\.messages|flat-index|mapKeyedChildren|humanEl|extract-page|userMessagesEquivalent)\b/i.test(t)) {
|
|
560
|
-
return true;
|
|
561
|
-
}
|
|
562
575
|
return false;
|
|
563
576
|
}
|
|
564
577
|
function isMeaningfulAssistantText(text) {
|
|
@@ -659,14 +672,17 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
659
672
|
return true;
|
|
660
673
|
return false;
|
|
661
674
|
}
|
|
662
|
-
let
|
|
675
|
+
let domSeqCounter = 0;
|
|
663
676
|
flatEls.forEach((el) => {
|
|
664
677
|
const idxRaw = el.getAttribute('data-flat-index') ?? '';
|
|
665
678
|
const idxParsed = Number.parseInt(idxRaw, 10);
|
|
666
|
-
const
|
|
679
|
+
const domSeq = domSeqCounter++;
|
|
680
|
+
// Sort/display: Cursor data-flat-index is the real timeline; domSeq only tie-breaks.
|
|
681
|
+
const attrFlatIndex = Number.isFinite(idxParsed) ? idxParsed : undefined;
|
|
682
|
+
const flatIndex = attrFlatIndex ?? domSeq;
|
|
667
683
|
const role = el.getAttribute('data-message-role') ?? '';
|
|
668
684
|
const kind = el.getAttribute('data-message-kind') ?? '';
|
|
669
|
-
const id = `${
|
|
685
|
+
const id = `${attrFlatIndex ?? flatIndex}-${role || 'x'}-${kind || 'x'}`;
|
|
670
686
|
if (isToolNoiseElement(el, kind)) {
|
|
671
687
|
recordDrop('tool', el);
|
|
672
688
|
return;
|
|
@@ -702,6 +718,7 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
702
718
|
text: displayText,
|
|
703
719
|
images: images.length ? images : undefined,
|
|
704
720
|
flatIndex,
|
|
721
|
+
domSeq,
|
|
705
722
|
});
|
|
706
723
|
messageDebug.extracted++;
|
|
707
724
|
return;
|
|
@@ -731,10 +748,10 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
731
748
|
text,
|
|
732
749
|
html: mdRoot.innerHTML || undefined,
|
|
733
750
|
flatIndex,
|
|
751
|
+
domSeq,
|
|
734
752
|
});
|
|
735
753
|
messageDebug.extracted++;
|
|
736
754
|
});
|
|
737
|
-
messages.sort((a, b) => (a.flatIndex ?? 0) - (b.flatIndex ?? 0));
|
|
738
755
|
function compareUserText(text) {
|
|
739
756
|
return cleanUserText(text).replace(/\s+/g, ' ').trim();
|
|
740
757
|
}
|
|
@@ -853,6 +870,30 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
853
870
|
}
|
|
854
871
|
}
|
|
855
872
|
const pendingApprovals = [];
|
|
873
|
+
function isVisibleButton(btn) {
|
|
874
|
+
const st = getComputedStyle(btn);
|
|
875
|
+
if (st.display === 'none' || st.visibility === 'hidden' || Number(st.opacity) < 0.05) {
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
878
|
+
const r = btn.getBoundingClientRect();
|
|
879
|
+
return r.width >= 2 && r.height >= 2;
|
|
880
|
+
}
|
|
881
|
+
/** Real tool Accept/Run — only explicit approval row (not chat status / "Run in background"). */
|
|
882
|
+
function isToolApprovalButton(btn) {
|
|
883
|
+
return !!btn.closest('.ui-shell-tool-call__approval-row');
|
|
884
|
+
}
|
|
885
|
+
function isDeniedApprovalLabel(label) {
|
|
886
|
+
const t = label.trim().toLowerCase();
|
|
887
|
+
if (!t)
|
|
888
|
+
return true;
|
|
889
|
+
if (t === 'run in background' || t.includes('run in background'))
|
|
890
|
+
return true;
|
|
891
|
+
if (/waiting for \d+ command/.test(t))
|
|
892
|
+
return true;
|
|
893
|
+
if (/will resume when|background work finishes|background shell/.test(t))
|
|
894
|
+
return true;
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
856
897
|
function isMenuTrigger(btn) {
|
|
857
898
|
const p = btn.getAttribute('aria-haspopup');
|
|
858
899
|
return p === 'menu' || p === 'true' || p === 'listbox';
|
|
@@ -922,7 +963,7 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
922
963
|
continue;
|
|
923
964
|
const actions = [];
|
|
924
965
|
const runBtn = row.querySelector('button.ui-shell-tool-call__run-btn');
|
|
925
|
-
if (runBtn && !isMenuTrigger(runBtn)) {
|
|
966
|
+
if (runBtn && isVisibleButton(runBtn) && !isMenuTrigger(runBtn)) {
|
|
926
967
|
actions.push({
|
|
927
968
|
label: cleanLabel(runBtn.textContent || '') || 'Run',
|
|
928
969
|
type: 'approve',
|
|
@@ -930,95 +971,25 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
|
|
|
930
971
|
});
|
|
931
972
|
}
|
|
932
973
|
const skipBtn = row.querySelector('button.ui-shell-tool-call__skip-btn');
|
|
933
|
-
if (skipBtn && !isMenuTrigger(skipBtn)) {
|
|
974
|
+
if (skipBtn && isVisibleButton(skipBtn) && !isMenuTrigger(skipBtn)) {
|
|
934
975
|
actions.push({
|
|
935
976
|
label: cleanLabel(skipBtn.textContent || '') || 'Skip',
|
|
936
977
|
type: 'reject',
|
|
937
978
|
selectorPath: buildSelectorPath(skipBtn),
|
|
938
979
|
});
|
|
939
980
|
}
|
|
940
|
-
|
|
981
|
+
const approveAction = actions.find((a) => a.type === 'approve' || a.type === 'approve_all');
|
|
982
|
+
if (!approveAction || isDeniedApprovalLabel(approveAction.label))
|
|
941
983
|
continue;
|
|
942
984
|
const cmd = card.querySelector('.ui-shell-tool-call__command')?.textContent?.trim();
|
|
985
|
+
if (cmd && isDeniedApprovalLabel(cmd))
|
|
986
|
+
continue;
|
|
943
987
|
pendingApprovals.push({
|
|
944
988
|
id: `tool:${buildSelectorPath(card)}`,
|
|
945
989
|
description: (cmd || 'Pending approval').slice(0, 240),
|
|
946
990
|
actions,
|
|
947
991
|
});
|
|
948
992
|
}
|
|
949
|
-
if (pendingApprovals.length === 0) {
|
|
950
|
-
const approveBtns = [];
|
|
951
|
-
const rejectBtns = [];
|
|
952
|
-
const seenA = new Set();
|
|
953
|
-
const seenR = new Set();
|
|
954
|
-
for (const sel of approveSelectors) {
|
|
955
|
-
try {
|
|
956
|
-
for (const btn of Array.from(container.querySelectorAll(sel))) {
|
|
957
|
-
if (seenA.has(btn) || isMenuTrigger(btn))
|
|
958
|
-
continue;
|
|
959
|
-
const label = btn.textContent?.trim() || btn.getAttribute('aria-label') || '';
|
|
960
|
-
if (label) {
|
|
961
|
-
seenA.add(btn);
|
|
962
|
-
approveBtns.push({ label, selector: buildSelectorPath(btn) });
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
catch {
|
|
967
|
-
/* skip */
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
if (approveBtns.length === 0) {
|
|
971
|
-
for (const btn of Array.from(container.querySelectorAll('button'))) {
|
|
972
|
-
if (seenA.has(btn) || isMenuTrigger(btn))
|
|
973
|
-
continue;
|
|
974
|
-
const text = `${btn.textContent || ''} ${btn.getAttribute('aria-label') || ''}`.toLowerCase();
|
|
975
|
-
for (const pat of approveTextMatch) {
|
|
976
|
-
if (text.includes(pat.toLowerCase())) {
|
|
977
|
-
seenA.add(btn);
|
|
978
|
-
approveBtns.push({
|
|
979
|
-
label: btn.textContent?.trim() || pat,
|
|
980
|
-
selector: buildSelectorPath(btn),
|
|
981
|
-
});
|
|
982
|
-
break;
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
for (const sel of rejectSelectors) {
|
|
988
|
-
try {
|
|
989
|
-
for (const btn of Array.from(container.querySelectorAll(sel))) {
|
|
990
|
-
if (seenR.has(btn) || isMenuTrigger(btn))
|
|
991
|
-
continue;
|
|
992
|
-
const label = btn.textContent?.trim() || btn.getAttribute('aria-label') || '';
|
|
993
|
-
if (label) {
|
|
994
|
-
seenR.add(btn);
|
|
995
|
-
rejectBtns.push({ label, selector: buildSelectorPath(btn) });
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
catch {
|
|
1000
|
-
/* skip */
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
if (approveBtns.length > 0 || rejectBtns.length > 0) {
|
|
1004
|
-
const actions = [];
|
|
1005
|
-
for (const b of approveBtns) {
|
|
1006
|
-
actions.push({
|
|
1007
|
-
label: b.label,
|
|
1008
|
-
type: b.label.toLowerCase().includes('all') ? 'approve_all' : 'approve',
|
|
1009
|
-
selectorPath: b.selector,
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
for (const b of rejectBtns) {
|
|
1013
|
-
actions.push({ label: b.label, type: 'reject', selectorPath: b.selector });
|
|
1014
|
-
}
|
|
1015
|
-
pendingApprovals.push({
|
|
1016
|
-
id: approveBtns.map((b) => b.label).join(','),
|
|
1017
|
-
description: approveBtns[0]?.label || 'Pending approval',
|
|
1018
|
-
actions,
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
993
|
let activeTab = tabs.find((t) => t.active);
|
|
1023
994
|
if (!activeTab) {
|
|
1024
995
|
const headerTitle = document.querySelector('span.auxiliary-bar-chat-title')?.textContent?.trim() ?? '';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface HistoryRequestOpts {
|
|
2
|
+
limit?: number;
|
|
3
|
+
offset?: number;
|
|
4
|
+
/** Ensure at least N tail rows (raises effective limit, capped at 500). */
|
|
5
|
+
minMessages?: number;
|
|
6
|
+
}
|
|
7
|
+
/** Resolve socket/HTTP `agents:history` tail size. */
|
|
8
|
+
export declare function resolveHistoryLimit(opts?: HistoryRequestOpts): number;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AGENT_HISTORY_DEFAULT_LIMIT } from './history-limit.js';
|
|
2
|
+
/** Resolve socket/HTTP `agents:history` tail size. */
|
|
3
|
+
export function resolveHistoryLimit(opts) {
|
|
4
|
+
const base = opts?.limit && opts.limit > 0 ? opts.limit : AGENT_HISTORY_DEFAULT_LIMIT;
|
|
5
|
+
const min = opts?.minMessages && opts.minMessages > 0 ? opts.minMessages : 0;
|
|
6
|
+
return Math.min(500, Math.max(base, min));
|
|
7
|
+
}
|
|
@@ -24,6 +24,7 @@ async function main() {
|
|
|
24
24
|
const commandExecutor = new CommandExecutor(selectors);
|
|
25
25
|
const cdpBridge = new CDPBridge(config);
|
|
26
26
|
const jsonlIndex = new JsonlIndex(config.cursorProjectsDir);
|
|
27
|
+
jsonlIndex.setBridgeStateProvider(() => stateManager.getState());
|
|
27
28
|
const messageDebugStore = new MessageDebugStore();
|
|
28
29
|
const extractor = new DOMExtractor(selectors, (state, err) => {
|
|
29
30
|
if (state?.messageDebug)
|
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import type { AgentsIndex, HistoryMessage } from './types.js';
|
|
2
|
+
import type { AgentsIndex, CursorState, HistoryMessage } from './types.js';
|
|
3
3
|
export declare class JsonlIndex extends EventEmitter {
|
|
4
4
|
private projectsDir;
|
|
5
5
|
private debounceTimer;
|
|
6
6
|
private subscribed;
|
|
7
7
|
private mtimeByAgent;
|
|
8
8
|
private pollTimer;
|
|
9
|
+
private readonly fileCache;
|
|
10
|
+
private readonly emitFileTimers;
|
|
11
|
+
private liveWatcher;
|
|
12
|
+
private liveWatchKey;
|
|
9
13
|
/** Skip poll `agent:history` while serving explicit `agents:history` (avoids relay race). */
|
|
10
14
|
readonly historyReplyInFlight: Set<string>;
|
|
15
|
+
private bridgeState;
|
|
11
16
|
constructor(projectsDir: string);
|
|
17
|
+
setBridgeStateProvider(fn: () => Pick<CursorState, 'composerIdByTitle' | 'activeComposerId' | 'tabs'>): void;
|
|
18
|
+
private resolveOpts;
|
|
12
19
|
start(): void;
|
|
13
20
|
stop(): void;
|
|
14
|
-
subscribe(agentId: string, title?: string
|
|
21
|
+
subscribe(agentId: string, title?: string, opts?: {
|
|
22
|
+
emitHistory?: boolean;
|
|
23
|
+
}): void;
|
|
15
24
|
unsubscribe(agentId: string): void;
|
|
25
|
+
/** Immediate `change` on subscribed `.jsonl` (no awaitWriteFinish delay). */
|
|
26
|
+
private refreshLiveWatcher;
|
|
16
27
|
getSubscribedAgents(): ReadonlyMap<string, {
|
|
17
28
|
title?: string;
|
|
18
29
|
}>;
|
|
19
30
|
private pollSubscribed;
|
|
20
31
|
private emitHistoryForAgent;
|
|
21
|
-
private
|
|
32
|
+
private scheduleEmitHistoryForFile;
|
|
33
|
+
private isLiveWatchedFile;
|
|
34
|
+
private loadMessagesForFile;
|
|
35
|
+
private emitHistoryForFileNow;
|
|
22
36
|
private scheduleRebuild;
|
|
23
37
|
rebuild(opts?: {
|
|
24
38
|
broadcast?: boolean;
|
|
@@ -27,7 +41,11 @@ export declare class JsonlIndex extends EventEmitter {
|
|
|
27
41
|
loadHistory(agentId: string, opts?: {
|
|
28
42
|
title?: string;
|
|
29
43
|
composerIdByTitle?: Record<string, string>;
|
|
44
|
+
activeComposerId?: string;
|
|
45
|
+
activeTabTitle?: string;
|
|
30
46
|
limit?: number;
|
|
47
|
+
/** Skip newest `offset` rows before taking `limit` tail (0 = newest). */
|
|
48
|
+
offset?: number;
|
|
31
49
|
}): Promise<{
|
|
32
50
|
agentId: string;
|
|
33
51
|
messages: HistoryMessage[];
|