dsh-agentone 0.5.30 → 0.5.32

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/lib/client.js CHANGED
@@ -16,18 +16,102 @@ window.__ModuleLoader__.load({
16
16
  factory: (require) => {
17
17
  const React = require('react');
18
18
 
19
+ // ==desktop-srcdoc-begin==
20
+ // DSH NEXT 桌面壳(dsh-app:// 协议)的转发层只给「主框架的子资源请求」注入
21
+ // x-dsh-desktop-renderer 令牌;iframe 子框架导航拿不到令牌,/agentone/ 被
22
+ // 403 空响应——这是桌面端设置区白屏的根因。桌面端改为主框架 fetch 取回页面
23
+ // HTML 后用 srcdoc 渲染,并注入两样东西:
24
+ // 1. <base href="/agentone/"> —— 页面相对路径(fetch('api/...'))的解析
25
+ // 基准与真实 iframe 一致;
26
+ // 2. fetch 代理 shim —— srcdoc 子框架自己发请求同样没有令牌,一律先按
27
+ // document.baseURI 解析成绝对 URL,再转主框架 parent.fetch 发出(同源
28
+ // srcdoc,令牌随主框架请求注入);同时打 __AGENTONE_DESKTOP__ 标记,
29
+ // 供页面登录流程走桌面本地回调。
30
+ const DESKTOP_PAGE_SHIM =
31
+ '<script>(function(){' +
32
+ 'window.__AGENTONE_DESKTOP__=true;' +
33
+ 'var pf=window.parent.fetch.bind(window.parent);' +
34
+ 'window.fetch=function(input,init){' +
35
+ 'if(typeof input==="string")input=new URL(input,document.baseURI).href;' +
36
+ 'return pf(input,init);' +
37
+ '};' +
38
+ '})();</scr' + 'ipt>';
39
+
40
+ function buildDesktopSrcdoc(html) {
41
+ const preamble = '<base href="/agentone/">\n' + DESKTOP_PAGE_SHIM + '\n';
42
+ const head = /<head[^>]*>/i.exec(html);
43
+ if (head) {
44
+ const at = head.index + head[0].length;
45
+ return html.slice(0, at) + '\n' + preamble + html.slice(at);
46
+ }
47
+ return preamble + html;
48
+ }
49
+ // ==desktop-srcdoc-end==
50
+
51
+ const SECTION_IFRAME_STYLE = {
52
+ border: 'none',
53
+ width: '100%',
54
+ height: '100%',
55
+ minHeight: '480px',
56
+ borderRadius: '12px',
57
+ background: 'transparent',
58
+ };
59
+
19
60
  function AgentOneSection() {
61
+ // dsh-app:// 即 DSH NEXT 桌面壳;http(s) 为 web/旧桌面(cookie 鉴权,iframe 直连可用)
62
+ const isDesktop = window.location.protocol === 'dsh-app:';
63
+ const [doc, setDoc] = React.useState(null);
64
+ const [loadError, setLoadError] = React.useState(false);
65
+ const [retryTick, setRetryTick] = React.useState(0);
66
+
67
+ React.useEffect(() => {
68
+ if (!isDesktop) return undefined;
69
+ let cancelled = false;
70
+ setDoc(null);
71
+ setLoadError(false);
72
+ fetch('/agentone/')
73
+ .then((response) => {
74
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
75
+ return response.text();
76
+ })
77
+ .then((html) => { if (!cancelled) setDoc(buildDesktopSrcdoc(html)); })
78
+ .catch(() => { if (!cancelled) setLoadError(true); });
79
+ return () => { cancelled = true; };
80
+ }, [isDesktop, retryTick]);
81
+
82
+ if (!isDesktop) {
83
+ return React.createElement('iframe', {
84
+ src: '/agentone/',
85
+ title: 'AgentOne',
86
+ style: SECTION_IFRAME_STYLE,
87
+ });
88
+ }
89
+ if (loadError) {
90
+ return React.createElement(
91
+ 'div',
92
+ { style: { padding: '24px', textAlign: 'center', color: 'var(--dsw-alias-label-secondary, #57606a)' } },
93
+ React.createElement('p', null, 'AgentOne 面板加载失败,请重试'),
94
+ React.createElement(
95
+ 'button',
96
+ {
97
+ onClick: () => setRetryTick((tick) => tick + 1),
98
+ style: { padding: '8px 20px', borderRadius: '10px', border: 'none', cursor: 'pointer', background: '#2f6feb', color: '#fff' },
99
+ },
100
+ '重新加载',
101
+ ),
102
+ );
103
+ }
104
+ if (doc === null) {
105
+ return React.createElement(
106
+ 'div',
107
+ { style: { padding: '24px', textAlign: 'center', color: 'var(--dsw-alias-label-secondary, #57606a)' } },
108
+ '加载中…',
109
+ );
110
+ }
20
111
  return React.createElement('iframe', {
21
- src: '/agentone/',
112
+ srcDoc: doc,
22
113
  title: 'AgentOne',
23
- style: {
24
- border: 'none',
25
- width: '100%',
26
- height: '100%',
27
- minHeight: '480px',
28
- borderRadius: '12px',
29
- background: 'transparent',
30
- },
114
+ style: SECTION_IFRAME_STYLE,
31
115
  });
32
116
  }
33
117
 
package/lib/model.js CHANGED
@@ -5,8 +5,10 @@
5
5
  // settings.yaml llm-pi-ai.providers.platform + agent-default-model
6
6
  // .credentials.yaml refs.AGENTONE_API_KEY(dsh 凭据库,owner-only,watch 热加载)
7
7
  //
8
- // 密钥形态是 30 天插件令牌(M1 exchange 签发),指向平台 /api/plugin/v1 代理;
9
- // 真实 Token Hub 密钥不出平台。
8
+ // 密钥形态:平台 /api/plugin/v1/models 下发 direct(Token Hub base_url + 该用户
9
+ // 自己的 Token Hub 用户级 key),dsh 直连 Token Hub,运行时流量不过境平台(平台
10
+ // 部署国外时避免跨境转发延迟);计量/配额由 Token Hub 按用户令牌口径执行。
11
+ // 旧版平台不下发 direct 时回退为 30 天插件令牌 + 平台 /api/plugin/v1 代理。
10
12
  import { readFile, rename, writeFile } from 'node:fs/promises';
11
13
  import { join } from 'node:path';
12
14
  import { parse, stringify } from 'yaml';
@@ -77,8 +79,13 @@ function parseYamlObject(text) {
77
79
  return {};
78
80
  }
79
81
 
80
- /** 合并出 settings.yaml 文本:覆盖 platform provider 与 agent-default-model。 */
81
- export function buildSettingsYaml(existingText, { baseUrl, models, defaultModel }) {
82
+ /** 合并出 settings.yaml 文本:覆盖 platform provider 与 agent-default-model。
83
+ * direct(直连 Token Hub)时写 compat.supportsDeveloperRole=false:pi-ai 对
84
+ * reasoning 模型默认把系统提示发成 developer 角色(自定义域名不在它的
85
+ * 非标准名单里),GLM 上游只认 system 会 400;false 保持 system。该配置键
86
+ * 需要 dsh-llm-pi-ai ≥ 0.1.0-rc.8,代理模式不写(网关侧有同样的归一化)。
87
+ */
88
+ export function buildSettingsYaml(existingText, { baseUrl, models, defaultModel, direct = false }) {
82
89
  const settings = parseYamlObject(existingText);
83
90
  const existing = settings['llm-pi-ai'];
84
91
  const llmPiAi =
@@ -90,6 +97,7 @@ export function buildSettingsYaml(existingText, { baseUrl, models, defaultModel
90
97
  apiKeyEnv: CREDENTIAL_REF,
91
98
  api: 'openai-completions',
92
99
  baseURL: baseUrl,
100
+ ...(direct ? { compat: { supportsDeveloperRole: false } } : {}),
93
101
  reasoning: 'high',
94
102
  models: models
95
103
  .map((item) => platformModelEntry(item))
@@ -151,7 +159,11 @@ export async function fetchPlatformModels(platformUrl, accessToken) {
151
159
  .filter(Boolean)
152
160
  : [];
153
161
  if (models.length === 0) throw new Error('平台返回的可用模型列表为空');
154
- return { models, defaultModel: data.default_model || (models[0]?.id ?? models[0]) };
162
+ const direct =
163
+ data.direct && typeof data.direct.base_url === 'string' && typeof data.direct.api_key === 'string'
164
+ ? { baseUrl: data.direct.base_url.replace(/\/+$/, ''), apiKey: data.direct.api_key }
165
+ : null;
166
+ return { models, defaultModel: data.default_model || (models[0]?.id ?? models[0]), direct };
155
167
  }
156
168
 
157
169
  async function writeFileAtomic(path, text, mode) {
@@ -172,11 +184,15 @@ function tmpName(path) {
172
184
  export async function applyPlatformModel(config) {
173
185
  const credentials = await loadCredentials(config);
174
186
  if (!credentials) throw new Error('尚未登录平台');
175
- const baseUrl = `${String(credentials.platform_url || config.platformUrl).replace(/\/+$/, '')}/api/plugin/v1`;
176
- const { models, defaultModel } = await fetchPlatformModels(
187
+ const { models, defaultModel, direct } = await fetchPlatformModels(
177
188
  credentials.platform_url || config.platformUrl,
178
189
  credentials.access_token,
179
190
  );
191
+ // 平台下发 direct 时 dsh 直连 Token Hub;否则回退平台代理(旧版平台)。
192
+ const baseUrl = direct
193
+ ? direct.baseUrl
194
+ : `${String(credentials.platform_url || config.platformUrl).replace(/\/+$/, '')}/api/plugin/v1`;
195
+ const providerKey = direct ? direct.apiKey : credentials.access_token;
180
196
  const home = resolveDshHome(config);
181
197
  const settingsPath = join(home, 'settings.yaml');
182
198
  const credentialsPath = join(home, '.credentials.yaml');
@@ -184,10 +200,10 @@ export async function applyPlatformModel(config) {
184
200
  readFile(settingsPath, 'utf8').catch(() => ''),
185
201
  readFile(credentialsPath, 'utf8').catch(() => ''),
186
202
  ]);
187
- await writeFileAtomic(credentialsPath, buildCredentialsYaml(existingCredentials, credentials.access_token), 0o600);
203
+ await writeFileAtomic(credentialsPath, buildCredentialsYaml(existingCredentials, providerKey), 0o600);
188
204
  await writeFileAtomic(
189
205
  settingsPath,
190
- buildSettingsYaml(existingSettings, { baseUrl, models, defaultModel }),
206
+ buildSettingsYaml(existingSettings, { baseUrl, models, defaultModel, direct: direct !== null }),
191
207
  );
192
208
  return { models, defaultModel };
193
209
  }
package/lib/page.js CHANGED
@@ -1084,8 +1084,10 @@ $('login-btn').onclick = async () => {
1084
1084
  const btn = $('login-btn');
1085
1085
  const base = location.pathname.replace(/\\/agentone\\/?$/, '');
1086
1086
  // DSH Desktop:页面 URL 带 dsh-desktop-mode 参数,且宿主对外请求 403——
1087
- // 传 desktop 标记让后端起一次性本地回调服务
1088
- const isDesktop = new URLSearchParams(location.search).has('dsh-desktop-mode');
1087
+ // 传 desktop 标记让后端起一次性本地回调服务。DSH NEXT 桌面壳下页面经
1088
+ // srcdoc 加载(无 URL 参数),由 client.js 注入的 __AGENTONE_DESKTOP__ 兜底。
1089
+ const isDesktop = new URLSearchParams(location.search).has('dsh-desktop-mode')
1090
+ || window.__AGENTONE_DESKTOP__ === true;
1089
1091
  const callbackUrl = isDesktop
1090
1092
  ? null
1091
1093
  : location.pathname.startsWith('/portal/')
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-agentone",
3
- "version": "0.5.30",
4
- "description": "AgentOne 平台集成插件:飞书登录、平台模型(同步失败自愈重试)、SkillHub 技能(升级检测一键升级、装后即时刷新、套餐技能自动同步)、套餐申请、插件管理与自助升级、lark-cli / ccpg-cli 探测与一键安装(请求超时与错误码透传、令牌轮换 single-flight、凭据损坏容错与并发安全写入、状态页 UX 增强、全局中文回复默认(AGENTS.md 受标记管理、永不覆盖用户设置))",
3
+ "version": "0.5.32",
4
+ "description": "AgentOne 平台集成插件:飞书登录、平台模型(直连 Token Hub 不过境平台、同步失败自愈重试)、SkillHub 技能(升级检测一键升级、装后即时刷新、套餐技能自动同步)、套餐申请、插件管理与自助升级、lark-cli / ccpg-cli 探测与一键安装(请求超时与错误码透传、令牌轮换 single-flight、凭据损坏容错与并发安全写入、状态页 UX 增强、全局中文回复默认(AGENTS.md 受标记管理、永不覆盖用户设置)、桌面版设置页白屏修复(iframe 子框架导航无桌面令牌改走主框架 fetch + srcdoc 渲染))",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "smoke": "node scripts/env-matrix-smoke.mjs"