@adhdev/daemon-core 0.5.42 → 0.5.44

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.
@@ -12,35 +12,15 @@
12
12
 
13
13
  // Find ProseMirror editor
14
14
  const editor = document.querySelector('.ProseMirror');
15
- if (!editor) return JSON.stringify({ error: 'ProseMirror editor not found' });
15
+ if (!editor) return JSON.stringify({ error: 'Editor not found' });
16
16
 
17
17
  // Focus the editor
18
18
  editor.focus();
19
19
 
20
- // Clear existing content
21
- const existingP = editor.querySelector('p');
22
- if (existingP) {
23
- existingP.textContent = message;
24
- // Dispatch input event for ProseMirror to detect the change
25
- editor.dispatchEvent(new InputEvent('input', {
26
- bubbles: true,
27
- cancelable: true,
28
- inputType: 'insertText',
29
- data: message,
30
- }));
31
- } else {
32
- // Fallback: create new paragraph
33
- const p = document.createElement('p');
34
- p.textContent = message;
35
- editor.innerHTML = '';
36
- editor.appendChild(p);
37
- editor.dispatchEvent(new InputEvent('input', {
38
- bubbles: true,
39
- cancelable: true,
40
- inputType: 'insertText',
41
- data: message,
42
- }));
43
- }
20
+ // Use execCommand to safely insert text. This avoids TrustedHTML errors
21
+ // and naturally triggers ProseMirror's state updates and Keyboard/Input events.
22
+ document.execCommand('selectAll', false, null);
23
+ document.execCommand('insertText', false, message);
44
24
 
45
25
  // Wait a tick then submit via Enter key
46
26
  setTimeout(() => {
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Codex Extension — set_mode
3
+ *
4
+ * Opens the mode dropdown (same discovery as list_modes), selects an item matching ${MODE}.
5
+ *
6
+ * Placeholder: ${MODE}
7
+ */
8
+ (() => {
9
+ try {
10
+ const targetMode = ${MODE};
11
+
12
+ function resolveDoc() {
13
+ let doc = document;
14
+ let root = doc.getElementById('root');
15
+ if (!root) {
16
+ const iframes = doc.querySelectorAll('iframe');
17
+ for (const iframe of iframes) {
18
+ try {
19
+ const innerDoc = iframe.contentDocument || iframe.contentWindow?.document;
20
+ if (innerDoc?.getElementById('root')) {
21
+ doc = innerDoc;
22
+ root = innerDoc.getElementById('root');
23
+ break;
24
+ }
25
+ } catch (e) { /* cross-origin */ }
26
+ }
27
+ }
28
+ return { doc, root };
29
+ }
30
+
31
+ function isModelText(text) {
32
+ return /^(GPT-|gpt-|o\d|claude-|sonnet|opus)/i.test(text);
33
+ }
34
+
35
+ /**
36
+ * Find the mode menu button using multi-root strategy (matching readChat).
37
+ */
38
+ function findModeButton(doc) {
39
+ for (const d of [doc, document]) {
40
+ const searchRoots = [
41
+ d.querySelector('[class*="thread-composer-max-width"]'),
42
+ d.querySelector('[class*="thread-composer"]'),
43
+ d.querySelector('[class*="pb-2"]'),
44
+ d.body,
45
+ ].filter(Boolean);
46
+
47
+ for (const searchRoot of searchRoots) {
48
+ const menuBtns = Array.from(searchRoot.querySelectorAll('button[aria-haspopup="menu"]'))
49
+ .filter(b => b.offsetWidth > 0);
50
+ for (const btn of menuBtns) {
51
+ const text = (btn.textContent || '').trim();
52
+ if (!isModelText(text) && text.length > 0 && text.length < 30) {
53
+ return btn;
54
+ }
55
+ }
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+
61
+ function openMenu(btn) {
62
+ const rect = btn.getBoundingClientRect();
63
+ const cx = rect.left + rect.width / 2;
64
+ const cy = rect.top + rect.height / 2;
65
+ btn.dispatchEvent(
66
+ new PointerEvent('pointerdown', { bubbles: true, clientX: cx, clientY: cy, pointerId: 1 }),
67
+ );
68
+ btn.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, clientX: cx, clientY: cy }));
69
+ btn.dispatchEvent(
70
+ new PointerEvent('pointerup', { bubbles: true, clientX: cx, clientY: cy, pointerId: 1 }),
71
+ );
72
+ btn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, clientX: cx, clientY: cy }));
73
+ btn.dispatchEvent(new MouseEvent('click', { bubbles: true, clientX: cx, clientY: cy }));
74
+ }
75
+
76
+ function clickItem(el) {
77
+ const ir = el.getBoundingClientRect();
78
+ const ix = ir.left + ir.width / 2;
79
+ const iy = ir.top + ir.height / 2;
80
+ el.dispatchEvent(
81
+ new PointerEvent('pointerdown', { bubbles: true, clientX: ix, clientY: iy }),
82
+ );
83
+ el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, clientX: ix, clientY: iy }));
84
+ el.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, clientX: ix, clientY: iy }));
85
+ el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, clientX: ix, clientY: iy }));
86
+ el.dispatchEvent(new MouseEvent('click', { bubbles: true, clientX: ix, clientY: iy }));
87
+ }
88
+
89
+ const { doc, root } = resolveDoc();
90
+ if (!root) return JSON.stringify({ success: false, error: 'no root' });
91
+
92
+ const want =
93
+ typeof targetMode === 'string'
94
+ ? targetMode.trim()
95
+ : targetMode != null
96
+ ? String(targetMode).trim()
97
+ : '';
98
+ if (!want) return JSON.stringify({ success: false, error: 'empty mode' });
99
+
100
+ const modeBtn = findModeButton(doc);
101
+ if (!modeBtn) return JSON.stringify({ success: false, error: 'mode menu button not found' });
102
+
103
+ const menuDoc = modeBtn.ownerDocument || doc;
104
+
105
+ const currentLabel = (modeBtn.textContent || '').trim();
106
+ if (currentLabel.toLowerCase() === want.toLowerCase()) {
107
+ return JSON.stringify({ success: true, mode: currentLabel, changed: false });
108
+ }
109
+
110
+ openMenu(modeBtn);
111
+
112
+ return new Promise((resolve) => {
113
+ setTimeout(() => {
114
+ let menu = menuDoc.querySelector('[role="menu"][data-state="open"]');
115
+ if (!menu) menu = menuDoc.querySelector('[role="menu"]');
116
+
117
+ if (!menu) {
118
+ menuDoc.dispatchEvent(
119
+ new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, bubbles: true }),
120
+ );
121
+ return resolve(JSON.stringify({ success: false, error: 'mode menu did not open' }));
122
+ }
123
+
124
+ const items = Array.from(
125
+ menu.querySelectorAll(
126
+ '[role="menuitem"], [role="menuitemradio"], [role="option"], div[class*="cursor-interaction"]',
127
+ ),
128
+ );
129
+
130
+ const norm = (s) => (s || '').trim().toLowerCase();
131
+ const wantN = norm(want);
132
+
133
+ let match = items.find((el) => norm(el.textContent) === wantN);
134
+ if (!match) {
135
+ match = items.find((el) => norm(el.textContent).includes(wantN) || wantN.includes(norm(el.textContent)));
136
+ }
137
+
138
+ if (!match) {
139
+ menuDoc.dispatchEvent(
140
+ new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, bubbles: true }),
141
+ );
142
+ const available = items
143
+ .map((el) => (el.textContent || '').trim())
144
+ .filter((t) => t.length > 0 && t.length < 80);
145
+ return resolve(
146
+ JSON.stringify({
147
+ success: false,
148
+ error: `mode "${want}" not found`,
149
+ available,
150
+ }),
151
+ );
152
+ }
153
+
154
+ const picked = (match.textContent || '').trim();
155
+ clickItem(match);
156
+
157
+ setTimeout(() => {
158
+ resolve(JSON.stringify({ success: true, mode: picked, changed: true }));
159
+ }, 350);
160
+ }, 550);
161
+ });
162
+ } catch (e) {
163
+ return JSON.stringify({ success: false, error: e.message || String(e) });
164
+ }
165
+ })();
@@ -164,16 +164,17 @@ export function readChatHistory(
164
164
  const dir = path.join(HISTORY_DIR, sanitized);
165
165
  if (!fs.existsSync(dir)) return { messages: [], hasMore: false };
166
166
 
167
- // JSONL file list — only matching prefix if instanceId is specified
167
+ // JSONL file list — filter by instanceId prefix if specified
168
168
  const sanitizedInstance = instanceId?.replace(/[^a-zA-Z0-9_-]/g, '_');
169
169
  const files = fs.readdirSync(dir)
170
170
  .filter(f => {
171
171
  if (!f.endsWith('.jsonl')) return false;
172
172
  if (sanitizedInstance) {
173
+ // With instanceId: only that instance's files
173
174
  return f.startsWith(`${sanitizedInstance}_`);
174
175
  }
175
- // Without instanceId, only files without prefix (legacy compatible)
176
- return !f.includes('_') || f.match(/^\d{4}-\d{2}-\d{2}\.jsonl$/);
176
+ // Without instanceId: include ALL files (legacy + instanced)
177
+ return true;
177
178
  })
178
179
  .sort()
179
180
  .reverse();