openclaw-openagent 1.0.6 → 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/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/util/url-resolver.d.ts +8 -0
- package/dist/src/util/url-resolver.js +40 -0
- 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/util/url-resolver.ts +44 -0
|
@@ -21,7 +21,7 @@ import { toolResult, } from './types.js';
|
|
|
21
21
|
import { sendC2CCustomMessage, waitForC2CReply } from '../tim/c2c.js';
|
|
22
22
|
import fs from 'node:fs';
|
|
23
23
|
import nodePath from 'node:path';
|
|
24
|
-
import { resolveOasnAccessUrl } from '../util/url-resolver.js';
|
|
24
|
+
import { resolveOasnAccessUrl, resolveOasnWebuiUrl } from '../util/url-resolver.js';
|
|
25
25
|
// ── Helpers ──
|
|
26
26
|
function fail(error) {
|
|
27
27
|
return toolResult(JSON.stringify({ success: false, error }));
|
|
@@ -291,7 +291,8 @@ export function createCallRemoteAgentTool() {
|
|
|
291
291
|
const accessBase = rt.config?.accessApiBase || rt.config?.apiBase;
|
|
292
292
|
if (result.webuiContinuation?.launchUrl) {
|
|
293
293
|
const rawLink = result.webuiContinuation.launchUrl;
|
|
294
|
-
const
|
|
294
|
+
const claimUrl = rt.config?.claimUrl;
|
|
295
|
+
const link = resolveOasnWebuiUrl(accessBase, claimUrl, rawLink);
|
|
295
296
|
const label = result.webuiContinuation.displayName || 'Open WebUI';
|
|
296
297
|
content = content.trim()
|
|
297
298
|
? `${content}\n\n${label}: ${link}`
|
package/dist/src/compat.d.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export declare const PLUGIN_VERSION = "1.0.0";
|
|
12
12
|
export declare const SUPPORTED_HOST_MIN = "2026.3.22";
|
|
13
|
+
/** Plugin has been verified up to this OpenClaw host version */
|
|
14
|
+
export declare const MAX_TESTED_VERSION = "2026.6.30";
|
|
13
15
|
export interface OpenClawVersion {
|
|
14
16
|
year: number;
|
|
15
17
|
month: number;
|
package/dist/src/compat.js
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
import { logger } from './util/logger.js';
|
|
12
12
|
export const PLUGIN_VERSION = '1.0.0';
|
|
13
13
|
export const SUPPORTED_HOST_MIN = '2026.3.22';
|
|
14
|
+
/** Plugin has been verified up to this OpenClaw host version */
|
|
15
|
+
export const MAX_TESTED_VERSION = '2026.6.30';
|
|
14
16
|
/**
|
|
15
17
|
* Parse an OpenClaw date version string (e.g. "2026.3.22") into components.
|
|
16
18
|
* Returns null for unparseable strings.
|
|
@@ -61,6 +63,13 @@ export function assertHostCompatibility(hostVersion) {
|
|
|
61
63
|
}
|
|
62
64
|
if (isHostVersionSupported(hostVersion)) {
|
|
63
65
|
logger.info(`[compat] Host OpenClaw ${hostVersion} >= ${SUPPORTED_HOST_MIN}, OK.`);
|
|
66
|
+
// Warn but don't block for versions beyond tested range
|
|
67
|
+
const host = parseOpenClawVersion(hostVersion);
|
|
68
|
+
const max = parseOpenClawVersion(MAX_TESTED_VERSION);
|
|
69
|
+
if (host && max && compareVersions(host, max) > 0) {
|
|
70
|
+
logger.warn(`[compat] Host OpenClaw ${hostVersion} is beyond max tested version ${MAX_TESTED_VERSION}. ` +
|
|
71
|
+
`The plugin may encounter compatibility issues.`);
|
|
72
|
+
}
|
|
64
73
|
return;
|
|
65
74
|
}
|
|
66
75
|
throw new Error(`openclaw-openagent@${PLUGIN_VERSION} requires OpenClaw >=${SUPPORTED_HOST_MIN}, ` +
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OpenAgent UI Override — Auto-generated, do not edit directly!
|
|
3
3
|
*
|
|
4
4
|
* Source: src/plugin-ui/modules/
|
|
5
|
-
* Built: 2026-06-
|
|
5
|
+
* Built: 2026-06-25T09:33:52.490Z
|
|
6
6
|
*
|
|
7
7
|
* To modify, edit the source modules and run:
|
|
8
8
|
* npm run build:override
|
|
@@ -1587,6 +1587,97 @@ function AgentCard(agent, opts = {}) {
|
|
|
1587
1587
|
return card;
|
|
1588
1588
|
}
|
|
1589
1589
|
|
|
1590
|
+
// ════════════════════════════════════════════════════════════════
|
|
1591
|
+
// MODULE: agent-book/panel/theme-adapter.js
|
|
1592
|
+
// ════════════════════════════════════════════════════════════════
|
|
1593
|
+
|
|
1594
|
+
// ── OpenAgent Theme Adapter ──
|
|
1595
|
+
//
|
|
1596
|
+
// 运行时检测 OpenClaw Control UI 主题系统(v4.x vscode tokens / v5.x glassmorphism),
|
|
1597
|
+
// 注入 --oa-* CSS 变量供 styles.js 中的 var() fallback 消费。
|
|
1598
|
+
//
|
|
1599
|
+
// 必须早于 styles.js 加载(由 build.cjs MANIFEST 顺序保证)。
|
|
1600
|
+
|
|
1601
|
+
(function _clInitThemeAdapter() {
|
|
1602
|
+
if (window.__openagentThemeAdapterInstalled) return;
|
|
1603
|
+
window.__openagentThemeAdapterInstalled = true;
|
|
1604
|
+
|
|
1605
|
+
/**
|
|
1606
|
+
* 检测当前 OC 主题系统类型。
|
|
1607
|
+
* @returns {{ hasGlass: boolean, hasVscode: boolean, isDark: boolean }}
|
|
1608
|
+
*/
|
|
1609
|
+
function detectThemeSystem() {
|
|
1610
|
+
var cs = getComputedStyle(document.documentElement);
|
|
1611
|
+
var hasGlass = !!(cs.getPropertyValue('--glass-surface').trim());
|
|
1612
|
+
var hasKn = !!(cs.getPropertyValue('--kn-bg-primary').trim());
|
|
1613
|
+
var hasVscode = !!(cs.getPropertyValue('--vscode-editor-background').trim());
|
|
1614
|
+
|
|
1615
|
+
// 多策略暗色检测:v4 data-theme-mode / v5 data-theme / prefers-color-scheme
|
|
1616
|
+
var themeMode = document.documentElement.getAttribute('data-theme-mode') // v4.x
|
|
1617
|
+
|| document.documentElement.getAttribute('data-theme') // v5.x
|
|
1618
|
+
|| '';
|
|
1619
|
+
var isDark = themeMode.indexOf('dark') >= 0
|
|
1620
|
+
|| window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
1621
|
+
|
|
1622
|
+
return {
|
|
1623
|
+
hasGlass: hasGlass || hasKn,
|
|
1624
|
+
hasVscode: hasVscode,
|
|
1625
|
+
isDark: isDark
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
/**
|
|
1630
|
+
* 注入 --oa-* CSS 变量到 :root。
|
|
1631
|
+
* styles.js 中的 var(--oa-xxx, fallback) 会优先取这些值。
|
|
1632
|
+
*/
|
|
1633
|
+
function setupThemeVariables() {
|
|
1634
|
+
var system = detectThemeSystem();
|
|
1635
|
+
var root = document.documentElement;
|
|
1636
|
+
|
|
1637
|
+
if (system.hasGlass) {
|
|
1638
|
+
// v5.x Glassmorphism: 使用 --glass-* / --kn-* token 族
|
|
1639
|
+
root.style.setProperty('--oa-panel-bg', 'var(--glass-surface, var(--card))');
|
|
1640
|
+
root.style.setProperty('--oa-panel-text', 'var(--kn-text-primary, var(--text, #e0e0e0))');
|
|
1641
|
+
root.style.setProperty('--oa-card-bg', 'var(--glass-card, var(--card, #f5f5f5))');
|
|
1642
|
+
root.style.setProperty('--oa-card-hover-bg', 'var(--glass-card-hover, var(--card-hover))');
|
|
1643
|
+
root.style.setProperty('--oa-border', 'var(--glass-border, var(--border, #e5e5e5))');
|
|
1644
|
+
root.style.setProperty('--oa-shadow-color', 'rgba(0, 0, 0, 0.08)');
|
|
1645
|
+
root.style.setProperty('--oa-divider', 'var(--glass-divider, var(--border, #eee))');
|
|
1646
|
+
root.style.setProperty('--oa-muted-text', 'var(--kn-text-secondary, var(--text-secondary, #999))');
|
|
1647
|
+
} else if (system.hasVscode) {
|
|
1648
|
+
// v4.x VSCode tokens: 使用 --vscode-* 变量
|
|
1649
|
+
root.style.setProperty('--oa-panel-bg', 'var(--vscode-editor-background, #fff)');
|
|
1650
|
+
root.style.setProperty('--oa-panel-text', 'var(--vscode-editor-foreground, #2F2F33)');
|
|
1651
|
+
root.style.setProperty('--oa-card-bg', 'var(--vscode-sideBar-background, #f5f5f5)');
|
|
1652
|
+
root.style.setProperty('--oa-card-hover-bg', 'var(--vscode-list-hoverBackground, #f0f0f0)');
|
|
1653
|
+
root.style.setProperty('--oa-border', 'var(--vscode-panel-border, #e5e5e5)');
|
|
1654
|
+
root.style.setProperty('--oa-shadow-color', 'rgba(0, 0, 0, 0.10)');
|
|
1655
|
+
root.style.setProperty('--oa-divider', 'var(--vscode-sideBar-border, #eee)');
|
|
1656
|
+
root.style.setProperty('--oa-muted-text', 'var(--vscode-descriptionForeground, #999)');
|
|
1657
|
+
}
|
|
1658
|
+
// 无特征 → 不注入变量,styles.js 中的 var() 会 fallback 到硬编码值
|
|
1659
|
+
|
|
1660
|
+
// 注入暗色 class 标记供 styles.js 选择器使用
|
|
1661
|
+
if (system.isDark) {
|
|
1662
|
+
root.style.setProperty('--oa-is-dark', '1');
|
|
1663
|
+
} else {
|
|
1664
|
+
root.style.setProperty('--oa-is-dark', '0');
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// 立即执行 + 监听主题变更(v5.x 可能运行时切换主题)
|
|
1669
|
+
setupThemeVariables();
|
|
1670
|
+
|
|
1671
|
+
// 监听 data-theme / data-theme-mode 属性变更
|
|
1672
|
+
var themeObserver = new MutationObserver(function () {
|
|
1673
|
+
setupThemeVariables();
|
|
1674
|
+
});
|
|
1675
|
+
themeObserver.observe(document.documentElement, {
|
|
1676
|
+
attributes: true,
|
|
1677
|
+
attributeFilter: ['data-theme', 'data-theme-mode', 'class']
|
|
1678
|
+
});
|
|
1679
|
+
})();
|
|
1680
|
+
|
|
1590
1681
|
// ════════════════════════════════════════════════════════════════
|
|
1591
1682
|
// MODULE: agent-book/panel/styles.js
|
|
1592
1683
|
// ════════════════════════════════════════════════════════════════
|
|
@@ -1704,10 +1795,10 @@ function injectStyles() {
|
|
|
1704
1795
|
.openagent-agent-panel {
|
|
1705
1796
|
position: fixed;
|
|
1706
1797
|
max-height: none;
|
|
1707
|
-
background: #fff;
|
|
1708
|
-
border: 1.2px solid rgba(47, 47, 51, 0.12);
|
|
1798
|
+
background: var(--oa-panel-bg, #fff);
|
|
1799
|
+
border: 1.2px solid var(--oa-border, rgba(47, 47, 51, 0.12));
|
|
1709
1800
|
border-radius: 18px;
|
|
1710
|
-
box-shadow: 0 -4px 24px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04);
|
|
1801
|
+
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));
|
|
1711
1802
|
z-index: 99999;
|
|
1712
1803
|
overflow: hidden;
|
|
1713
1804
|
display: none;
|
|
@@ -1751,7 +1842,7 @@ function injectStyles() {
|
|
|
1751
1842
|
flex-shrink: 0;
|
|
1752
1843
|
}
|
|
1753
1844
|
.openagent-panel-close {
|
|
1754
|
-
background: rgba(249, 249, 249, 1);
|
|
1845
|
+
background: var(--oa-card-bg, rgba(249, 249, 249, 1));
|
|
1755
1846
|
border: none;
|
|
1756
1847
|
color: rgba(47, 47, 51, 0.6);
|
|
1757
1848
|
cursor: pointer;
|
|
@@ -2211,41 +2302,61 @@ function injectStyles() {
|
|
|
2211
2302
|
padding: 32px; text-align: center; color: #aaa; font-size: 13px;
|
|
2212
2303
|
}
|
|
2213
2304
|
|
|
2214
|
-
/* ──
|
|
2215
|
-
[data-theme-mode="dark"] .openagent-agent-panel
|
|
2305
|
+
/* ── 深色主题(v4: data-theme-mode / v5: data-theme) ── */
|
|
2306
|
+
[data-theme-mode="dark"] .openagent-agent-panel,
|
|
2307
|
+
[data-theme="dark"] .openagent-agent-panel {
|
|
2216
2308
|
background: #1c1c1c; border-color: rgba(255,255,255,0.12);
|
|
2217
2309
|
box-shadow: 0 -4px 24px rgba(0,0,0,0.3);
|
|
2218
2310
|
}
|
|
2219
|
-
[data-theme-mode="dark"] .openagent-panel-head
|
|
2220
|
-
[data-theme
|
|
2221
|
-
[data-theme-mode="dark"] .openagent-panel-
|
|
2222
|
-
[data-theme
|
|
2223
|
-
[data-theme-mode="dark"] .openagent-
|
|
2224
|
-
[data-theme
|
|
2225
|
-
[data-theme-mode="dark"] .openagent-panel-
|
|
2226
|
-
|
|
2227
|
-
[data-theme-mode="dark"] .
|
|
2228
|
-
[data-theme
|
|
2229
|
-
[data-theme-mode="dark"] .
|
|
2230
|
-
|
|
2231
|
-
[data-theme-mode="dark"] .
|
|
2232
|
-
[data-theme
|
|
2233
|
-
|
|
2311
|
+
[data-theme-mode="dark"] .openagent-panel-head,
|
|
2312
|
+
[data-theme="dark"] .openagent-panel-head { border-color: #2a2a2a; }
|
|
2313
|
+
[data-theme-mode="dark"] .openagent-panel-title,
|
|
2314
|
+
[data-theme="dark"] .openagent-panel-title { color: rgba(224, 224, 224, 0.8); }
|
|
2315
|
+
[data-theme-mode="dark"] .openagent-panel-close,
|
|
2316
|
+
[data-theme="dark"] .openagent-panel-close { background: #2a2a2a; color: #888; }
|
|
2317
|
+
[data-theme-mode="dark"] .openagent-panel-close:hover,
|
|
2318
|
+
[data-theme="dark"] .openagent-panel-close:hover { background: #333; color: #ccc; }
|
|
2319
|
+
[data-theme-mode="dark"] .openagent-section-heading,
|
|
2320
|
+
[data-theme="dark"] .openagent-section-heading { color: rgba(224, 224, 224, 0.45); }
|
|
2321
|
+
[data-theme-mode="dark"] .openagent-parent-heading,
|
|
2322
|
+
[data-theme="dark"] .openagent-parent-heading { color: rgba(224, 224, 224, 0.3); }
|
|
2323
|
+
[data-theme-mode="dark"] .openagent-panel-divider,
|
|
2324
|
+
[data-theme="dark"] .openagent-panel-divider { border-color: rgba(255,255,255,0.1); }
|
|
2325
|
+
|
|
2326
|
+
[data-theme-mode="dark"] .agent-card__name,
|
|
2327
|
+
[data-theme="dark"] .agent-card__name { color: #e0e0e0; }
|
|
2328
|
+
[data-theme-mode="dark"] .agent-card__bio,
|
|
2329
|
+
[data-theme="dark"] .agent-card__bio { color: rgba(224, 224, 224, 0.7); }
|
|
2330
|
+
[data-theme-mode="dark"] .agent-card__stat,
|
|
2331
|
+
[data-theme="dark"] .agent-card__stat { color: #64748b; }
|
|
2332
|
+
|
|
2333
|
+
[data-theme-mode="dark"] .agent-card--mention,
|
|
2334
|
+
[data-theme="dark"] .agent-card--mention { background: transparent; }
|
|
2335
|
+
[data-theme-mode="dark"] .agent-card--mention:hover,
|
|
2336
|
+
[data-theme="dark"] .agent-card--mention:hover { background: #252525; }
|
|
2337
|
+
[data-theme-mode="dark"] .agent-card--mention .agent-card__action,
|
|
2338
|
+
[data-theme="dark"] .agent-card--mention .agent-card__action {
|
|
2234
2339
|
background: #333; border-color: #555; color: #ddd;
|
|
2235
2340
|
}
|
|
2236
|
-
[data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover
|
|
2237
|
-
[data-theme
|
|
2238
|
-
[data-theme-mode="dark"] .agent-card--search
|
|
2341
|
+
[data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover,
|
|
2342
|
+
[data-theme="dark"] .agent-card--mention .agent-card__action:hover { background: #444; }
|
|
2343
|
+
[data-theme-mode="dark"] .agent-card--search:hover,
|
|
2344
|
+
[data-theme="dark"] .agent-card--search:hover { background: #252525; }
|
|
2345
|
+
[data-theme-mode="dark"] .agent-card--search.openagent-card-selected,
|
|
2346
|
+
[data-theme="dark"] .agent-card--search.openagent-card-selected {
|
|
2239
2347
|
background: rgba(99, 102, 241, 0.12);
|
|
2240
2348
|
outline-color: rgba(99, 102, 241, 0.4);
|
|
2241
2349
|
}
|
|
2242
|
-
[data-theme-mode="dark"] .agent-card--search .agent-card__action
|
|
2350
|
+
[data-theme-mode="dark"] .agent-card--search .agent-card__action,
|
|
2351
|
+
[data-theme="dark"] .agent-card--search .agent-card__action {
|
|
2243
2352
|
background: #333; border-color: #555; color: #ddd;
|
|
2244
2353
|
}
|
|
2245
|
-
[data-theme-mode="dark"] .agent-card--search .agent-card__action:hover
|
|
2354
|
+
[data-theme-mode="dark"] .agent-card--search .agent-card__action:hover,
|
|
2355
|
+
[data-theme="dark"] .agent-card--search .agent-card__action:hover { background: #444; }
|
|
2246
2356
|
|
|
2247
2357
|
|
|
2248
|
-
[data-theme-mode="dark"] .agent-card--inline
|
|
2358
|
+
[data-theme-mode="dark"] .agent-card--inline,
|
|
2359
|
+
[data-theme="dark"] .agent-card--inline {
|
|
2249
2360
|
background: linear-gradient(135deg, #1e1b3a 0%, #1a1a2e 100%);
|
|
2250
2361
|
border-color: #2d2b55;
|
|
2251
2362
|
}
|
|
@@ -2255,7 +2366,8 @@ function injectStyles() {
|
|
|
2255
2366
|
}
|
|
2256
2367
|
[data-theme-mode="dark"] .agent-card--inline .agent-card__name { color: #e2e8f0; }
|
|
2257
2368
|
[data-theme-mode="dark"] .agent-card--inline .agent-card__avatar { border-color: #2d2b55; }
|
|
2258
|
-
[data-theme-mode="dark"] .agent-card__loading
|
|
2369
|
+
[data-theme-mode="dark"] .agent-card__loading,
|
|
2370
|
+
[data-theme="dark"] .agent-card__loading {
|
|
2259
2371
|
background: #1e1b3a; border-color: #2d2b55; color: #64748b;
|
|
2260
2372
|
}
|
|
2261
2373
|
`;
|
|
@@ -2890,8 +3002,8 @@ liveTextarea.dispatchEvent(new Event('change', { bubbles: true }));
|
|
|
2890
3002
|
//
|
|
2891
3003
|
// tryInject() 只做 DOM 注入(@ 按钮 + CSS class),不绑定任何事件。
|
|
2892
3004
|
|
|
2893
|
-
// ── textarea
|
|
2894
|
-
//
|
|
3005
|
+
// ── textarea 选择器(多版本兼容)──
|
|
3006
|
+
// v3 "Message..." / v4 "发消息..." / v5.x 结构化选择器
|
|
2895
3007
|
const _CL_TEXTAREA_SELECTOR = [
|
|
2896
3008
|
'textarea[placeholder*="发消息"]',
|
|
2897
3009
|
'textarea[placeholder*="Message"]',
|
|
@@ -2900,6 +3012,8 @@ const _CL_TEXTAREA_SELECTOR = [
|
|
|
2900
3012
|
'.chat-composer textarea',
|
|
2901
3013
|
'.chat-input textarea',
|
|
2902
3014
|
'[class*="composer"] textarea:not([placeholder*="Search"])',
|
|
3015
|
+
'[class*="chat-input"] textarea',
|
|
3016
|
+
'[data-component="chat-input"] textarea',
|
|
2903
3017
|
].join(', ');
|
|
2904
3018
|
|
|
2905
3019
|
function _getTextarea() {
|
|
@@ -3015,37 +3129,59 @@ function _installSendClickInterceptor() {
|
|
|
3015
3129
|
}, true);
|
|
3016
3130
|
}
|
|
3017
3131
|
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
const textareaParent = textarea.parentElement;
|
|
3027
|
-
let toolbarRow = null;
|
|
3132
|
+
/**
|
|
3133
|
+
* 多策略定位 textarea 所在输入区域的工具栏行。
|
|
3134
|
+
* v4.x: 工具栏在 textarea.parentElement.children 中
|
|
3135
|
+
* v5.x: 工具栏可能嵌套在祖父容器或包含 toolbar class
|
|
3136
|
+
* 返回 null 表示未找到则回退到 textarea 父级插入
|
|
3137
|
+
*/
|
|
3138
|
+
function _findToolbarRow(textarea) {
|
|
3139
|
+
var parent = textarea.parentElement;
|
|
3028
3140
|
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
break;
|
|
3034
|
-
}
|
|
3141
|
+
// 策略 1(v4.x): textarea 兄弟元素含 SVG 图标
|
|
3142
|
+
if (parent) {
|
|
3143
|
+
for (var i = 0; i < parent.children.length; i++) {
|
|
3144
|
+
if (parent.children[i] !== textarea && parent.children[i].querySelector('svg')) return parent.children[i];
|
|
3035
3145
|
}
|
|
3036
3146
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3147
|
+
|
|
3148
|
+
// 策略 2(v4.x): 祖父级兄弟元素含 SVG 图标
|
|
3149
|
+
if (parent) {
|
|
3150
|
+
var grandparent = parent.parentElement;
|
|
3039
3151
|
if (grandparent) {
|
|
3040
|
-
for (
|
|
3041
|
-
if (
|
|
3042
|
-
toolbarRow = child;
|
|
3043
|
-
break;
|
|
3044
|
-
}
|
|
3152
|
+
for (var j = 0; j < grandparent.children.length; j++) {
|
|
3153
|
+
if (grandparent.children[j] !== parent && grandparent.children[j].querySelector('svg')) return grandparent.children[j];
|
|
3045
3154
|
}
|
|
3046
3155
|
}
|
|
3047
3156
|
}
|
|
3048
3157
|
|
|
3158
|
+
// 策略 3(v5.x): 搜索包含 toolbar class 的元素
|
|
3159
|
+
var chatContainer = textarea.closest('[class*="chat"]');
|
|
3160
|
+
if (chatContainer) {
|
|
3161
|
+
var toolbarEl = chatContainer.querySelector('[class*="toolbar"]');
|
|
3162
|
+
if (toolbarEl) return toolbarEl;
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
// 策略 4(v5.x fallback): 搜索 voice/mic 按钮定位其容器
|
|
3166
|
+
var voiceBtn = document.querySelector(
|
|
3167
|
+
'button[aria-label*="voice"], button[aria-label*="语音"], ' +
|
|
3168
|
+
'button[aria-label*="mic"], button[aria-label*="麦克风"]'
|
|
3169
|
+
);
|
|
3170
|
+
if (voiceBtn) return voiceBtn.parentElement;
|
|
3171
|
+
|
|
3172
|
+
return null;
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
function tryInject() {
|
|
3176
|
+
const textarea = _getTextarea();
|
|
3177
|
+
|
|
3178
|
+
if (!textarea) return false;
|
|
3179
|
+
_installSendClickInterceptor();
|
|
3180
|
+
if (document.querySelector('.openagent-at-btn')) return true; // 已注入
|
|
3181
|
+
|
|
3182
|
+
// ── 定位插入点:麦克风图标所在的 toolbar 行(多策略)──
|
|
3183
|
+
var toolbarRow = _findToolbarRow(textarea);
|
|
3184
|
+
|
|
3049
3185
|
// ── 创建 Agent Book 按钮 ──
|
|
3050
3186
|
const atBtn = document.createElement('button');
|
|
3051
3187
|
atBtn.className = 'openagent-at-btn';
|
|
@@ -3079,10 +3215,15 @@ function tryInject() {
|
|
|
3079
3215
|
if (toolbarRow) {
|
|
3080
3216
|
const toolbarLeft = toolbarRow.querySelector('[class*="toolbar-left"]') || toolbarRow;
|
|
3081
3217
|
const iconButtons = toolbarLeft.querySelectorAll('button, [role="button"]');
|
|
3218
|
+
// 优先按 aria-label 匹配 voice/mic 按钮,回退到位置假设
|
|
3082
3219
|
let micBtn = null;
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3220
|
+
for (var i = 0; i < iconButtons.length; i++) {
|
|
3221
|
+
var label = (iconButtons[i].getAttribute('aria-label') || '').toLowerCase();
|
|
3222
|
+
if (/voice|mic|语音|麦克风/.test(label)) { micBtn = iconButtons[i]; break; }
|
|
3223
|
+
}
|
|
3224
|
+
if (!micBtn && iconButtons.length >= 2) {
|
|
3225
|
+
micBtn = iconButtons[1]; // fallback: v4.x 中 mic 一般是第 2 个按钮
|
|
3226
|
+
} else if (!micBtn && iconButtons.length === 1) {
|
|
3086
3227
|
micBtn = iconButtons[0];
|
|
3087
3228
|
}
|
|
3088
3229
|
|
|
@@ -3092,16 +3233,19 @@ function tryInject() {
|
|
|
3092
3233
|
toolbarLeft.appendChild(atBtn);
|
|
3093
3234
|
}
|
|
3094
3235
|
} else {
|
|
3095
|
-
|
|
3236
|
+
// 无工具栏时回退到 textarea 的父级插入
|
|
3237
|
+
var fallbackParent = textarea.parentElement;
|
|
3238
|
+
if (fallbackParent) fallbackParent.appendChild(atBtn);
|
|
3096
3239
|
}
|
|
3097
3240
|
|
|
3098
3241
|
// ── 给输入区域加 position: relative 包裹层 ──
|
|
3242
|
+
var inputParent = textarea.parentElement;
|
|
3099
3243
|
const inputContainer = textarea.closest('.agent-chat__composer-combobox')
|
|
3100
3244
|
|| textarea.closest('.chat-input')
|
|
3101
3245
|
|| textarea.closest('[class*="chat-composer"]')
|
|
3102
3246
|
|| textarea.closest('[class*="chat"]')
|
|
3103
|
-
||
|
|
3104
|
-
||
|
|
3247
|
+
|| (inputParent ? inputParent.parentElement : null)
|
|
3248
|
+
|| inputParent;
|
|
3105
3249
|
if (inputContainer) {
|
|
3106
3250
|
inputContainer.classList.add('openagent-input-wrapper');
|
|
3107
3251
|
inputWrapperRef = inputContainer;
|
|
@@ -5418,14 +5562,20 @@ function _clScan() {
|
|
|
5418
5562
|
var SHELL_SELECTORS = [
|
|
5419
5563
|
'.chat-tool-card:not(:has(.cl-remote-agent-card))',
|
|
5420
5564
|
'.chat-tool-msg-collapse:not(:has(.cl-remote-agent-card))',
|
|
5421
|
-
'.chat-tools-collapse:not(:has(.cl-remote-agent-card))'
|
|
5565
|
+
'.chat-tools-collapse:not(:has(.cl-remote-agent-card))',
|
|
5566
|
+
// v5.x+ variants
|
|
5567
|
+
'.tool-card:not(:has(.cl-remote-agent-card))',
|
|
5568
|
+
'.chat-tool-card__wrapper:not(:has(.cl-remote-agent-card))'
|
|
5422
5569
|
];
|
|
5423
5570
|
|
|
5424
5571
|
var SUMMARY_SELECTORS = [
|
|
5425
5572
|
'.chat-tool-card__header',
|
|
5426
5573
|
'.chat-tool-card__title',
|
|
5427
5574
|
'.chat-tool-msg-summary:not(.cl-remote-agent-host-shell *)',
|
|
5428
|
-
'.chat-tools-summary:not(.cl-remote-agent-host-shell *)'
|
|
5575
|
+
'.chat-tools-summary:not(.cl-remote-agent-host-shell *)',
|
|
5576
|
+
// v5.x+ variants
|
|
5577
|
+
'.tool-card__header',
|
|
5578
|
+
'.tool-card__title'
|
|
5429
5579
|
];
|
|
5430
5580
|
|
|
5431
5581
|
var INDENT_SELECTORS = [
|
|
@@ -235,27 +235,28 @@ const toolcardRender = {
|
|
|
235
235
|
if (hasManualCollapseWrapper) {
|
|
236
236
|
const wrapperFn = findFunctionByAnchor(content, wrapperAnchor);
|
|
237
237
|
if (!wrapperFn) {
|
|
238
|
-
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found');
|
|
239
|
-
return false;
|
|
238
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found, skipping wrapper patch');
|
|
240
239
|
}
|
|
241
|
-
if (!wrapperFn.body.includes('chat-tool-msg-body') || !wrapperFn.body.includes('onToggleExpanded')) {
|
|
242
|
-
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper shape mismatch');
|
|
243
|
-
return false;
|
|
240
|
+
else if (!wrapperFn.body.includes('chat-tool-msg-body') || !wrapperFn.body.includes('onToggleExpanded')) {
|
|
241
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper shape mismatch, skipping wrapper patch');
|
|
244
242
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
243
|
+
else {
|
|
244
|
+
const wrapperReturnTagMatch = wrapperFn.body.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
|
|
245
|
+
if (!wrapperReturnTagMatch) {
|
|
246
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse Lit html tag not found, skipping wrapper patch');
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const wrapperHtmlVar = wrapperReturnTagMatch[1];
|
|
250
|
+
const wrapperParams = wrapperFn.params.split(',').map((s) => s.trim());
|
|
251
|
+
const wrapperCardParam = wrapperParams[0] || 'e';
|
|
252
|
+
const wrapperOptionsParam = wrapperParams[1] || '';
|
|
253
|
+
const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
|
|
254
|
+
const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
|
|
255
|
+
content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
|
|
256
|
+
patchedWrapper = true;
|
|
257
|
+
logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
|
|
258
|
+
}
|
|
249
259
|
}
|
|
250
|
-
const wrapperHtmlVar = wrapperReturnTagMatch[1];
|
|
251
|
-
const wrapperParams = wrapperFn.params.split(',').map((s) => s.trim());
|
|
252
|
-
const wrapperCardParam = wrapperParams[0] || 'e';
|
|
253
|
-
const wrapperOptionsParam = wrapperParams[1] || '';
|
|
254
|
-
const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
|
|
255
|
-
const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
|
|
256
|
-
content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
|
|
257
|
-
patchedWrapper = true;
|
|
258
|
-
logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
|
|
259
260
|
}
|
|
260
261
|
// 4.11+ also renders tool-result messages through the grouped message
|
|
261
262
|
// renderer itself instead of nb(). That path only calls the inner ToolCard
|
|
@@ -267,12 +268,12 @@ const toolcardRender = {
|
|
|
267
268
|
let toolMessageOk = true;
|
|
268
269
|
const toolMessageFn = findFunctionByAnchor(content, toolMessageAnchor, 8000);
|
|
269
270
|
if (!toolMessageFn) {
|
|
270
|
-
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found');
|
|
271
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found, skipping');
|
|
271
272
|
toolMessageOk = false;
|
|
272
273
|
}
|
|
273
274
|
if (toolMessageOk && (!toolMessageFn.body.includes('chat-tool-msg-collapse--manual')
|
|
274
275
|
|| !toolMessageFn.body.includes('chat-tool-msg-body'))) {
|
|
275
|
-
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch');
|
|
276
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch, skipping');
|
|
276
277
|
toolMessageOk = false;
|
|
277
278
|
}
|
|
278
279
|
let toolMessageHtmlVar = '';
|
|
@@ -613,11 +614,36 @@ const remoteAgentMessageShortCircuit = {
|
|
|
613
614
|
logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy C, sc=${scVar})`);
|
|
614
615
|
return true;
|
|
615
616
|
}
|
|
617
|
+
// Strategy D (v5.17+ fallback): search for any if-condition referencing
|
|
618
|
+
// hasToolCards alongside a role-like variable — v5.17+ may replace the
|
|
619
|
+
// ternary/compound patterns of A/B/C with a different expression shape.
|
|
620
|
+
const strategyDRe = new RegExp(`if\\(!` + `([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*&&\\s*` +
|
|
621
|
+
hasToolCardsVar + `\\s*&&\\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\)`);
|
|
622
|
+
const strategyDMatch = content.match(strategyDRe);
|
|
623
|
+
if (strategyDMatch) {
|
|
624
|
+
const markdownVarD = strategyDMatch[1];
|
|
625
|
+
const isToolResultVarD = strategyDMatch[2];
|
|
626
|
+
const dMatchFull = strategyDMatch[0];
|
|
627
|
+
const dIdx = strategyDMatch.index;
|
|
628
|
+
const patched = `if(` + marker +
|
|
629
|
+
`(!` + markdownVarD + `||window.__openagentOwnsToolCards&&window.__openagentOwnsToolCards(` +
|
|
630
|
+
toolCardsVar + `))&&` + hasToolCardsVar + `&&` + isToolResultVarD + `)`;
|
|
631
|
+
content = content.substring(0, dIdx) + patched + content.substring(dIdx + dMatchFull.length);
|
|
632
|
+
writeFileSync(bundlePath, content, 'utf-8');
|
|
633
|
+
logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy D, md=${markdownVarD})`);
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
616
636
|
// 策略B (v4.11~v4.1x fallback): compound = hasToolCards && (showToolCalls ?? !0)
|
|
617
637
|
const compoundRe = new RegExp(`(\\w+)=${hasToolCardsVar}&&\\(\\w+\\.showToolCalls\\?\\?!0\\)`);
|
|
618
638
|
const compoundMatch = content.match(compoundRe);
|
|
619
639
|
if (!compoundMatch) {
|
|
620
|
-
|
|
640
|
+
// All strategies (A/C/D/B) failed — dump context for diagnostics
|
|
641
|
+
const anchorHintIdx = content.indexOf(hasToolCardsVar);
|
|
642
|
+
const ctxStart = anchorHintIdx > 200 ? anchorHintIdx - 200 : 0;
|
|
643
|
+
const ctxEnd = Math.min(anchorHintIdx + 200, content.length);
|
|
644
|
+
logger.warn(`[ui-ext:regex] remote-agent-msg: all strategies (A/C/D/B) failed for toolCards=${toolCardsVar}. ` +
|
|
645
|
+
`Context around hasToolCardsVar (at ${anchorHintIdx}): ` +
|
|
646
|
+
content.substring(ctxStart, ctxEnd));
|
|
621
647
|
return false;
|
|
622
648
|
}
|
|
623
649
|
const compoundVar = compoundMatch[1];
|
|
@@ -670,14 +696,15 @@ const mentionHooks = {
|
|
|
670
696
|
const markerA = makeMarker(this.id);
|
|
671
697
|
const hookA = `${markerA}if(window.__clMention?.onKeyDown(${eventParam}))return;`;
|
|
672
698
|
content = content.substring(0, keydownInsertIdx) + hookA + content.substring(keydownInsertIdx);
|
|
673
|
-
// 注入点 B: handleInput —
|
|
699
|
+
// 注入点 B: handleInput — 四种形态兼容
|
|
674
700
|
// Shape A (old): .onDraftChange(target.value)}}
|
|
675
701
|
// Shape B (4.11~4.x): .onDraftChange(target.value) (arrow function 内)
|
|
676
|
-
// Shape C (2026.6.x): be=t=>{let n=t.target;pL(e,n.value)} — 引入 pL helper
|
|
677
|
-
//
|
|
702
|
+
// Shape C (2026.6.x): be=t=>{let n=t.target;pL(e,n.value)} — 引入 pL helper
|
|
703
|
+
// Shape D (generic): .onDraftChange(任意.target.value) 不加结尾限制
|
|
678
704
|
const inputReA = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)\}\}/;
|
|
679
705
|
const inputReB = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)/;
|
|
680
706
|
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\)\}/;
|
|
707
|
+
const inputReD = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.target\.value\)/;
|
|
681
708
|
let inputMatch = content.match(inputReA);
|
|
682
709
|
let inputShape = 'A';
|
|
683
710
|
if (!inputMatch) {
|
|
@@ -689,12 +716,22 @@ const mentionHooks = {
|
|
|
689
716
|
inputShape = 'B';
|
|
690
717
|
}
|
|
691
718
|
if (!inputMatch) {
|
|
692
|
-
|
|
719
|
+
inputMatch = content.match(inputReD);
|
|
720
|
+
inputShape = 'D';
|
|
721
|
+
}
|
|
722
|
+
if (!inputMatch) {
|
|
723
|
+
// All shapes missed — dump context for diagnostics
|
|
724
|
+
const draftHintIdx = content.indexOf('.onDraftChange');
|
|
725
|
+
const ctxStart = draftHintIdx > 200 ? draftHintIdx - 200 : 0;
|
|
726
|
+
const ctxEnd = Math.min(draftHintIdx + 200, content.length);
|
|
727
|
+
logger.warn(`[ui-ext:regex] mention-hooks: input anchor not found (Shape A/B/C/D all missed). ` +
|
|
728
|
+
`Context around onDraftChange (at ${draftHintIdx}): ` +
|
|
729
|
+
content.substring(ctxStart, ctxEnd));
|
|
693
730
|
return false;
|
|
694
731
|
}
|
|
695
732
|
const targetVar = inputMatch[1];
|
|
696
733
|
const inputAnchorIdx = content.indexOf(inputMatch[0]);
|
|
697
|
-
// Shape A: }} 之前;Shape C: 末尾 } 之前;Shape B: ) 之后
|
|
734
|
+
// Shape A: }} 之前;Shape C: 末尾 } 之前;Shape B/D: ) 之后
|
|
698
735
|
const draftCallEnd = inputShape === 'A'
|
|
699
736
|
? inputAnchorIdx + inputMatch[0].length - 2
|
|
700
737
|
: inputShape === 'C'
|
|
@@ -5,3 +5,11 @@
|
|
|
5
5
|
* Used by remote-agent-tool (WebUI continuation) and download-file-tool (artifact download).
|
|
6
6
|
*/
|
|
7
7
|
export declare function resolveOasnAccessUrl(accessApiBase: string | undefined, url: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a WebUI continuation URL for user-facing browser clicks.
|
|
10
|
+
*
|
|
11
|
+
* WebUI auth is Owner-cookie based, so the clickable URL must use the same
|
|
12
|
+
* public origin as the claim/login flow. API-key calls can use an internal
|
|
13
|
+
* Access base, but browser links cannot reuse cookies across IP/domain origins.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveOasnWebuiUrl(accessApiBase: string | undefined, claimUrl: string | undefined, url: string): string;
|
|
@@ -18,3 +18,43 @@ export function resolveOasnAccessUrl(accessApiBase, url) {
|
|
|
18
18
|
return url;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve a WebUI continuation URL for user-facing browser clicks.
|
|
23
|
+
*
|
|
24
|
+
* WebUI auth is Owner-cookie based, so the clickable URL must use the same
|
|
25
|
+
* public origin as the claim/login flow. API-key calls can use an internal
|
|
26
|
+
* Access base, but browser links cannot reuse cookies across IP/domain origins.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveOasnWebuiUrl(accessApiBase, claimUrl, url) {
|
|
29
|
+
if (!url)
|
|
30
|
+
return url;
|
|
31
|
+
const claimOrigin = originOf(claimUrl);
|
|
32
|
+
if (!claimOrigin || !isWebuiUrl(url, accessApiBase || claimOrigin)) {
|
|
33
|
+
return resolveOasnAccessUrl(accessApiBase, url);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const absolute = new URL(url, accessApiBase || claimOrigin);
|
|
37
|
+
return new URL(`${absolute.pathname}${absolute.search}${absolute.hash}`, claimOrigin).toString();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return resolveOasnAccessUrl(accessApiBase, url);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function originOf(value) {
|
|
44
|
+
if (!value)
|
|
45
|
+
return undefined;
|
|
46
|
+
try {
|
|
47
|
+
return new URL(value).origin;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function isWebuiUrl(url, base) {
|
|
54
|
+
try {
|
|
55
|
+
return new URL(url, base).pathname.startsWith('/webui/');
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|