dsh-agentone 0.5.12 → 0.5.13
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 +72 -3
- package/lib/page.js +69 -2
- package/lib/proc.js +1 -1
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -32,7 +32,7 @@ import { applyPlatformModel, removePlatformModel } from './model.js';
|
|
|
32
32
|
import { installPlatformSkill, listPlatformSkills, removePlatformSkill } from './skill.js';
|
|
33
33
|
import { platformFetch } from './http.js';
|
|
34
34
|
import { donePageHtml, statusPageHtml } from './page.js';
|
|
35
|
-
import { compareSemver, dshPluginCli, probeCliDetail, pnpmViewLatest, upgradeCliTool } from './proc.js';
|
|
35
|
+
import { compareSemver, dshPluginCli, firstErrorLines, probeCliDetail, pnpmViewLatest, upgradeCliTool } from './proc.js';
|
|
36
36
|
|
|
37
37
|
import { clearCredentials, loadCredentials, resolveDshHome, saveCredentials, updateCredentials } from './store.js';
|
|
38
38
|
|
|
@@ -158,7 +158,7 @@ async function ensureFreshToken(config, log) {
|
|
|
158
158
|
* DSH Desktop 用 desktopProfiles.current(apply 时探测填充)。
|
|
159
159
|
*/
|
|
160
160
|
let PLUGIN_PROFILE = 'web';
|
|
161
|
-
/** desktop 受管插件操作通道(desktopPnpm.runPlugin
|
|
161
|
+
/** desktop 受管插件操作通道(desktopPnpm.runPlugin 包装),非 desktop 为 null。 */
|
|
162
162
|
let desktopPluginCli = null;
|
|
163
163
|
|
|
164
164
|
function pluginProfileDir(config) {
|
|
@@ -173,6 +173,48 @@ async function runPluginCli(args) {
|
|
|
173
173
|
return dshPluginCli(args, PLUGIN_PROFILE);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/**
|
|
177
|
+
* 包装宿主 desktopPnpm.runPlugin,对齐 dshPluginCli 的返回契约(成功
|
|
178
|
+
* {stdout, stderr},失败 throw Error)。宿主契约(dsh-plugin-desktop 的
|
|
179
|
+
* DesktopPnpmService)有两点与直觉不同,历史 bug 均源于此:
|
|
180
|
+
* 1. runPlugin(argv, invokingDir, signal) 的 invokingDir 是必填绝对路径
|
|
181
|
+
* (作为子进程 cwd)——只传 argv 会在宿主内抛
|
|
182
|
+
* "Cannot read properties of undefined (reading 'length')";
|
|
183
|
+
* 2. 返回 {stdout, stderr, done, cancel} 句柄而非 Promise,done 结算
|
|
184
|
+
* {exitCode, signal} 且不因非零退出 reject——直接 await 句柄会立即
|
|
185
|
+
* 返回,装完前的落盘校验必然读到旧版本,误报「升级未生效」。
|
|
186
|
+
*/
|
|
187
|
+
export function desktopRunPlugin(service, invokingDir) {
|
|
188
|
+
return async (args) => {
|
|
189
|
+
const handle = service.runPlugin([...args], invokingDir);
|
|
190
|
+
let stdout = '';
|
|
191
|
+
let stderr = '';
|
|
192
|
+
const append = (acc, chunk) => (acc + String(chunk)).slice(-64 * 1024);
|
|
193
|
+
handle.stdout?.on?.('data', (chunk) => { stdout = append(stdout, chunk); });
|
|
194
|
+
handle.stderr?.on?.('data', (chunk) => { stderr = append(stderr, chunk); });
|
|
195
|
+
// 与 web 通道 runCliTool 同宽的超时(300s):宿主自身不设整体超时
|
|
196
|
+
let timedOut = false;
|
|
197
|
+
const timer = setTimeout(() => {
|
|
198
|
+
timedOut = true;
|
|
199
|
+
handle.cancel();
|
|
200
|
+
}, 300_000);
|
|
201
|
+
try {
|
|
202
|
+
const outcome = await handle.done;
|
|
203
|
+
if (timedOut) throw new Error('命令执行超时,请稍后重试');
|
|
204
|
+
if (outcome.exitCode !== 0 || outcome.signal != null) {
|
|
205
|
+
// ERR_PNPM_IGNORED_BUILDS 等非零退出在这里抛出,由调用侧
|
|
206
|
+
// install/upgrade 的「以落盘版本为准」兜底接管
|
|
207
|
+
const message = firstErrorLines(`${stderr}\n${stdout}`)
|
|
208
|
+
|| `命令执行失败(退出码 ${outcome.exitCode ?? '未知'})`;
|
|
209
|
+
throw new Error(message);
|
|
210
|
+
}
|
|
211
|
+
return { stdout, stderr };
|
|
212
|
+
} finally {
|
|
213
|
+
clearTimeout(timer);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
176
218
|
/**
|
|
177
219
|
* npm 包名白名单(scoped 与非 scoped)。用户输入会进入 CLI 参数,必须先
|
|
178
220
|
* 收敛到 npm 官方命名规则,杜绝以 `-` 开头的值被 dsh/pnpm 当成 flag。
|
|
@@ -586,7 +628,7 @@ export function apply(ctx, config) {
|
|
|
586
628
|
if (desktopProfiles?.current?.name) {
|
|
587
629
|
PLUGIN_PROFILE = desktopProfiles.current.name;
|
|
588
630
|
ctx.inject(['desktopPnpm'], (desktopCtx) => {
|
|
589
|
-
desktopPluginCli = (
|
|
631
|
+
desktopPluginCli = desktopRunPlugin(desktopCtx.desktopPnpm, pluginProfileDir(config));
|
|
590
632
|
log.info(`[dsh-agentone] desktop profile: ${PLUGIN_PROFILE}`);
|
|
591
633
|
});
|
|
592
634
|
}
|
|
@@ -923,6 +965,33 @@ export function apply(ctx, config) {
|
|
|
923
965
|
}
|
|
924
966
|
});
|
|
925
967
|
|
|
968
|
+
register('/agentone/api/plugin/self', async (request, response) => {
|
|
969
|
+
if (request.method !== 'GET') {
|
|
970
|
+
response.writeHead(405, { allow: 'GET' });
|
|
971
|
+
response.end();
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
// 「关于」页的轻量版本接口:落盘已装版本 + registry 实时 latest + 运行
|
|
975
|
+
// 中版本。升级成功后落盘先变、运行代码要重启宿主才换——两者差值即
|
|
976
|
+
// 前端「已安装待重启」提示的依据。
|
|
977
|
+
try {
|
|
978
|
+
const dir = pluginProfileDir(config);
|
|
979
|
+
const [version, latest] = await Promise.all([
|
|
980
|
+
installedVersion(dir, PLUGIN_PACKAGE_NAME),
|
|
981
|
+
pnpmViewLatest(PLUGIN_PACKAGE_NAME, dir).catch(() => null),
|
|
982
|
+
]);
|
|
983
|
+
sendJson(response, 200, {
|
|
984
|
+
name: PLUGIN_PACKAGE_NAME,
|
|
985
|
+
version,
|
|
986
|
+
running: PLUGIN_VERSION,
|
|
987
|
+
latest: latest && version && compareSemver(latest, version) > 0 ? latest : null,
|
|
988
|
+
self_managed: isSelfManaged(),
|
|
989
|
+
});
|
|
990
|
+
} catch (error) {
|
|
991
|
+
sendJson(response, 500, { error: error?.message || '读取插件版本失败' });
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
|
|
926
995
|
register('/agentone/api/cli/status', async (request, response) => {
|
|
927
996
|
if (request.method !== 'GET') {
|
|
928
997
|
response.writeHead(405, { allow: 'GET' });
|
package/lib/page.js
CHANGED
|
@@ -204,6 +204,10 @@ const activateTab = (name) => {
|
|
|
204
204
|
renderPlugins._loaded = true;
|
|
205
205
|
renderPlugins();
|
|
206
206
|
}
|
|
207
|
+
// 关于页版本/升级信息按需拉取(pnpm view 有网络开销,1 分钟内不重复)
|
|
208
|
+
if (name === 'about') {
|
|
209
|
+
refreshAboutSelf();
|
|
210
|
+
}
|
|
207
211
|
// CLI 状态按需探测(auth status 有进程开销,不进 5s 轮询;1 分钟内不重复)
|
|
208
212
|
if (name === 'cli') {
|
|
209
213
|
const now = Date.now();
|
|
@@ -481,7 +485,10 @@ const pluginAction = (name, action, btn) => withLoading(
|
|
|
481
485
|
headers: { 'Content-Type': 'application/json' },
|
|
482
486
|
body: JSON.stringify({ name }),
|
|
483
487
|
}),
|
|
484
|
-
|
|
488
|
+
// 装/升级只落盘,运行中的插件代码要重启 dsh 才换新——成功提示必须带重启说明
|
|
489
|
+
action === 'install' ? '插件已安装,重启 DSH 后生效'
|
|
490
|
+
: action === 'remove' ? '插件已卸载'
|
|
491
|
+
: '插件已升级,重启 DSH 后生效',
|
|
485
492
|
).then(() => renderPlugins());
|
|
486
493
|
const renderPlugins = async () => {
|
|
487
494
|
const list = $('plugins-list');
|
|
@@ -558,14 +565,74 @@ const renderPlugins = async () => {
|
|
|
558
565
|
}).then(() => renderPlugins()), '插件已卸载');
|
|
559
566
|
});
|
|
560
567
|
};
|
|
568
|
+
// 关于页版本行:aboutSelf(GET api/plugin/self)有值时展示升级入口或
|
|
569
|
+
// 「已安装待重启」提示;尚未取到时退化为运行中版本(状态轮询自带字段)。
|
|
570
|
+
let aboutSelf = null;
|
|
571
|
+
const aboutVersionRow = () => {
|
|
572
|
+
const self = aboutSelf;
|
|
573
|
+
if (!self) {
|
|
574
|
+
return '<div class="kv"><span class="k">插件版本</span><span class="v">dsh-agentone v' + esc(lastStatus?.plugin_version || '?') + '</span></div>';
|
|
575
|
+
}
|
|
576
|
+
let value;
|
|
577
|
+
if (self.latest && !self.self_managed) {
|
|
578
|
+
value = 'v' + esc(self.version || '?') + ' → <b>v' + esc(self.latest) + '</b>'
|
|
579
|
+
+ ' <button id="about-upgrade-btn" class="btn small primary" data-loading="升级中…">升级</button>';
|
|
580
|
+
} else if (self.version && self.running && self.version !== self.running) {
|
|
581
|
+
value = 'v' + esc(self.running) + '(运行中)· v' + esc(self.version) + ' 已安装,重启 DSH 后生效';
|
|
582
|
+
} else {
|
|
583
|
+
value = 'v' + esc(self.version || self.running || '?');
|
|
584
|
+
}
|
|
585
|
+
return '<div class="kv"><span class="k">插件版本</span><span class="v">' + value + '</span></div>';
|
|
586
|
+
};
|
|
587
|
+
const wireAboutUpgrade = () => {
|
|
588
|
+
const btn = $('about-upgrade-btn');
|
|
589
|
+
if (!btn || btn.onclick) return;
|
|
590
|
+
btn.onclick = async () => {
|
|
591
|
+
btn.disabled = true;
|
|
592
|
+
btn.innerHTML = '<span class="spin"></span>升级中…';
|
|
593
|
+
try {
|
|
594
|
+
const resp = await fetch('api/plugin/upgrade', {
|
|
595
|
+
method: 'POST',
|
|
596
|
+
headers: { 'Content-Type': 'application/json' },
|
|
597
|
+
body: JSON.stringify({ name: 'dsh-agentone' }),
|
|
598
|
+
});
|
|
599
|
+
const data = await resp.json().catch(() => ({}));
|
|
600
|
+
if (!resp.ok) throw new Error(data.error || '插件升级失败');
|
|
601
|
+
notify('插件已升级,重启 DSH 后生效');
|
|
602
|
+
await refreshAboutSelf(true);
|
|
603
|
+
} catch (error) {
|
|
604
|
+
notify(error.message || '插件升级失败', 'err');
|
|
605
|
+
btn.disabled = false;
|
|
606
|
+
btn.innerHTML = '升级';
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
};
|
|
610
|
+
// 版本 + latest 查询有网络开销(pnpm view 打 registry):1 分钟节流,
|
|
611
|
+
// 切到「关于」tab 或升级完成后刷新
|
|
612
|
+
const refreshAboutSelf = async (force = false) => {
|
|
613
|
+
const now = Date.now();
|
|
614
|
+
if (!force && refreshAboutSelf._at && now - refreshAboutSelf._at < 60000) return;
|
|
615
|
+
refreshAboutSelf._at = now;
|
|
616
|
+
try {
|
|
617
|
+
const resp = await fetch('api/plugin/self');
|
|
618
|
+
const data = await resp.json().catch(() => ({}));
|
|
619
|
+
if (!resp.ok) throw new Error(data.error || '版本查询失败');
|
|
620
|
+
aboutSelf = data;
|
|
621
|
+
} catch {
|
|
622
|
+
return; // 查询失败保持现状,版本行退化为运行中版本
|
|
623
|
+
}
|
|
624
|
+
if (!$('about-body') || document.getElementById('pane-about').hidden) return;
|
|
625
|
+
renderAbout(lastStatus);
|
|
626
|
+
};
|
|
561
627
|
const renderAbout = (s) => {
|
|
562
628
|
const lines = [
|
|
563
|
-
|
|
629
|
+
aboutVersionRow(),
|
|
564
630
|
'<div class="kv"><span class="k">登录账号</span><span class="v">' + esc(s.user_email || '未登录') + '</span></div>',
|
|
565
631
|
'<div class="kv"><span class="k">所属套餐</span><span class="v">' + esc(s.capabilities?.plan_name || '—') + '</span></div>',
|
|
566
632
|
'<div class="kv"><span class="k">平台地址</span><span class="v muted">' + esc(s.platform_url || '—') + '</span></div>',
|
|
567
633
|
];
|
|
568
634
|
$('about-body').innerHTML = lines.join('');
|
|
635
|
+
wireAboutUpgrade();
|
|
569
636
|
};
|
|
570
637
|
const searchCatalog = async () => {
|
|
571
638
|
const btn = $('search-btn');
|
package/lib/proc.js
CHANGED
|
@@ -66,7 +66,7 @@ function execResolved(tool, args, { timeoutMs, cwd, env } = {}) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/** 从 pnpm 冗长输出里提取对用户有意义的错误行。 */
|
|
69
|
-
function firstErrorLines(raw) {
|
|
69
|
+
export function firstErrorLines(raw) {
|
|
70
70
|
const lines = String(raw).split('\n').map((line) => line.trim()).filter(Boolean);
|
|
71
71
|
const meaningful = lines.filter(
|
|
72
72
|
(line) => /ERR_|error|ERROR|404|ENOTFOUND|ETIMEDOUT|ECONNREFUSED/.test(line)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agentone",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "AgentOne 平台集成插件:飞书登录、平台模型、SkillHub
|
|
3
|
+
"version": "0.5.13",
|
|
4
|
+
"description": "AgentOne 平台集成插件:飞书登录、平台模型、SkillHub 技能、套餐申请、插件管理与自助升级、lark-cli / ccpg-cli 探测(请求超时与错误码透传、令牌轮换 single-flight、凭据损坏容错、状态页 UX 增强)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|