dsh-agentone 0.5.32 → 0.5.34
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/index.js +22 -4
- package/lib/model.js +111 -20
- package/lib/page.js +49 -18
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -84,18 +84,23 @@ async function readJsonBody(request, limitBytes = 8192) {
|
|
|
84
84
|
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/** 应用平台模型配置并把结果落盘(成功清 model_error,失败记录原因与错误码)。
|
|
87
|
+
/** 应用平台模型配置并把结果落盘(成功清 model_error,失败记录原因与错误码)。
|
|
88
|
+
* model_apply 记录本次是否写进了活配置层('live' 即时生效 / 'next-restart')。 */
|
|
88
89
|
async function syncPlatformModel(config, log) {
|
|
89
90
|
try {
|
|
90
|
-
const result = await applyPlatformModel(config);
|
|
91
|
+
const result = await applyPlatformModel(config, settingsService);
|
|
91
92
|
await updateCredentials(config, {
|
|
92
93
|
models_count: result.models.length,
|
|
93
94
|
default_model: result.defaultModel,
|
|
94
95
|
model_synced_at: new Date().toISOString(),
|
|
96
|
+
model_apply: result.apply,
|
|
95
97
|
model_error: null,
|
|
96
98
|
model_error_code: null,
|
|
97
99
|
});
|
|
98
|
-
log.info(
|
|
100
|
+
log.info(
|
|
101
|
+
`[dsh-agentone] platform model applied: ${result.defaultModel}(共 ${result.models.length} 个可用模型,`
|
|
102
|
+
+ `${result.apply === 'live' ? '已即时生效' : '重启 dsh 后生效'})`,
|
|
103
|
+
);
|
|
99
104
|
return result;
|
|
100
105
|
} catch (error) {
|
|
101
106
|
const message = error?.message || String(error);
|
|
@@ -190,6 +195,12 @@ async function ensureFreshToken(config, log) {
|
|
|
190
195
|
let PLUGIN_PROFILE = 'web';
|
|
191
196
|
/** desktop 受管插件操作通道(desktopPnpm.runPlugin 包装),非 desktop 为 null。 */
|
|
192
197
|
let desktopPluginCli = null;
|
|
198
|
+
/**
|
|
199
|
+
* dsh ≥ 0.1.7 的官方 settings 服务(apply 时可选获取):用来把平台模型写进
|
|
200
|
+
* profile patch 活配置层,同步即时生效。旧宿主没有该服务,取到 null 即回退
|
|
201
|
+
* 为「只写 settings.yaml + 下次启动导入」。
|
|
202
|
+
*/
|
|
203
|
+
let settingsService = null;
|
|
193
204
|
|
|
194
205
|
function pluginProfileDir(config) {
|
|
195
206
|
return join(resolveDshHome(config), 'profiles', PLUGIN_PROFILE);
|
|
@@ -772,6 +783,10 @@ export function apply(ctx, config) {
|
|
|
772
783
|
// desktopPnpm 做装/卸/升级(desktop 里 dsh CLI 拒绝操作 desktop profile)。
|
|
773
784
|
const desktopProfiles = ctx.get?.('desktopProfiles');
|
|
774
785
|
const isDesktopRuntime = Boolean(desktopProfiles?.current?.name) || isDesktopHost();
|
|
786
|
+
// 官方 settings 服务(可选依赖):dsh ≥ 0.1.7 用它把平台模型写进 profile
|
|
787
|
+
// patch 活配置层;取不到(旧宿主)就回退「只写 settings.yaml + 下次启动导入」。
|
|
788
|
+
// 不能写进 inject:那会让插件在旧宿主上一直 PENDING。
|
|
789
|
+
settingsService = ctx.get?.('settings') ?? null;
|
|
775
790
|
if (desktopProfiles?.current?.name) {
|
|
776
791
|
PLUGIN_PROFILE = desktopProfiles.current.name;
|
|
777
792
|
ctx.inject(['desktopPnpm'], (desktopCtx) => {
|
|
@@ -864,6 +879,8 @@ export function apply(ctx, config) {
|
|
|
864
879
|
default_model: credentials.default_model ?? null,
|
|
865
880
|
models_count: credentials.models_count ?? 0,
|
|
866
881
|
synced_at: credentials.model_synced_at ?? null,
|
|
882
|
+
// 'live' 已写活配置层即时生效;'next-restart' 需重启 dsh 才导入
|
|
883
|
+
apply: credentials.model_apply ?? null,
|
|
867
884
|
error: credentials.model_error ?? null,
|
|
868
885
|
error_code: credentials.model_error_code ?? null,
|
|
869
886
|
}
|
|
@@ -976,6 +993,7 @@ export function apply(ctx, config) {
|
|
|
976
993
|
ok: true,
|
|
977
994
|
default_model: result.defaultModel,
|
|
978
995
|
models_count: result.models.length,
|
|
996
|
+
apply: result.apply,
|
|
979
997
|
});
|
|
980
998
|
} catch (error) {
|
|
981
999
|
sendJson(response, 502, { error: error?.message || '同步平台模型失败' });
|
|
@@ -1490,7 +1508,7 @@ export function apply(ctx, config) {
|
|
|
1490
1508
|
await revokePlatformToken(config.platformUrl, credentials.refresh_token);
|
|
1491
1509
|
}
|
|
1492
1510
|
await clearCredentials(config);
|
|
1493
|
-
await removePlatformModel(config).catch((error) => {
|
|
1511
|
+
await removePlatformModel(config, settingsService).catch((error) => {
|
|
1494
1512
|
log.warn(`[dsh-agentone] remove platform model failed: ${error?.message || error}`);
|
|
1495
1513
|
});
|
|
1496
1514
|
await removePlatformSkillhub(config, log).catch((error) => {
|
package/lib/model.js
CHANGED
|
@@ -79,20 +79,16 @@ function parseYamlObject(text) {
|
|
|
79
79
|
return {};
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
83
|
-
*
|
|
82
|
+
/** 平台 provider 配置片段。settings.yaml 与 dsh「活配置层」(profile patch)
|
|
83
|
+
* 必须写出同一形状,两处共用本构造器,避免两份定义漂移。
|
|
84
|
+
*
|
|
85
|
+
* direct(直连 Token Hub)时带 compat.supportsDeveloperRole=false:pi-ai 对
|
|
84
86
|
* reasoning 模型默认把系统提示发成 developer 角色(自定义域名不在它的
|
|
85
87
|
* 非标准名单里),GLM 上游只认 system 会 400;false 保持 system。该配置键
|
|
86
88
|
* 需要 dsh-llm-pi-ai ≥ 0.1.0-rc.8,代理模式不写(网关侧有同样的归一化)。
|
|
87
89
|
*/
|
|
88
|
-
export function
|
|
89
|
-
|
|
90
|
-
const existing = settings['llm-pi-ai'];
|
|
91
|
-
const llmPiAi =
|
|
92
|
-
existing && typeof existing === 'object' && existing.providers && typeof existing.providers === 'object'
|
|
93
|
-
? existing
|
|
94
|
-
: { providers: {} };
|
|
95
|
-
llmPiAi.providers[PROVIDER_KEY] = {
|
|
90
|
+
export function buildPlatformProvider(baseUrl, models, direct = false) {
|
|
91
|
+
return {
|
|
96
92
|
displayName: '平台模型',
|
|
97
93
|
apiKeyEnv: CREDENTIAL_REF,
|
|
98
94
|
api: 'openai-completions',
|
|
@@ -103,12 +99,24 @@ export function buildSettingsYaml(existingText, { baseUrl, models, defaultModel,
|
|
|
103
99
|
.map((item) => platformModelEntry(item))
|
|
104
100
|
.filter((entry) => entry !== null),
|
|
105
101
|
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** 平台默认模型配置片段(settings.yaml 与活层共用)。 */
|
|
105
|
+
export function buildDefaultModel(defaultModel) {
|
|
106
|
+
return { provider: PROVIDER_KEY, model: defaultModel, reasoningEffort: 'high' };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 合并出 settings.yaml 文本:覆盖 platform provider 与 agent-default-model。 */
|
|
110
|
+
export function buildSettingsYaml(existingText, { baseUrl, models, defaultModel, direct = false }) {
|
|
111
|
+
const settings = parseYamlObject(existingText);
|
|
112
|
+
const existing = settings['llm-pi-ai'];
|
|
113
|
+
const llmPiAi =
|
|
114
|
+
existing && typeof existing === 'object' && existing.providers && typeof existing.providers === 'object'
|
|
115
|
+
? existing
|
|
116
|
+
: { providers: {} };
|
|
117
|
+
llmPiAi.providers[PROVIDER_KEY] = buildPlatformProvider(baseUrl, models, direct);
|
|
106
118
|
settings['llm-pi-ai'] = llmPiAi;
|
|
107
|
-
settings['agent-default-model'] =
|
|
108
|
-
provider: PROVIDER_KEY,
|
|
109
|
-
model: defaultModel,
|
|
110
|
-
reasoningEffort: 'high',
|
|
111
|
-
};
|
|
119
|
+
settings['agent-default-model'] = buildDefaultModel(defaultModel);
|
|
112
120
|
return stringify(settings);
|
|
113
121
|
}
|
|
114
122
|
|
|
@@ -177,11 +185,85 @@ function tmpName(path) {
|
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
/**
|
|
180
|
-
*
|
|
188
|
+
* 在 dsh「活配置层」(profile patch)里写一个 entry 的 volatile 字段。
|
|
189
|
+
*
|
|
190
|
+
* 活层写操作都要带 revision:冲突只重试一次(重新读 revision),绝不盲循环。
|
|
191
|
+
*/
|
|
192
|
+
async function withLiveEntry(settings, ns, run) {
|
|
193
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
194
|
+
const descriptor = settings.describe().find((row) => row?.ns === ns);
|
|
195
|
+
if (!descriptor) throw new Error(`配置项 ${ns} 不可用`);
|
|
196
|
+
try {
|
|
197
|
+
await run(descriptor.revision);
|
|
198
|
+
return;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (error?.code !== 'SETTINGS_CONFLICT' || attempt >= 1) throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 把平台 provider 写进 dsh 活配置层,返回是否即时生效。
|
|
207
|
+
*
|
|
208
|
+
* dsh ≥ 0.1.7 起用户配置真源迁到 profile patch 层:settings.yaml 只在 host
|
|
209
|
+
* 启动时被导入一次(dsh-settings 的 importLegacyDocument,导入后改名
|
|
210
|
+
* .imported),此后 patch 层才是活配置。所以插件只写 settings.yaml 的话,
|
|
211
|
+
* 面板点「重新同步模型」要等重启才生效,界面还会长期显示上一轮导入的旧值。
|
|
212
|
+
*
|
|
213
|
+
* 有官方 settings 服务(ctx.get('settings'))时把同一份 provider 写进活层:
|
|
214
|
+
* - `llm-pi-ai` 的 providers 与 `agent-default-model` 的字段在目标版本都是
|
|
215
|
+
* volatile,可经 settings.update 写入;
|
|
216
|
+
* - 合并语义是对象深合并、数组整体替换,所以只覆盖 platform,不碰用户的
|
|
217
|
+
* 其它 provider;
|
|
218
|
+
* - llm-pi-ai 监听 loader/volatile-update 重新注册路由 → 改完即时生效。
|
|
219
|
+
*
|
|
220
|
+
* @returns 'live' 已写活层;'next-restart' 没有该服务或写入被拒(旧宿主行为)
|
|
221
|
+
*/
|
|
222
|
+
export async function applyPlatformProviderLive(settings, { baseUrl, models, defaultModel, direct = false }) {
|
|
223
|
+
if (!settings || typeof settings.describe !== 'function' || typeof settings.update !== 'function') {
|
|
224
|
+
return 'next-restart';
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const provider = buildPlatformProvider(baseUrl, models, direct);
|
|
228
|
+
await withLiveEntry(settings, 'llm-pi-ai', (revision) =>
|
|
229
|
+
settings.update('llm-pi-ai', { providers: { [PROVIDER_KEY]: provider } }, revision),
|
|
230
|
+
);
|
|
231
|
+
} catch {
|
|
232
|
+
// 活层写不进去不阻断:settings.yaml 已写成功,下次启动仍会导入(旧行为)
|
|
233
|
+
return 'next-restart';
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
await withLiveEntry(settings, 'agent-default-model', (revision) =>
|
|
237
|
+
settings.update('agent-default-model', buildDefaultModel(defaultModel), revision),
|
|
238
|
+
);
|
|
239
|
+
} catch {
|
|
240
|
+
// 默认模型写不进去不影响模型可用(provider 已生效),保留用户当前默认
|
|
241
|
+
}
|
|
242
|
+
return 'live';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 登出时清掉活层的平台 provider。
|
|
247
|
+
*
|
|
248
|
+
* 用 mutate 的 unset 语义(applyPathOp 对对象键走 Reflect.deleteProperty);
|
|
249
|
+
* 刻意不动 `agent-default-model`:它的 provider/model 是 schema required 字段,
|
|
250
|
+
* 置空会把 entry 弄成半残,而删掉 provider 已经足够让平台模型不可选。
|
|
251
|
+
*/
|
|
252
|
+
export async function removePlatformProviderLive(settings) {
|
|
253
|
+
if (!settings || typeof settings.describe !== 'function' || typeof settings.mutate !== 'function') return;
|
|
254
|
+
await withLiveEntry(settings, 'llm-pi-ai', (revision) =>
|
|
255
|
+
settings.mutate('llm-pi-ai', [{ op: 'unset', path: ['providers', PROVIDER_KEY] }], revision),
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 拉取平台模型并写入本机 dsh 配置。返回 { models, defaultModel, apply }。
|
|
261
|
+
* apply='live' 表示已写进活配置层即时生效;'next-restart' 表示只能等下次
|
|
262
|
+
* 启动导入 settings.yaml。
|
|
181
263
|
* 失败抛 PlatformError(中文 message + 平台错误码),由调用方记录/展示
|
|
182
264
|
* (不影响已生效的旧配置)。
|
|
183
265
|
*/
|
|
184
|
-
export async function applyPlatformModel(config) {
|
|
266
|
+
export async function applyPlatformModel(config, settings) {
|
|
185
267
|
const credentials = await loadCredentials(config);
|
|
186
268
|
if (!credentials) throw new Error('尚未登录平台');
|
|
187
269
|
const { models, defaultModel, direct } = await fetchPlatformModels(
|
|
@@ -205,11 +287,19 @@ export async function applyPlatformModel(config) {
|
|
|
205
287
|
settingsPath,
|
|
206
288
|
buildSettingsYaml(existingSettings, { baseUrl, models, defaultModel, direct: direct !== null }),
|
|
207
289
|
);
|
|
208
|
-
|
|
290
|
+
// settings.yaml 与活层双写:前者兼容旧宿主,也让下次启动的导入是幂等的
|
|
291
|
+
// (只写活层的话,settings.yaml 里的旧值会在下次启动把活层盖回去)。
|
|
292
|
+
const apply = await applyPlatformProviderLive(settings, {
|
|
293
|
+
baseUrl,
|
|
294
|
+
models,
|
|
295
|
+
defaultModel,
|
|
296
|
+
direct: direct !== null,
|
|
297
|
+
});
|
|
298
|
+
return { models, defaultModel, apply };
|
|
209
299
|
}
|
|
210
300
|
|
|
211
|
-
/** 登出时清理本机配置:删除凭据引用与 platform provider
|
|
212
|
-
export async function removePlatformModel(config) {
|
|
301
|
+
/** 登出时清理本机配置:删除凭据引用与 platform provider(含活层)。 */
|
|
302
|
+
export async function removePlatformModel(config, settings) {
|
|
213
303
|
const home = resolveDshHome(config);
|
|
214
304
|
const settingsPath = join(home, 'settings.yaml');
|
|
215
305
|
const credentialsPath = join(home, '.credentials.yaml');
|
|
@@ -223,4 +313,5 @@ export async function removePlatformModel(config) {
|
|
|
223
313
|
if (existingSettings) {
|
|
224
314
|
await writeFileAtomic(settingsPath, buildLogoutSettingsYaml(existingSettings));
|
|
225
315
|
}
|
|
316
|
+
await removePlatformProviderLive(settings).catch(() => {});
|
|
226
317
|
}
|
package/lib/page.js
CHANGED
|
@@ -149,11 +149,13 @@ let lastStatus = null;
|
|
|
149
149
|
// 上一次轮询到的套餐状态(pending→active 自动同步模型用)
|
|
150
150
|
let lastPlanState = null;
|
|
151
151
|
const notify = (message, kind) => {
|
|
152
|
-
|
|
152
|
+
// 保留 toast-fixed:覆盖 className 会把它一起抹掉,常驻可视区顶部的定位
|
|
153
|
+
// 失效,页面滚动后操作反馈落在视口外(CSS 注释里的「仍可见」不再成立)
|
|
154
|
+
toast.className = 'banner toast-fixed ' + (kind || 'ok');
|
|
153
155
|
toast.textContent = message;
|
|
154
156
|
clearTimeout(toastTimer);
|
|
155
157
|
// 错误信息可能较长(含平台原因),给足阅读时间;成功提示维持 4s
|
|
156
|
-
toastTimer = setTimeout(() => { toast.className = 'banner'; }, kind === 'err' ? 10000 : 4000);
|
|
158
|
+
toastTimer = setTimeout(() => { toast.className = 'banner toast-fixed'; }, kind === 'err' ? 10000 : 4000);
|
|
157
159
|
};
|
|
158
160
|
// withLoading 收尾的刷新分两档:
|
|
159
161
|
// 'status'(默认)——拉 /api/status 并整页 refresh()。模型/SkillHub/登录类
|
|
@@ -888,9 +890,15 @@ const renderPlugins = async () => {
|
|
|
888
890
|
}).then(() => renderPlugins()), '插件已卸载');
|
|
889
891
|
});
|
|
890
892
|
};
|
|
893
|
+
// ==about-upgrade-begin==
|
|
891
894
|
// 关于页版本行:aboutSelf(GET api/plugin/self)有值时展示升级入口或
|
|
892
895
|
// 「已安装待重启」提示;尚未取到时退化为运行中版本(状态轮询自带字段)。
|
|
893
896
|
let aboutSelf = null;
|
|
897
|
+
// 升级进行中标记。本行没有插件/技能列表那样的指纹保护:refresh() 的 5s 状态
|
|
898
|
+
// 轮询每次都会重建它,而升级本身(pnpm view + pnpm add)要几十秒。不把这个态
|
|
899
|
+
// 跨渲染保留,点击后的「升级中…」会在一次轮询内被抹回「升级」——按钮原样弹
|
|
900
|
+
// 回,用户看到的就是「点了没反应」,且能再点一次触发并发重复升级。
|
|
901
|
+
let aboutUpgrading = false;
|
|
894
902
|
const aboutVersionRow = () => {
|
|
895
903
|
const self = aboutSelf;
|
|
896
904
|
if (!self) {
|
|
@@ -899,7 +907,9 @@ const aboutVersionRow = () => {
|
|
|
899
907
|
let value;
|
|
900
908
|
if (self.latest && !self.self_managed) {
|
|
901
909
|
value = 'v' + esc(self.version || '?') + ' → <b>v' + esc(self.latest) + '</b>'
|
|
902
|
-
+
|
|
910
|
+
+ (aboutUpgrading
|
|
911
|
+
? ' <button id="about-upgrade-btn" class="btn small primary" disabled><span class="spin"></span>升级中…</button>'
|
|
912
|
+
: ' <button id="about-upgrade-btn" class="btn small primary" data-loading="升级中…">升级</button>');
|
|
903
913
|
} else if (self.version && self.running && self.version !== self.running) {
|
|
904
914
|
value = 'v' + esc(self.running) + '(运行中)· v' + esc(self.version) + ' 已安装,重启 DSH 后生效';
|
|
905
915
|
} else {
|
|
@@ -909,8 +919,11 @@ const aboutVersionRow = () => {
|
|
|
909
919
|
};
|
|
910
920
|
const wireAboutUpgrade = () => {
|
|
911
921
|
const btn = $('about-upgrade-btn');
|
|
912
|
-
|
|
922
|
+
// 升级中:行里已经是 disabled + spinner(aboutVersionRow 按 aboutUpgrading 渲染),
|
|
923
|
+
// 不重复接线;disabled 按钮本身也点不动,起不了第二个升级
|
|
924
|
+
if (!btn || aboutUpgrading || btn.onclick) return;
|
|
913
925
|
btn.onclick = async () => {
|
|
926
|
+
aboutUpgrading = true;
|
|
914
927
|
btn.disabled = true;
|
|
915
928
|
btn.innerHTML = '<span class="spin"></span>升级中…';
|
|
916
929
|
try {
|
|
@@ -921,15 +934,22 @@ const wireAboutUpgrade = () => {
|
|
|
921
934
|
});
|
|
922
935
|
const data = await resp.json().catch(() => ({}));
|
|
923
936
|
if (!resp.ok) throw new Error(data.error || '插件升级失败');
|
|
937
|
+
aboutUpgrading = false;
|
|
924
938
|
notify('插件已升级,重启 DSH 后生效');
|
|
925
939
|
await refreshAboutSelf(true);
|
|
926
940
|
} catch (error) {
|
|
941
|
+
aboutUpgrading = false;
|
|
927
942
|
notify(error.message || '插件升级失败', 'err');
|
|
928
|
-
|
|
929
|
-
|
|
943
|
+
// 轮询可能刚重建过本行:按可见性整体重渲染,别只改手里这个已脱离文档的节点
|
|
944
|
+
rerenderAbout();
|
|
930
945
|
}
|
|
931
946
|
};
|
|
932
947
|
};
|
|
948
|
+
// 关于页重渲染,仅在「关于」tab 可见时执行:切 tab 拿到版本信息、升级结束后刷新
|
|
949
|
+
const rerenderAbout = () => {
|
|
950
|
+
if (!$('about-body') || document.getElementById('pane-about').hidden) return;
|
|
951
|
+
renderAbout(lastStatus);
|
|
952
|
+
};
|
|
933
953
|
// 版本 + latest 查询有网络开销(pnpm view 打 registry):1 分钟节流,
|
|
934
954
|
// 切到「关于」tab 或升级完成后刷新
|
|
935
955
|
const refreshAboutSelf = async (force = false) => {
|
|
@@ -944,8 +964,7 @@ const refreshAboutSelf = async (force = false) => {
|
|
|
944
964
|
} catch {
|
|
945
965
|
return; // 查询失败保持现状,版本行退化为运行中版本
|
|
946
966
|
}
|
|
947
|
-
|
|
948
|
-
renderAbout(lastStatus);
|
|
967
|
+
rerenderAbout();
|
|
949
968
|
};
|
|
950
969
|
const renderAbout = (s) => {
|
|
951
970
|
const lines = [
|
|
@@ -958,6 +977,7 @@ const renderAbout = (s) => {
|
|
|
958
977
|
$('about-body').innerHTML = lines.join('');
|
|
959
978
|
wireAboutUpgrade();
|
|
960
979
|
};
|
|
980
|
+
// ==about-upgrade-end==
|
|
961
981
|
const searchCatalog = async () => {
|
|
962
982
|
const btn = $('search-btn');
|
|
963
983
|
const q = $('search-input').value.trim();
|
|
@@ -992,6 +1012,21 @@ const searchCatalog = async () => {
|
|
|
992
1012
|
btn.innerHTML = '搜索';
|
|
993
1013
|
}
|
|
994
1014
|
};
|
|
1015
|
+
// 手动同步平台模型(模型卡与套餐阻断兜底两处共用)。成功提示必须区分是否
|
|
1016
|
+
// 已写进活配置层:dsh ≥0.1.7 走 settings 服务即时生效;旧宿主只落盘
|
|
1017
|
+
// settings.yaml,要重启才被导入——不区分的话用户会以为同步没生效。
|
|
1018
|
+
const syncModelNow = (btn) => withLoading(
|
|
1019
|
+
btn,
|
|
1020
|
+
async () => {
|
|
1021
|
+
const resp = await fetch('api/model/sync', { method: 'POST' });
|
|
1022
|
+
const data = await resp.clone().json().catch(() => ({}));
|
|
1023
|
+
if (resp.ok) {
|
|
1024
|
+
notify(data.apply === 'live' ? '平台模型已同步,已即时生效' : '平台模型已同步,重启 DSH 后生效');
|
|
1025
|
+
}
|
|
1026
|
+
return resp;
|
|
1027
|
+
},
|
|
1028
|
+
null,
|
|
1029
|
+
);
|
|
995
1030
|
const refresh = async () => {
|
|
996
1031
|
let s;
|
|
997
1032
|
try {
|
|
@@ -1044,6 +1079,10 @@ const refresh = async () => {
|
|
|
1044
1079
|
lines.push('<div class="kv"><span class="k">默认模型</span><span class="v">' + esc(m.default_model || '未同步') + '</span></div>');
|
|
1045
1080
|
lines.push('<div class="kv"><span class="k">可用模型</span><span class="v">' + esc(m.models_count) + ' 个</span></div>');
|
|
1046
1081
|
if (m.synced_at) lines.push('<div class="kv"><span class="k">上次同步</span><span class="v">' + new Date(m.synced_at).toLocaleString() + '</span></div>');
|
|
1082
|
+
// 旧宿主(无 settings 服务)才提示:同步内容要等下次启动导入才生效
|
|
1083
|
+
if (m.apply === 'next-restart') {
|
|
1084
|
+
lines.push('<div class="kv"><span class="k">生效方式</span><span class="v muted">重启 DSH 后生效</span></div>');
|
|
1085
|
+
}
|
|
1047
1086
|
}
|
|
1048
1087
|
if (!planBlocked && m.error) lines.push('<div class="kv"><span class="k">同步状态</span><span class="v" style="color:var(--err)">' + esc(m.error) + '</span></div>');
|
|
1049
1088
|
if (s.token_expires_at) {
|
|
@@ -1061,11 +1100,7 @@ const refresh = async () => {
|
|
|
1061
1100
|
// planBlocked 兜底手动出口:旧错误残留时(套餐已批但本地还挂着
|
|
1062
1101
|
// plan_required)可立即重同步,不等后端自愈的退避周期
|
|
1063
1102
|
const retryModel = $('retry-model-btn');
|
|
1064
|
-
if (retryModel) retryModel.onclick = () =>
|
|
1065
|
-
retryModel,
|
|
1066
|
-
() => fetch('api/model/sync', { method: 'POST' }),
|
|
1067
|
-
'平台模型已同步',
|
|
1068
|
-
);
|
|
1103
|
+
if (retryModel) retryModel.onclick = () => syncModelNow(retryModel);
|
|
1069
1104
|
}
|
|
1070
1105
|
if (s.logged_in && s.skillhub) {
|
|
1071
1106
|
const sh = s.skillhub;
|
|
@@ -1105,11 +1140,7 @@ $('login-btn').onclick = async () => {
|
|
|
1105
1140
|
// 按钮保持可点:授权窗被用户关掉后可立即重新发起,无需等 2 分钟
|
|
1106
1141
|
btn.textContent = '已打开授权窗口,完成后自动继续(可重开)';
|
|
1107
1142
|
};
|
|
1108
|
-
$('sync-btn').onclick = () =>
|
|
1109
|
-
$('sync-btn'),
|
|
1110
|
-
() => fetch('api/model/sync', { method: 'POST' }),
|
|
1111
|
-
'平台模型已同步',
|
|
1112
|
-
);
|
|
1143
|
+
$('sync-btn').onclick = () => syncModelNow($('sync-btn'));
|
|
1113
1144
|
$('skillhub-sync-btn').onclick = () => withLoading(
|
|
1114
1145
|
$('skillhub-sync-btn'),
|
|
1115
1146
|
() => fetch('api/skillhub/sync', { method: 'POST' }),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agentone",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "AgentOne 平台集成插件:飞书登录、平台模型(直连 Token Hub 不过境平台、同步失败自愈重试)、SkillHub 技能(升级检测一键升级、装后即时刷新、套餐技能自动同步)、套餐申请、插件管理与自助升级、lark-cli / ccpg-cli 探测与一键安装(请求超时与错误码透传、令牌轮换 single-flight、凭据损坏容错与并发安全写入、状态页 UX 增强、全局中文回复默认(AGENTS.md 受标记管理、永不覆盖用户设置)、桌面版设置页白屏修复(iframe 子框架导航无桌面令牌改走主框架 fetch + srcdoc
|
|
3
|
+
"version": "0.5.34",
|
|
4
|
+
"description": "AgentOne 平台集成插件:飞书登录、平台模型(直连 Token Hub 不过境平台、同步失败自愈重试)、SkillHub 技能(升级检测一键升级、装后即时刷新、套餐技能自动同步)、套餐申请、插件管理与自助升级、lark-cli / ccpg-cli 探测与一键安装(请求超时与错误码透传、令牌轮换 single-flight、凭据损坏容错与并发安全写入、状态页 UX 增强、全局中文回复默认(AGENTS.md 受标记管理、永不覆盖用户设置)、桌面版设置页白屏修复(iframe 子框架导航无桌面令牌改走主框架 fetch + srcdoc 渲染)、「关于」页升级中态跨状态轮询保留(点升级不再像没反应、不再并发重复升级)与 toast 常驻定位修复、平台模型同步写进 dsh 活配置层(DSH ≥0.1.7 即时生效不再要重启)与测试污染真实 home 修复)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"smoke": "node scripts/env-matrix-smoke.mjs"
|