dsh-agentone 0.5.4 → 0.5.6

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.
Files changed (3) hide show
  1. package/lib/index.js +38 -37
  2. package/lib/proc.js +25 -1
  3. package/package.json +1 -1
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 { dshPluginCli, probeCliTool, pnpmCli } from './proc.js';
35
+ import { dshPluginCli, pnpmViewLatest, probeCliTool } from './proc.js';
36
36
 
37
37
  import { clearCredentials, loadCredentials, resolveDshHome, saveCredentials, updateCredentials } from './store.js';
38
38
 
@@ -234,25 +234,6 @@ const INBOX_BUNDLES = new Set([
234
234
  */
235
235
  const BUILTIN_PLUGINS = ['dsh-better-sidebar', 'dsh-harness-one', 'dshmarket', 'dsh-im'];
236
236
 
237
- /**
238
- * 解析 pnpm outdated 输出:两行一组,包名行 + "current => latest" 行。
239
- * 输出格式随 pnpm 版本变化:解析出的条目必须满足「上一行是已知的插件
240
- * 包名」才采纳,格式不认识时返回空表(所有 latest 为 null,「升级」按钮
241
- * 整体不出现),而不是错配出假版本号。
242
- */
243
- function parseOutdatedList(stdout, knownNames = []) {
244
- const result = {};
245
- const known = new Set(knownNames);
246
- const rows = String(stdout).split('\n').map((line) => line.trim()).filter(Boolean);
247
- rows.forEach((line, i) => {
248
- const match = i > 0 ? /^(\S+)\s+=>\s+(\S+)$/.exec(line) : null;
249
- if (match && (!known.size || known.has(rows[i - 1]))) {
250
- result[rows[i - 1]] = { current: match[1], latest: match[2] };
251
- }
252
- });
253
- return result;
254
- }
255
-
256
237
  /**
257
238
  * 内置插件的用途说明(以各自官方 README 为准):用户视角讲清楚装了能
258
239
  * 干什么、在哪里用到。
@@ -356,7 +337,11 @@ async function removeProfilePlugin(config, name) {
356
337
  return listProfilePlugins(config);
357
338
  }
358
339
 
359
- /** 升级到 manifest 区间内最新版:dsh plugin update;in-box dsh 本体升级。 */
340
+ /** 升级到 registry 最新版:view 查实时 latest 显式版本号 add(会重写
341
+ * package.json spec)→ 校验落盘版本。不能用 `dsh plugin update`(即
342
+ * `pnpm update`):它只在 spec range 内升级,`dsh plugin add` 写入的精确
343
+ * pin(如 "0.19.1")下永远是 no-op,却返回成功——历史上「点升级提示已
344
+ * 升级、版本永不变」的根因。 */
360
345
  async function upgradeProfilePlugin(config, name) {
361
346
  assertValidPackage(name);
362
347
  if (INBOX_BUNDLES.has(name)) {
@@ -365,7 +350,23 @@ async function upgradeProfilePlugin(config, name) {
365
350
  if (name === PLUGIN_PACKAGE_NAME) {
366
351
  throw new Error('AgentOne 插件随平台镜像一同升级,无需手动操作');
367
352
  }
368
- await runPluginCli(['update', name]);
353
+ const dir = pluginProfileDir(config);
354
+ const [latest, current] = await Promise.all([
355
+ pnpmViewLatest(name, dir).catch(() => null),
356
+ installedVersion(dir, name),
357
+ ]);
358
+ if (!latest) {
359
+ throw new Error('查询插件最新版本失败,请稍后重试');
360
+ }
361
+ if (latest === current) {
362
+ throw new Error(`已是最新版本(v${latest})`);
363
+ }
364
+ await runPluginCli(['add', `${name}@${latest}`, ...(BUILTIN_PLUGIN_ALLOW_BUILD[name] || [])]);
365
+ const after = await installedVersion(dir, name);
366
+ if (after !== latest) {
367
+ // 兑现「插件已升级」提示的前置校验:没升上去就明说,不再假报成功
368
+ throw new Error(`升级未生效(当前 v${after ?? '未安装'},目标 v${latest}),请稍后重试`);
369
+ }
369
370
  return listProfilePlugins(config);
370
371
  }
371
372
 
@@ -545,7 +546,7 @@ button{font:inherit;padding:9px 22px;border-radius:10px;border:none;cursor:point
545
546
  * location.search 不含宿主 query,页面自检在设置 iframe 场景会漏。
546
547
  */
547
548
  function isDesktopHost() {
548
- return /DSH Desktop\.app/i.test(process.execPath);
549
+ return /DSH Desktop\.(?:app|exe)/i.test(process.execPath);
549
550
  }
550
551
 
551
552
  export function apply(ctx, config) {
@@ -554,6 +555,7 @@ export function apply(ctx, config) {
554
555
  // 只在 DSH Desktop 存在。据此把插件管理切到当前 profile,并用受管
555
556
  // desktopPnpm 做装/卸/升级(desktop 里 dsh CLI 拒绝操作 desktop profile)。
556
557
  const desktopProfiles = ctx.get?.('desktopProfiles');
558
+ const isDesktopRuntime = Boolean(desktopProfiles?.current?.name) || isDesktopHost();
557
559
  if (desktopProfiles?.current?.name) {
558
560
  PLUGIN_PROFILE = desktopProfiles.current.name;
559
561
  ctx.inject(['desktopPnpm'], (desktopCtx) => {
@@ -643,7 +645,7 @@ export function apply(ctx, config) {
643
645
  // DSH Desktop:宿主 webserver 只信 Electron 内部请求,外部浏览器访问
644
646
  // /agentone/callback 会 403。desktop 环境(服务端自检 execPath 或页面
645
647
  // 传 desktop 标记)改由插件在本进程起一次性本地服务收回调。
646
- const callbackUrl = body.desktop || isDesktopHost()
648
+ const callbackUrl = body.desktop || isDesktopRuntime
647
649
  ? await openCallbackServer(config, log)
648
650
  : String(body.callback_url || '');
649
651
  if (!/^https?:\/\/|^\/portal\//.test(callbackUrl)) {
@@ -824,20 +826,19 @@ export function apply(ctx, config) {
824
826
  return;
825
827
  }
826
828
  try {
827
- const [state, outdatedStdout] = await Promise.all([
828
- listProfilePlugins(config),
829
- pnpmCli(['outdated', '--format', 'list'], 60000, pluginProfileDir(config))
830
- .then((r) => r.stdout)
831
- .catch(() => ''),
832
- ]);
833
- const outdated = parseOutdatedList(
834
- outdatedStdout,
835
- state.plugins.map((plugin) => plugin.name),
829
+ const state = await listProfilePlugins(config);
830
+ // latest 逐包实时查询(pnpm view <name>@latest version,并行):
831
+ // 不用 pnpm outdated——它受元数据缓存与 minimumReleaseAge 冷却期
832
+ // 影响,新发布的版本一段时间内查不到,升级按钮追着发版延迟出现。
833
+ // 只查已装且来自 npm 的包;未安装/本地链接没有版本可查。
834
+ const dir = pluginProfileDir(config);
835
+ await Promise.all(
836
+ state.plugins
837
+ .filter((item) => item.version !== null && item.source === 'npm')
838
+ .map(async (item) => {
839
+ item.latest = await pnpmViewLatest(item.name, dir).catch(() => null);
840
+ }),
836
841
  );
837
- for (const plugin of state.plugins) {
838
- const spread = outdated[plugin.name];
839
- plugin.latest = spread ? spread.latest : null;
840
- }
841
842
  sendJson(response, 200, state);
842
843
  } catch (error) {
843
844
  sendJson(response, 500, { error: error?.message || '读取插件列表失败' });
package/lib/proc.js CHANGED
@@ -51,11 +51,35 @@ export function dshPluginCli(args, profile = 'web') {
51
51
  return runCliTool('dsh', ['plugin', '--profile', profile, ...args]);
52
52
  }
53
53
 
54
- /** pnpm 子命令(当前仅 outdated 查询);cwd 指向目标 profile 目录。 */
54
+ /** pnpm 子命令(当前仅 view 查询);cwd 指向目标 profile 目录。 */
55
55
  export function pnpmCli(args, timeoutMs, cwd) {
56
56
  return runCliTool('pnpm', args, { timeoutMs, cwd });
57
57
  }
58
58
 
59
+ /** pnpm view 输出里挑出第一个「纯版本号」行。pnpm 偶尔附加提示行(如
60
+ * "If you need the full list..."),只有语义化版本行才是我们要的 latest。 */
61
+ export function parseLatestVersionLine(stdout) {
62
+ const VERSION_LINE_RE = /^\d+(?:\.\d+){0,3}(?:[-+][a-z0-9._-]+)?$/i;
63
+ for (const line of String(stdout).split('\n')) {
64
+ const trimmed = line.trim();
65
+ if (VERSION_LINE_RE.test(trimmed)) return trimmed;
66
+ }
67
+ return null;
68
+ }
69
+
70
+ /**
71
+ * 查询包在 registry 的最新版本(`pnpm view <name>@latest version`)。
72
+ * 为什么不用 `pnpm outdated` / `update -L` / `add pkg@latest`:实测 pnpm 11
73
+ * 里 outdated 受 dist-tag 元数据缓存与 minimumReleaseAge 冷却期影响(新版
74
+ * 发布后一段时间查不到),update -L 对精确 pin 的依赖是 no-op,add@latest
75
+ * 会解析到缓存旧版;只有显式版本号可用。view 单字段查询每次真实打
76
+ * registry,是这里唯一的实时数据源。查询失败返回 null(前端按钮不出现)。
77
+ */
78
+ export async function pnpmViewLatest(name, cwd) {
79
+ const { stdout } = await pnpmCli(['view', `${name}@latest`, 'version'], 30000, cwd);
80
+ return parseLatestVersionLine(stdout);
81
+ }
82
+
59
83
  /**
60
84
  * 探测本机 CLI 工具状态。返回 'missing'(未安装)/ 'authorized' /
61
85
  * 'unauthorized'(已装未授权)。与平台 instance_service 的判定口径一致:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agentone",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "AgentOne 平台集成插件:飞书登录、平台模型、SkillHub 技能、套餐申请、插件管理、lark-cli / ccpg-cli 探测(请求超时与错误码透传、令牌轮换 single-flight、凭据损坏容错、状态页 UX 增强)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",