openclaw-openagent 1.0.5 → 1.0.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/dist/index.js +10 -1
- package/dist/src/app/remote-agent-tool.js +3 -2
- package/dist/src/compat.d.ts +2 -0
- package/dist/src/compat.js +9 -0
- package/dist/src/plugin-ui/assets/openagent-override.js +212 -62
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.js +63 -26
- package/dist/src/proxy/auth-proxy.js +33 -1
- package/dist/src/util/url-resolver.d.ts +8 -0
- package/dist/src/util/url-resolver.js +40 -0
- package/index.ts +9 -1
- package/package.json +1 -1
- package/src/app/remote-agent-tool.ts +3 -2
- package/src/compat.ts +12 -0
- package/src/plugin-ui/assets/openagent-override.js +212 -62
- package/src/plugin-ui/build.cjs +1 -0
- package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +63 -31
- package/src/plugin-ui/modules/agent-book/panel/styles.js +49 -28
- package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +8 -2
- package/src/plugin-ui/ui-extension-loader/registry-regex.ts +70 -29
- package/src/proxy/auth-proxy.ts +34 -1
- package/src/util/url-resolver.ts +44 -0
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
//
|
|
10
10
|
// tryInject() 只做 DOM 注入(@ 按钮 + CSS class),不绑定任何事件。
|
|
11
11
|
|
|
12
|
-
// ── textarea
|
|
13
|
-
//
|
|
12
|
+
// ── textarea 选择器(多版本兼容)──
|
|
13
|
+
// v3 "Message..." / v4 "发消息..." / v5.x 结构化选择器
|
|
14
14
|
const _CL_TEXTAREA_SELECTOR = [
|
|
15
15
|
'textarea[placeholder*="发消息"]',
|
|
16
16
|
'textarea[placeholder*="Message"]',
|
|
@@ -19,6 +19,8 @@ const _CL_TEXTAREA_SELECTOR = [
|
|
|
19
19
|
'.chat-composer textarea',
|
|
20
20
|
'.chat-input textarea',
|
|
21
21
|
'[class*="composer"] textarea:not([placeholder*="Search"])',
|
|
22
|
+
'[class*="chat-input"] textarea',
|
|
23
|
+
'[data-component="chat-input"] textarea',
|
|
22
24
|
].join(', ');
|
|
23
25
|
|
|
24
26
|
function _getTextarea() {
|
|
@@ -134,37 +136,59 @@ function _installSendClickInterceptor() {
|
|
|
134
136
|
}, true);
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const textareaParent = textarea.parentElement;
|
|
146
|
-
let toolbarRow = null;
|
|
139
|
+
/**
|
|
140
|
+
* 多策略定位 textarea 所在输入区域的工具栏行。
|
|
141
|
+
* v4.x: 工具栏在 textarea.parentElement.children 中
|
|
142
|
+
* v5.x: 工具栏可能嵌套在祖父容器或包含 toolbar class
|
|
143
|
+
* 返回 null 表示未找到则回退到 textarea 父级插入
|
|
144
|
+
*/
|
|
145
|
+
function _findToolbarRow(textarea) {
|
|
146
|
+
var parent = textarea.parentElement;
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
148
|
+
// 策略 1(v4.x): textarea 兄弟元素含 SVG 图标
|
|
149
|
+
if (parent) {
|
|
150
|
+
for (var i = 0; i < parent.children.length; i++) {
|
|
151
|
+
if (parent.children[i] !== textarea && parent.children[i].querySelector('svg')) return parent.children[i];
|
|
154
152
|
}
|
|
155
153
|
}
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
|
|
155
|
+
// 策略 2(v4.x): 祖父级兄弟元素含 SVG 图标
|
|
156
|
+
if (parent) {
|
|
157
|
+
var grandparent = parent.parentElement;
|
|
158
158
|
if (grandparent) {
|
|
159
|
-
for (
|
|
160
|
-
if (
|
|
161
|
-
toolbarRow = child;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
159
|
+
for (var j = 0; j < grandparent.children.length; j++) {
|
|
160
|
+
if (grandparent.children[j] !== parent && grandparent.children[j].querySelector('svg')) return grandparent.children[j];
|
|
164
161
|
}
|
|
165
162
|
}
|
|
166
163
|
}
|
|
167
164
|
|
|
165
|
+
// 策略 3(v5.x): 搜索包含 toolbar class 的元素
|
|
166
|
+
var chatContainer = textarea.closest('[class*="chat"]');
|
|
167
|
+
if (chatContainer) {
|
|
168
|
+
var toolbarEl = chatContainer.querySelector('[class*="toolbar"]');
|
|
169
|
+
if (toolbarEl) return toolbarEl;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 策略 4(v5.x fallback): 搜索 voice/mic 按钮定位其容器
|
|
173
|
+
var voiceBtn = document.querySelector(
|
|
174
|
+
'button[aria-label*="voice"], button[aria-label*="语音"], ' +
|
|
175
|
+
'button[aria-label*="mic"], button[aria-label*="麦克风"]'
|
|
176
|
+
);
|
|
177
|
+
if (voiceBtn) return voiceBtn.parentElement;
|
|
178
|
+
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function tryInject() {
|
|
183
|
+
const textarea = _getTextarea();
|
|
184
|
+
|
|
185
|
+
if (!textarea) return false;
|
|
186
|
+
_installSendClickInterceptor();
|
|
187
|
+
if (document.querySelector('.openagent-at-btn')) return true; // 已注入
|
|
188
|
+
|
|
189
|
+
// ── 定位插入点:麦克风图标所在的 toolbar 行(多策略)──
|
|
190
|
+
var toolbarRow = _findToolbarRow(textarea);
|
|
191
|
+
|
|
168
192
|
// ── 创建 Agent Book 按钮 ──
|
|
169
193
|
const atBtn = document.createElement('button');
|
|
170
194
|
atBtn.className = 'openagent-at-btn';
|
|
@@ -198,10 +222,15 @@ function tryInject() {
|
|
|
198
222
|
if (toolbarRow) {
|
|
199
223
|
const toolbarLeft = toolbarRow.querySelector('[class*="toolbar-left"]') || toolbarRow;
|
|
200
224
|
const iconButtons = toolbarLeft.querySelectorAll('button, [role="button"]');
|
|
225
|
+
// 优先按 aria-label 匹配 voice/mic 按钮,回退到位置假设
|
|
201
226
|
let micBtn = null;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
227
|
+
for (var i = 0; i < iconButtons.length; i++) {
|
|
228
|
+
var label = (iconButtons[i].getAttribute('aria-label') || '').toLowerCase();
|
|
229
|
+
if (/voice|mic|语音|麦克风/.test(label)) { micBtn = iconButtons[i]; break; }
|
|
230
|
+
}
|
|
231
|
+
if (!micBtn && iconButtons.length >= 2) {
|
|
232
|
+
micBtn = iconButtons[1]; // fallback: v4.x 中 mic 一般是第 2 个按钮
|
|
233
|
+
} else if (!micBtn && iconButtons.length === 1) {
|
|
205
234
|
micBtn = iconButtons[0];
|
|
206
235
|
}
|
|
207
236
|
|
|
@@ -211,16 +240,19 @@ function tryInject() {
|
|
|
211
240
|
toolbarLeft.appendChild(atBtn);
|
|
212
241
|
}
|
|
213
242
|
} else {
|
|
214
|
-
|
|
243
|
+
// 无工具栏时回退到 textarea 的父级插入
|
|
244
|
+
var fallbackParent = textarea.parentElement;
|
|
245
|
+
if (fallbackParent) fallbackParent.appendChild(atBtn);
|
|
215
246
|
}
|
|
216
247
|
|
|
217
248
|
// ── 给输入区域加 position: relative 包裹层 ──
|
|
249
|
+
var inputParent = textarea.parentElement;
|
|
218
250
|
const inputContainer = textarea.closest('.agent-chat__composer-combobox')
|
|
219
251
|
|| textarea.closest('.chat-input')
|
|
220
252
|
|| textarea.closest('[class*="chat-composer"]')
|
|
221
253
|
|| textarea.closest('[class*="chat"]')
|
|
222
|
-
||
|
|
223
|
-
||
|
|
254
|
+
|| (inputParent ? inputParent.parentElement : null)
|
|
255
|
+
|| inputParent;
|
|
224
256
|
if (inputContainer) {
|
|
225
257
|
inputContainer.classList.add('openagent-input-wrapper');
|
|
226
258
|
inputWrapperRef = inputContainer;
|
|
@@ -111,10 +111,10 @@ function injectStyles() {
|
|
|
111
111
|
.openagent-agent-panel {
|
|
112
112
|
position: fixed;
|
|
113
113
|
max-height: none;
|
|
114
|
-
background: #fff;
|
|
115
|
-
border: 1.2px solid rgba(47, 47, 51, 0.12);
|
|
114
|
+
background: var(--oa-panel-bg, #fff);
|
|
115
|
+
border: 1.2px solid var(--oa-border, rgba(47, 47, 51, 0.12));
|
|
116
116
|
border-radius: 18px;
|
|
117
|
-
box-shadow: 0 -4px 24px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04);
|
|
117
|
+
box-shadow: 0 -4px 24px var(--oa-shadow-color, rgba(0,0,0,0.08)), 0 2px 8px var(--oa-shadow-color, rgba(0,0,0,0.04));
|
|
118
118
|
z-index: 99999;
|
|
119
119
|
overflow: hidden;
|
|
120
120
|
display: none;
|
|
@@ -158,7 +158,7 @@ function injectStyles() {
|
|
|
158
158
|
flex-shrink: 0;
|
|
159
159
|
}
|
|
160
160
|
.openagent-panel-close {
|
|
161
|
-
background: rgba(249, 249, 249, 1);
|
|
161
|
+
background: var(--oa-card-bg, rgba(249, 249, 249, 1));
|
|
162
162
|
border: none;
|
|
163
163
|
color: rgba(47, 47, 51, 0.6);
|
|
164
164
|
cursor: pointer;
|
|
@@ -618,41 +618,61 @@ function injectStyles() {
|
|
|
618
618
|
padding: 32px; text-align: center; color: #aaa; font-size: 13px;
|
|
619
619
|
}
|
|
620
620
|
|
|
621
|
-
/* ──
|
|
622
|
-
[data-theme-mode="dark"] .openagent-agent-panel
|
|
621
|
+
/* ── 深色主题(v4: data-theme-mode / v5: data-theme) ── */
|
|
622
|
+
[data-theme-mode="dark"] .openagent-agent-panel,
|
|
623
|
+
[data-theme="dark"] .openagent-agent-panel {
|
|
623
624
|
background: #1c1c1c; border-color: rgba(255,255,255,0.12);
|
|
624
625
|
box-shadow: 0 -4px 24px rgba(0,0,0,0.3);
|
|
625
626
|
}
|
|
626
|
-
[data-theme-mode="dark"] .openagent-panel-head
|
|
627
|
-
[data-theme
|
|
628
|
-
[data-theme-mode="dark"] .openagent-panel-
|
|
629
|
-
[data-theme
|
|
630
|
-
[data-theme-mode="dark"] .openagent-
|
|
631
|
-
[data-theme
|
|
632
|
-
[data-theme-mode="dark"] .openagent-panel-
|
|
633
|
-
|
|
634
|
-
[data-theme-mode="dark"] .
|
|
635
|
-
[data-theme
|
|
636
|
-
[data-theme-mode="dark"] .
|
|
637
|
-
|
|
638
|
-
[data-theme-mode="dark"] .
|
|
639
|
-
[data-theme
|
|
640
|
-
|
|
627
|
+
[data-theme-mode="dark"] .openagent-panel-head,
|
|
628
|
+
[data-theme="dark"] .openagent-panel-head { border-color: #2a2a2a; }
|
|
629
|
+
[data-theme-mode="dark"] .openagent-panel-title,
|
|
630
|
+
[data-theme="dark"] .openagent-panel-title { color: rgba(224, 224, 224, 0.8); }
|
|
631
|
+
[data-theme-mode="dark"] .openagent-panel-close,
|
|
632
|
+
[data-theme="dark"] .openagent-panel-close { background: #2a2a2a; color: #888; }
|
|
633
|
+
[data-theme-mode="dark"] .openagent-panel-close:hover,
|
|
634
|
+
[data-theme="dark"] .openagent-panel-close:hover { background: #333; color: #ccc; }
|
|
635
|
+
[data-theme-mode="dark"] .openagent-section-heading,
|
|
636
|
+
[data-theme="dark"] .openagent-section-heading { color: rgba(224, 224, 224, 0.45); }
|
|
637
|
+
[data-theme-mode="dark"] .openagent-parent-heading,
|
|
638
|
+
[data-theme="dark"] .openagent-parent-heading { color: rgba(224, 224, 224, 0.3); }
|
|
639
|
+
[data-theme-mode="dark"] .openagent-panel-divider,
|
|
640
|
+
[data-theme="dark"] .openagent-panel-divider { border-color: rgba(255,255,255,0.1); }
|
|
641
|
+
|
|
642
|
+
[data-theme-mode="dark"] .agent-card__name,
|
|
643
|
+
[data-theme="dark"] .agent-card__name { color: #e0e0e0; }
|
|
644
|
+
[data-theme-mode="dark"] .agent-card__bio,
|
|
645
|
+
[data-theme="dark"] .agent-card__bio { color: rgba(224, 224, 224, 0.7); }
|
|
646
|
+
[data-theme-mode="dark"] .agent-card__stat,
|
|
647
|
+
[data-theme="dark"] .agent-card__stat { color: #64748b; }
|
|
648
|
+
|
|
649
|
+
[data-theme-mode="dark"] .agent-card--mention,
|
|
650
|
+
[data-theme="dark"] .agent-card--mention { background: transparent; }
|
|
651
|
+
[data-theme-mode="dark"] .agent-card--mention:hover,
|
|
652
|
+
[data-theme="dark"] .agent-card--mention:hover { background: #252525; }
|
|
653
|
+
[data-theme-mode="dark"] .agent-card--mention .agent-card__action,
|
|
654
|
+
[data-theme="dark"] .agent-card--mention .agent-card__action {
|
|
641
655
|
background: #333; border-color: #555; color: #ddd;
|
|
642
656
|
}
|
|
643
|
-
[data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover
|
|
644
|
-
[data-theme
|
|
645
|
-
[data-theme-mode="dark"] .agent-card--search
|
|
657
|
+
[data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover,
|
|
658
|
+
[data-theme="dark"] .agent-card--mention .agent-card__action:hover { background: #444; }
|
|
659
|
+
[data-theme-mode="dark"] .agent-card--search:hover,
|
|
660
|
+
[data-theme="dark"] .agent-card--search:hover { background: #252525; }
|
|
661
|
+
[data-theme-mode="dark"] .agent-card--search.openagent-card-selected,
|
|
662
|
+
[data-theme="dark"] .agent-card--search.openagent-card-selected {
|
|
646
663
|
background: rgba(99, 102, 241, 0.12);
|
|
647
664
|
outline-color: rgba(99, 102, 241, 0.4);
|
|
648
665
|
}
|
|
649
|
-
[data-theme-mode="dark"] .agent-card--search .agent-card__action
|
|
666
|
+
[data-theme-mode="dark"] .agent-card--search .agent-card__action,
|
|
667
|
+
[data-theme="dark"] .agent-card--search .agent-card__action {
|
|
650
668
|
background: #333; border-color: #555; color: #ddd;
|
|
651
669
|
}
|
|
652
|
-
[data-theme-mode="dark"] .agent-card--search .agent-card__action:hover
|
|
670
|
+
[data-theme-mode="dark"] .agent-card--search .agent-card__action:hover,
|
|
671
|
+
[data-theme="dark"] .agent-card--search .agent-card__action:hover { background: #444; }
|
|
653
672
|
|
|
654
673
|
|
|
655
|
-
[data-theme-mode="dark"] .agent-card--inline
|
|
674
|
+
[data-theme-mode="dark"] .agent-card--inline,
|
|
675
|
+
[data-theme="dark"] .agent-card--inline {
|
|
656
676
|
background: linear-gradient(135deg, #1e1b3a 0%, #1a1a2e 100%);
|
|
657
677
|
border-color: #2d2b55;
|
|
658
678
|
}
|
|
@@ -662,7 +682,8 @@ function injectStyles() {
|
|
|
662
682
|
}
|
|
663
683
|
[data-theme-mode="dark"] .agent-card--inline .agent-card__name { color: #e2e8f0; }
|
|
664
684
|
[data-theme-mode="dark"] .agent-card--inline .agent-card__avatar { border-color: #2d2b55; }
|
|
665
|
-
[data-theme-mode="dark"] .agent-card__loading
|
|
685
|
+
[data-theme-mode="dark"] .agent-card__loading,
|
|
686
|
+
[data-theme="dark"] .agent-card__loading {
|
|
666
687
|
background: #1e1b3a; border-color: #2d2b55; color: #64748b;
|
|
667
688
|
}
|
|
668
689
|
`;
|
|
@@ -12,14 +12,20 @@
|
|
|
12
12
|
var SHELL_SELECTORS = [
|
|
13
13
|
'.chat-tool-card:not(:has(.cl-remote-agent-card))',
|
|
14
14
|
'.chat-tool-msg-collapse:not(:has(.cl-remote-agent-card))',
|
|
15
|
-
'.chat-tools-collapse:not(:has(.cl-remote-agent-card))'
|
|
15
|
+
'.chat-tools-collapse:not(:has(.cl-remote-agent-card))',
|
|
16
|
+
// v5.x+ variants
|
|
17
|
+
'.tool-card:not(:has(.cl-remote-agent-card))',
|
|
18
|
+
'.chat-tool-card__wrapper:not(:has(.cl-remote-agent-card))'
|
|
16
19
|
];
|
|
17
20
|
|
|
18
21
|
var SUMMARY_SELECTORS = [
|
|
19
22
|
'.chat-tool-card__header',
|
|
20
23
|
'.chat-tool-card__title',
|
|
21
24
|
'.chat-tool-msg-summary:not(.cl-remote-agent-host-shell *)',
|
|
22
|
-
'.chat-tools-summary:not(.cl-remote-agent-host-shell *)'
|
|
25
|
+
'.chat-tools-summary:not(.cl-remote-agent-host-shell *)',
|
|
26
|
+
// v5.x+ variants
|
|
27
|
+
'.tool-card__header',
|
|
28
|
+
'.tool-card__title'
|
|
23
29
|
];
|
|
24
30
|
|
|
25
31
|
var INDENT_SELECTORS = [
|
|
@@ -258,27 +258,25 @@ const toolcardRender: UIExtension = {
|
|
|
258
258
|
if (hasManualCollapseWrapper) {
|
|
259
259
|
const wrapperFn = findFunctionByAnchor(content, wrapperAnchor);
|
|
260
260
|
if (!wrapperFn) {
|
|
261
|
-
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found');
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
261
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found, skipping wrapper patch');
|
|
262
|
+
} else if (!wrapperFn.body.includes('chat-tool-msg-body') || !wrapperFn.body.includes('onToggleExpanded')) {
|
|
263
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper shape mismatch, skipping wrapper patch');
|
|
264
|
+
} else {
|
|
265
|
+
const wrapperReturnTagMatch = wrapperFn.body.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
|
|
266
|
+
if (!wrapperReturnTagMatch) {
|
|
267
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse Lit html tag not found, skipping wrapper patch');
|
|
268
|
+
} else {
|
|
269
|
+
const wrapperHtmlVar = wrapperReturnTagMatch[1];
|
|
270
|
+
const wrapperParams = wrapperFn.params.split(',').map((s: string) => s.trim());
|
|
271
|
+
const wrapperCardParam = wrapperParams[0] || 'e';
|
|
272
|
+
const wrapperOptionsParam = wrapperParams[1] || '';
|
|
273
|
+
const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
|
|
274
|
+
const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
|
|
275
|
+
content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
|
|
276
|
+
patchedWrapper = true;
|
|
277
|
+
logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
|
|
278
|
+
}
|
|
272
279
|
}
|
|
273
|
-
const wrapperHtmlVar = wrapperReturnTagMatch[1];
|
|
274
|
-
const wrapperParams = wrapperFn.params.split(',').map((s: string) => s.trim());
|
|
275
|
-
const wrapperCardParam = wrapperParams[0] || 'e';
|
|
276
|
-
const wrapperOptionsParam = wrapperParams[1] || '';
|
|
277
|
-
const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
|
|
278
|
-
const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
|
|
279
|
-
content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
|
|
280
|
-
patchedWrapper = true;
|
|
281
|
-
logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
|
|
282
280
|
}
|
|
283
281
|
|
|
284
282
|
// 4.11+ also renders tool-result messages through the grouped message
|
|
@@ -291,13 +289,13 @@ const toolcardRender: UIExtension = {
|
|
|
291
289
|
let toolMessageOk = true;
|
|
292
290
|
const toolMessageFn = findFunctionByAnchor(content, toolMessageAnchor, 8000);
|
|
293
291
|
if (!toolMessageFn) {
|
|
294
|
-
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found');
|
|
292
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found, skipping');
|
|
295
293
|
toolMessageOk = false;
|
|
296
294
|
}
|
|
297
295
|
|
|
298
296
|
if (toolMessageOk && (!toolMessageFn!.body.includes('chat-tool-msg-collapse--manual')
|
|
299
297
|
|| !toolMessageFn!.body.includes('chat-tool-msg-body'))) {
|
|
300
|
-
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch');
|
|
298
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch, skipping');
|
|
301
299
|
toolMessageOk = false;
|
|
302
300
|
}
|
|
303
301
|
|
|
@@ -650,11 +648,41 @@ const remoteAgentMessageShortCircuit: UIExtension = {
|
|
|
650
648
|
return true;
|
|
651
649
|
}
|
|
652
650
|
|
|
651
|
+
// Strategy D (v5.17+ fallback): search for any if-condition referencing
|
|
652
|
+
// hasToolCards alongside a role-like variable — v5.17+ may replace the
|
|
653
|
+
// ternary/compound patterns of A/B/C with a different expression shape.
|
|
654
|
+
const strategyDRe = new RegExp(
|
|
655
|
+
`if\\(!` + `([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*&&\\s*` +
|
|
656
|
+
hasToolCardsVar + `\\s*&&\\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\)`
|
|
657
|
+
);
|
|
658
|
+
const strategyDMatch = content.match(strategyDRe);
|
|
659
|
+
if (strategyDMatch) {
|
|
660
|
+
const markdownVarD = strategyDMatch[1]!;
|
|
661
|
+
const isToolResultVarD = strategyDMatch[2]!;
|
|
662
|
+
const dMatchFull = strategyDMatch[0];
|
|
663
|
+
const dIdx = strategyDMatch.index!;
|
|
664
|
+
const patched = `if(` + marker +
|
|
665
|
+
`(!` + markdownVarD + `||window.__openagentOwnsToolCards&&window.__openagentOwnsToolCards(` +
|
|
666
|
+
toolCardsVar + `))&&` + hasToolCardsVar + `&&` + isToolResultVarD + `)`;
|
|
667
|
+
content = content.substring(0, dIdx) + patched + content.substring(dIdx + dMatchFull.length);
|
|
668
|
+
writeFileSync(bundlePath, content, 'utf-8');
|
|
669
|
+
logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy D, md=${markdownVarD})`);
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
|
|
653
673
|
// 策略B (v4.11~v4.1x fallback): compound = hasToolCards && (showToolCalls ?? !0)
|
|
654
674
|
const compoundRe = new RegExp(`(\\w+)=${hasToolCardsVar}&&\\(\\w+\\.showToolCalls\\?\\?!0\\)`);
|
|
655
675
|
const compoundMatch = content.match(compoundRe);
|
|
656
676
|
if (!compoundMatch) {
|
|
657
|
-
|
|
677
|
+
// All strategies (A/C/D/B) failed — dump context for diagnostics
|
|
678
|
+
const anchorHintIdx = content.indexOf(hasToolCardsVar);
|
|
679
|
+
const ctxStart = anchorHintIdx > 200 ? anchorHintIdx - 200 : 0;
|
|
680
|
+
const ctxEnd = Math.min(anchorHintIdx + 200, content.length);
|
|
681
|
+
logger.warn(
|
|
682
|
+
`[ui-ext:regex] remote-agent-msg: all strategies (A/C/D/B) failed for toolCards=${toolCardsVar}. ` +
|
|
683
|
+
`Context around hasToolCardsVar (at ${anchorHintIdx}): ` +
|
|
684
|
+
content.substring(ctxStart, ctxEnd)
|
|
685
|
+
);
|
|
658
686
|
return false;
|
|
659
687
|
}
|
|
660
688
|
|
|
@@ -709,17 +737,18 @@ const mentionHooks: UIExtension = {
|
|
|
709
737
|
const hookA = `${markerA}if(window.__clMention?.onKeyDown(${eventParam}))return;`;
|
|
710
738
|
content = content.substring(0, keydownInsertIdx) + hookA + content.substring(keydownInsertIdx);
|
|
711
739
|
|
|
712
|
-
// 注入点 B: handleInput —
|
|
740
|
+
// 注入点 B: handleInput — 四种形态兼容
|
|
713
741
|
// Shape A (old): .onDraftChange(target.value)}}
|
|
714
742
|
// Shape B (4.11~4.x): .onDraftChange(target.value) (arrow function 内)
|
|
715
|
-
// Shape C (2026.6.x): be=t=>{let n=t.target;pL(e,n.value)} — 引入 pL helper
|
|
716
|
-
//
|
|
743
|
+
// Shape C (2026.6.x): be=t=>{let n=t.target;pL(e,n.value)} — 引入 pL helper
|
|
744
|
+
// Shape D (generic): .onDraftChange(任意.target.value) 不加结尾限制
|
|
717
745
|
const inputReA = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)\}\}/;
|
|
718
746
|
const inputReB = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)/;
|
|
719
747
|
const inputReC = /let ([a-zA-Z_$][a-zA-Z0-9_$]*)=[a-zA-Z_$][a-zA-Z0-9_$]*\.target;[a-zA-Z_$][a-zA-Z0-9_$]*\([a-zA-Z_$][a-zA-Z0-9_$]*,\1\.value\)\}/;
|
|
748
|
+
const inputReD = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.target\.value\)/;
|
|
720
749
|
|
|
721
750
|
let inputMatch = content.match(inputReA);
|
|
722
|
-
let inputShape: 'A' | 'B' | 'C' = 'A';
|
|
751
|
+
let inputShape: 'A' | 'B' | 'C' | 'D' = 'A';
|
|
723
752
|
|
|
724
753
|
if (!inputMatch) {
|
|
725
754
|
inputMatch = content.match(inputReC);
|
|
@@ -729,14 +758,26 @@ const mentionHooks: UIExtension = {
|
|
|
729
758
|
inputMatch = content.match(inputReB);
|
|
730
759
|
inputShape = 'B';
|
|
731
760
|
}
|
|
761
|
+
if (!inputMatch) {
|
|
762
|
+
inputMatch = content.match(inputReD);
|
|
763
|
+
inputShape = 'D';
|
|
764
|
+
}
|
|
732
765
|
|
|
733
766
|
if (!inputMatch) {
|
|
734
|
-
|
|
767
|
+
// All shapes missed — dump context for diagnostics
|
|
768
|
+
const draftHintIdx = content.indexOf('.onDraftChange');
|
|
769
|
+
const ctxStart = draftHintIdx > 200 ? draftHintIdx - 200 : 0;
|
|
770
|
+
const ctxEnd = Math.min(draftHintIdx + 200, content.length);
|
|
771
|
+
logger.warn(
|
|
772
|
+
`[ui-ext:regex] mention-hooks: input anchor not found (Shape A/B/C/D all missed). ` +
|
|
773
|
+
`Context around onDraftChange (at ${draftHintIdx}): ` +
|
|
774
|
+
content.substring(ctxStart, ctxEnd)
|
|
775
|
+
);
|
|
735
776
|
return false;
|
|
736
777
|
}
|
|
737
778
|
const targetVar = inputMatch[1];
|
|
738
779
|
const inputAnchorIdx = content.indexOf(inputMatch[0]);
|
|
739
|
-
// Shape A: }} 之前;Shape C: 末尾 } 之前;Shape B: ) 之后
|
|
780
|
+
// Shape A: }} 之前;Shape C: 末尾 } 之前;Shape B/D: ) 之后
|
|
740
781
|
const draftCallEnd = inputShape === 'A'
|
|
741
782
|
? inputAnchorIdx + inputMatch[0].length - 2
|
|
742
783
|
: inputShape === 'C'
|
package/src/proxy/auth-proxy.ts
CHANGED
|
@@ -26,6 +26,15 @@ const AUTH_UPSTREAM_TIM = 'https://auth.ai-talk.live';
|
|
|
26
26
|
const PROXY_TIMEOUT_MS = 15_000;
|
|
27
27
|
const MAX_BODY_BYTES = 64 * 1024; // 64KB — tagline API body is ~100 bytes
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* 获取当前 OASN access API base URL,用于解析响应中的相对路径。
|
|
31
|
+
*/
|
|
32
|
+
function getOasnAccessBase(): string {
|
|
33
|
+
const rt = registry.getDefault();
|
|
34
|
+
if (rt?.transportType !== 'oasn') return '';
|
|
35
|
+
return (rt.config?.accessApiBase || rt.config?.apiBase || '').replace(/\/+$/, '');
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
/**
|
|
30
39
|
* 根据当前 active runtime 的 transport 类型决定上游。
|
|
31
40
|
*
|
|
@@ -143,7 +152,31 @@ export function createAuthProxyHandler() {
|
|
|
143
152
|
const contentType = upstream.headers.get('content-type');
|
|
144
153
|
if (contentType) res.setHeader('Content-Type', contentType);
|
|
145
154
|
|
|
146
|
-
|
|
155
|
+
let buf = Buffer.from(await upstream.arrayBuffer());
|
|
156
|
+
|
|
157
|
+
// Resolve OASN-relative URLs in GET invocation responses so the
|
|
158
|
+
// frontend receives absolute URLs (launch_url, content_url, etc.).
|
|
159
|
+
if (method === 'GET' && /^\/api\/invocations\/[^/]+$/.test(targetPath) && buf.length > 0) {
|
|
160
|
+
try {
|
|
161
|
+
const isJson = contentType?.includes('json');
|
|
162
|
+
if (isJson) {
|
|
163
|
+
const oasnBase = getOasnAccessBase();
|
|
164
|
+
if (oasnBase) {
|
|
165
|
+
const text = buf.toString('utf-8');
|
|
166
|
+
const resolved = text.replace(
|
|
167
|
+
/"(launch_url|content_url)":"(\/[^"]+)"/g,
|
|
168
|
+
(_m: string, key: string, path: string) => {
|
|
169
|
+
try {
|
|
170
|
+
return `"${key}":"${new URL(path, oasnBase).toString()}"`;
|
|
171
|
+
} catch { return _m; }
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
buf = Buffer.from(resolved, 'utf-8');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch { /* best-effort; don't break proxy on parse errors */ }
|
|
178
|
+
}
|
|
179
|
+
|
|
147
180
|
res.end(buf);
|
|
148
181
|
|
|
149
182
|
logger.info(`[proxy] ${method} ${url.pathname} → ${upstream.status} (${buf.length} bytes)`);
|
package/src/util/url-resolver.ts
CHANGED
|
@@ -15,3 +15,47 @@ export function resolveOasnAccessUrl(accessApiBase: string | undefined, url: str
|
|
|
15
15
|
return url;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a WebUI continuation URL for user-facing browser clicks.
|
|
21
|
+
*
|
|
22
|
+
* WebUI auth is Owner-cookie based, so the clickable URL must use the same
|
|
23
|
+
* public origin as the claim/login flow. API-key calls can use an internal
|
|
24
|
+
* Access base, but browser links cannot reuse cookies across IP/domain origins.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveOasnWebuiUrl(
|
|
27
|
+
accessApiBase: string | undefined,
|
|
28
|
+
claimUrl: string | undefined,
|
|
29
|
+
url: string,
|
|
30
|
+
): string {
|
|
31
|
+
if (!url) return url;
|
|
32
|
+
|
|
33
|
+
const claimOrigin = originOf(claimUrl);
|
|
34
|
+
if (!claimOrigin || !isWebuiUrl(url, accessApiBase || claimOrigin)) {
|
|
35
|
+
return resolveOasnAccessUrl(accessApiBase, url);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const absolute = new URL(url, accessApiBase || claimOrigin);
|
|
40
|
+
return new URL(`${absolute.pathname}${absolute.search}${absolute.hash}`, claimOrigin).toString();
|
|
41
|
+
} catch {
|
|
42
|
+
return resolveOasnAccessUrl(accessApiBase, url);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function originOf(value: string | undefined): string | undefined {
|
|
47
|
+
if (!value) return undefined;
|
|
48
|
+
try {
|
|
49
|
+
return new URL(value).origin;
|
|
50
|
+
} catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isWebuiUrl(url: string, base: string): boolean {
|
|
56
|
+
try {
|
|
57
|
+
return new URL(url, base).pathname.startsWith('/webui/');
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|