dsh-agentone 0.5.5 → 0.5.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/lib/index.js +81 -42
- package/lib/page.js +40 -3
- package/lib/proc.js +93 -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,
|
|
35
|
+
import { dshPluginCli, probeCliDetail, pnpmViewLatest, upgradeCliTool } from './proc.js';
|
|
36
36
|
|
|
37
37
|
import { clearCredentials, loadCredentials, resolveDshHome, saveCredentials, updateCredentials } from './store.js';
|
|
38
38
|
|
|
@@ -218,7 +218,22 @@ function specLabel(spec) {
|
|
|
218
218
|
return '';
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
/** dsh
|
|
221
|
+
/** dsh 插件与 CLI 操作白名单:用户输入不得进入 execFile 的 tool 位。 */
|
|
222
|
+
const CLI_TOOLS = new Set(['lark-cli', 'ccpg-cli']);
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 插件自身是否由平台托管镜像管理:镜像 Dockerfile 固有 ENV
|
|
226
|
+
* `DSH_FORWARDER_LISTEN_PORT`(forwarder 是镜像特有组件,独立桌面版不存在)。
|
|
227
|
+
* 托管镜像内插件版本随镜像走,禁自助升级;独立 dsh(用户自装)没有镜像
|
|
228
|
+
* 通道,允许在插件页自助升级 —— 否则用户会被卡在旧版只能命令行破局。
|
|
229
|
+
*/
|
|
230
|
+
function isSelfManaged() {
|
|
231
|
+
return Boolean(process.env.DSH_FORWARDER_LISTEN_PORT);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* dsh 官方 in-box bundle:profile 模板自带,不是用户可管理的插件。
|
|
236
|
+
*/
|
|
222
237
|
const INBOX_BUNDLES = new Set([
|
|
223
238
|
'@deepseek-ai/dsh-base',
|
|
224
239
|
'@deepseek-ai/dsh-web-app',
|
|
@@ -234,25 +249,6 @@ const INBOX_BUNDLES = new Set([
|
|
|
234
249
|
*/
|
|
235
250
|
const BUILTIN_PLUGINS = ['dsh-better-sidebar', 'dsh-harness-one', 'dshmarket', 'dsh-im'];
|
|
236
251
|
|
|
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
252
|
/**
|
|
257
253
|
* 内置插件的用途说明(以各自官方 README 为准):用户视角讲清楚装了能
|
|
258
254
|
* 干什么、在哪里用到。
|
|
@@ -293,7 +289,7 @@ async function listProfilePlugins(config) {
|
|
|
293
289
|
active: bundle && bundles.includes(name),
|
|
294
290
|
builtin: BUILTIN_PLUGINS.includes(name),
|
|
295
291
|
// 自身随平台镜像升级:前端据此隐藏卸载/升级按钮(与后端拒绝逻辑同源)
|
|
296
|
-
self_managed: name === PLUGIN_PACKAGE_NAME,
|
|
292
|
+
self_managed: name === PLUGIN_PACKAGE_NAME && isSelfManaged(),
|
|
297
293
|
description: BUILTIN_PLUGIN_DESCRIPTIONS[name] || null,
|
|
298
294
|
});
|
|
299
295
|
}
|
|
@@ -356,16 +352,36 @@ async function removeProfilePlugin(config, name) {
|
|
|
356
352
|
return listProfilePlugins(config);
|
|
357
353
|
}
|
|
358
354
|
|
|
359
|
-
/** 升级到
|
|
355
|
+
/** 升级到 registry 最新版:view 查实时 latest → 显式版本号 add(会重写
|
|
356
|
+
* package.json spec)→ 校验落盘版本。不能用 `dsh plugin update`(即
|
|
357
|
+
* `pnpm update`):它只在 spec range 内升级,`dsh plugin add` 写入的精确
|
|
358
|
+
* pin(如 "0.19.1")下永远是 no-op,却返回成功——历史上「点升级提示已
|
|
359
|
+
* 升级、版本永不变」的根因。 */
|
|
360
360
|
async function upgradeProfilePlugin(config, name) {
|
|
361
361
|
assertValidPackage(name);
|
|
362
362
|
if (INBOX_BUNDLES.has(name)) {
|
|
363
363
|
throw new Error('dsh 内置组件随 dsh 一同升级,请等待平台更新 dsh');
|
|
364
364
|
}
|
|
365
|
-
if (name === PLUGIN_PACKAGE_NAME) {
|
|
365
|
+
if (name === PLUGIN_PACKAGE_NAME && isSelfManaged()) {
|
|
366
366
|
throw new Error('AgentOne 插件随平台镜像一同升级,无需手动操作');
|
|
367
367
|
}
|
|
368
|
-
|
|
368
|
+
const dir = pluginProfileDir(config);
|
|
369
|
+
const [latest, current] = await Promise.all([
|
|
370
|
+
pnpmViewLatest(name, dir).catch(() => null),
|
|
371
|
+
installedVersion(dir, name),
|
|
372
|
+
]);
|
|
373
|
+
if (!latest) {
|
|
374
|
+
throw new Error('查询插件最新版本失败,请稍后重试');
|
|
375
|
+
}
|
|
376
|
+
if (latest === current) {
|
|
377
|
+
throw new Error(`已是最新版本(v${latest})`);
|
|
378
|
+
}
|
|
379
|
+
await runPluginCli(['add', `${name}@${latest}`, ...(BUILTIN_PLUGIN_ALLOW_BUILD[name] || [])]);
|
|
380
|
+
const after = await installedVersion(dir, name);
|
|
381
|
+
if (after !== latest) {
|
|
382
|
+
// 兑现「插件已升级」提示的前置校验:没升上去就明说,不再假报成功
|
|
383
|
+
throw new Error(`升级未生效(当前 v${after ?? '未安装'},目标 v${latest}),请稍后重试`);
|
|
384
|
+
}
|
|
369
385
|
return listProfilePlugins(config);
|
|
370
386
|
}
|
|
371
387
|
|
|
@@ -825,20 +841,19 @@ export function apply(ctx, config) {
|
|
|
825
841
|
return;
|
|
826
842
|
}
|
|
827
843
|
try {
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
844
|
+
const state = await listProfilePlugins(config);
|
|
845
|
+
// latest 逐包实时查询(pnpm view <name>@latest version,并行):
|
|
846
|
+
// 不用 pnpm outdated——它受元数据缓存与 minimumReleaseAge 冷却期
|
|
847
|
+
// 影响,新发布的版本一段时间内查不到,升级按钮追着发版延迟出现。
|
|
848
|
+
// 只查已装且来自 npm 的包;未安装/本地链接没有版本可查。
|
|
849
|
+
const dir = pluginProfileDir(config);
|
|
850
|
+
await Promise.all(
|
|
851
|
+
state.plugins
|
|
852
|
+
.filter((item) => item.version !== null && item.source === 'npm')
|
|
853
|
+
.map(async (item) => {
|
|
854
|
+
item.latest = await pnpmViewLatest(item.name, dir).catch(() => null);
|
|
855
|
+
}),
|
|
837
856
|
);
|
|
838
|
-
for (const plugin of state.plugins) {
|
|
839
|
-
const spread = outdated[plugin.name];
|
|
840
|
-
plugin.latest = spread ? spread.latest : null;
|
|
841
|
-
}
|
|
842
857
|
sendJson(response, 200, state);
|
|
843
858
|
} catch (error) {
|
|
844
859
|
sendJson(response, 500, { error: error?.message || '读取插件列表失败' });
|
|
@@ -900,15 +915,39 @@ export function apply(ctx, config) {
|
|
|
900
915
|
response.end();
|
|
901
916
|
return;
|
|
902
917
|
}
|
|
903
|
-
// 真实探测本机 CLI
|
|
904
|
-
//
|
|
918
|
+
// 真实探测本机 CLI:三态与平台 cli-deployd 判定口径一致,附带版本
|
|
919
|
+
// 与最新版(查询失败降级 null,不阻塞三态)。并发探测,单个失败不影
|
|
920
|
+
// 响另一个。
|
|
905
921
|
const [lark, ccpg] = await Promise.all([
|
|
906
|
-
|
|
907
|
-
|
|
922
|
+
probeCliDetail('lark-cli').catch(() => ({ state: 'missing', version: null, latest: null })),
|
|
923
|
+
probeCliDetail('ccpg-cli').catch(() => ({ state: 'missing', version: null, latest: null })),
|
|
908
924
|
]);
|
|
909
925
|
sendJson(response, 200, { tools: { 'lark-cli': lark, 'ccpg-cli': ccpg } });
|
|
910
926
|
});
|
|
911
927
|
|
|
928
|
+
register('/agentone/api/cli/upgrade', async (request, response) => {
|
|
929
|
+
if (request.method !== 'POST') {
|
|
930
|
+
response.writeHead(405, { allow: 'POST' });
|
|
931
|
+
response.end();
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
try {
|
|
935
|
+
const body = await readJsonBody(request);
|
|
936
|
+
const tool = String(body.tool || '');
|
|
937
|
+
if (!CLI_TOOLS.has(tool)) {
|
|
938
|
+
sendJson(response, 400, { error: '不支持的 CLI 工具' });
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
// 官方自升级(ccpg 下载二进制可达分钟级),输出尾部透传给前端
|
|
942
|
+
// (lark-cli 手动安装时含 GitHub 下载地址)
|
|
943
|
+
const output = await upgradeCliTool(tool);
|
|
944
|
+
const detail = await probeCliDetail(tool).catch(() => null);
|
|
945
|
+
sendJson(response, 200, { ok: true, output, tool: detail });
|
|
946
|
+
} catch (error) {
|
|
947
|
+
sendJson(response, 502, { error: error?.message || 'CLI 升级失败,请稍后重试' });
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
|
|
912
951
|
register('/agentone/api/plan', async (request, response) => {
|
|
913
952
|
if (request.method !== 'GET') {
|
|
914
953
|
response.writeHead(405, { allow: 'GET' });
|
package/lib/page.js
CHANGED
|
@@ -423,19 +423,56 @@ const renderCli = async (s) => {
|
|
|
423
423
|
return;
|
|
424
424
|
}
|
|
425
425
|
const rows = Object.entries(CLI_TOOL_META).map(([name, meta]) => {
|
|
426
|
-
const
|
|
426
|
+
const info = tools[name] || { state: 'missing' };
|
|
427
|
+
const state = info.state || 'missing';
|
|
427
428
|
const status = state === 'authorized'
|
|
428
429
|
? '<span class="pill ok" style="font-size:11px">已授权</span>'
|
|
429
430
|
: state === 'unauthorized'
|
|
430
431
|
? '<span class="pill warn" style="font-size:11px">未授权</span>'
|
|
431
432
|
: '<span class="pill warn" style="font-size:11px">未安装</span>';
|
|
433
|
+
// 版本展示:当前版 · 有新版时「v旧 → v新」+ 升级按钮(查询失败静默退化)
|
|
434
|
+
const versionLine = meta.desc + ' · ' + meta.label
|
|
435
|
+
+ (info.version ? ' · v' + esc(info.version) : '')
|
|
436
|
+
+ (info.latest && info.latest !== info.version
|
|
437
|
+
? ' → <b>v' + esc(info.latest) + '</b>'
|
|
438
|
+
: '');
|
|
439
|
+
const upgradable = info.latest && info.latest !== info.version;
|
|
440
|
+
const actions = upgradable
|
|
441
|
+
? '<button class="btn small primary" data-cli-upgrade="' + esc(name) + '">升级</button>'
|
|
442
|
+
: '';
|
|
432
443
|
return '<div class="skill-item"><div class="skill-info"><div class="skill-name">'
|
|
433
444
|
+ esc(name) + status + '</div><div class="skill-sub">'
|
|
434
|
-
+
|
|
445
|
+
+ versionLine + '</div></div><div>' + actions + '</div></div>';
|
|
435
446
|
});
|
|
436
447
|
body.innerHTML = rows.join('')
|
|
437
448
|
+ '<div class="hint">在对话中直接使用(如 /lark 帮助),由 dsh 自动调用。</div>'
|
|
438
|
-
+ '<div class="hint"
|
|
449
|
+
+ '<div class="hint">未安装/未授权时,请联系管理员在镜像或本机完成安装与授权;有新版本时可在此一键升级。</div>';
|
|
450
|
+
body.querySelectorAll('[data-cli-upgrade]').forEach((btn) => {
|
|
451
|
+
btn.onclick = () => cliUpgrade(btn.dataset.cliUpgrade, btn);
|
|
452
|
+
});
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/** CLI 一键升级:官方 tool update(ccpg 下载二进制可达分钟级)。 */
|
|
456
|
+
const cliUpgrade = async (tool, btn) => {
|
|
457
|
+
btn.disabled = true;
|
|
458
|
+
btn.textContent = '升级中,可能需要 1–2 分钟…';
|
|
459
|
+
try {
|
|
460
|
+
const resp = await fetch('api/cli/upgrade', {
|
|
461
|
+
method: 'POST',
|
|
462
|
+
headers: { 'Content-Type': 'application/json' },
|
|
463
|
+
body: JSON.stringify({ tool }),
|
|
464
|
+
});
|
|
465
|
+
const data = await resp.json().catch(() => ({}));
|
|
466
|
+
if (!resp.ok) throw new Error(data.error || 'CLI 升级失败');
|
|
467
|
+
// lark-cli 手动安装时 update 只会打印下载地址,原样展示给用户
|
|
468
|
+
if (data.output) alert(data.output);
|
|
469
|
+
renderCli._probedAt = 0;
|
|
470
|
+
renderCli(lastStatus);
|
|
471
|
+
} catch (error) {
|
|
472
|
+
alert(error.message || 'CLI 升级失败,请稍后重试');
|
|
473
|
+
btn.disabled = false;
|
|
474
|
+
btn.textContent = '升级';
|
|
475
|
+
}
|
|
439
476
|
};
|
|
440
477
|
const pluginAction = (name, action, btn) => withLoading(
|
|
441
478
|
btn,
|
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 子命令(当前仅
|
|
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 的判定口径一致:
|
|
@@ -65,7 +89,29 @@ export function pnpmCli(args, timeoutMs, cwd) {
|
|
|
65
89
|
* ∈ {valid, needs_refresh}(access 过期但 refresh 可用仍算已授权)
|
|
66
90
|
*/
|
|
67
91
|
export async function probeCliTool(tool, { timeoutMs = 15000 } = {}) {
|
|
92
|
+
const detail = await probeCliDetail(tool, { timeoutMs });
|
|
93
|
+
return detail.state;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** --version 输出解析:提取首个 x.y[.z…] 版本号(两 CLI 均为
|
|
97
|
+
* "xxx version 1.2.3" 形态)。 */
|
|
98
|
+
export function parseCliVersion(stdout) {
|
|
99
|
+
const match = /(\d+\.\d+(?:\.\d+)*)/.exec(String(stdout || ''));
|
|
100
|
+
return match ? match[1] : null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 三态探测 + 版本 + 最新版(任一失败降级为 null,不阻塞三态):
|
|
105
|
+
* version: `tool --version`
|
|
106
|
+
* latest: ccpg-cli 走官方 `update --check --json`(结构化,为 agent
|
|
107
|
+
* 设计);lark-cli 无 check 子命令,用 `npm view @larksuite/cli
|
|
108
|
+
* version`(版本号全局一致,对 npm/pnpm/手动安装都成立)。
|
|
109
|
+
* npm view 需要 npm 在 PATH(node 环境下成立);查询超时/失败返回 null,
|
|
110
|
+
* 前端只显示当前版本不显示升级按钮。
|
|
111
|
+
*/
|
|
112
|
+
export async function probeCliDetail(tool, { timeoutMs = 15000 } = {}) {
|
|
68
113
|
let stdout;
|
|
114
|
+
let state = 'unauthorized';
|
|
69
115
|
try {
|
|
70
116
|
({ stdout } = await execFileAsync(tool, ['auth', 'status'], {
|
|
71
117
|
timeout: timeoutMs,
|
|
@@ -73,11 +119,55 @@ export async function probeCliTool(tool, { timeoutMs = 15000 } = {}) {
|
|
|
73
119
|
env: tool === 'ccpg-cli' ? { ...process.env, CCPG_FULL_CLI: '1' } : process.env,
|
|
74
120
|
}));
|
|
75
121
|
} catch (error) {
|
|
76
|
-
if (error.code === 'ENOENT') return 'missing';
|
|
122
|
+
if (error.code === 'ENOENT') return { state: 'missing', version: null, latest: null };
|
|
77
123
|
// 探测失败不区分超时/非零退出:统一按已装但状态未知处理
|
|
78
124
|
stdout = `${error.stdout || ''}`;
|
|
79
125
|
}
|
|
80
|
-
|
|
126
|
+
if (larkStyleAuthorized(stdout) || ccpgStyleAuthorized(stdout)) state = 'authorized';
|
|
127
|
+
|
|
128
|
+
const [version, latest] = await Promise.all([
|
|
129
|
+
execFileAsync(tool, ['--version'], { timeout: 10_000, killSignal: 'SIGKILL' })
|
|
130
|
+
.then((r) => parseCliVersion(r.stdout))
|
|
131
|
+
.catch(() => null),
|
|
132
|
+
cliLatestVersion(tool, timeoutMs).catch(() => null),
|
|
133
|
+
]);
|
|
134
|
+
return { state, version, latest };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 最新版查询:ccpg-cli 官方 check 接口;lark-cli 查 npm 包 dist-tag。 */
|
|
138
|
+
async function cliLatestVersion(tool, timeoutMs) {
|
|
139
|
+
if (tool === 'ccpg-cli') {
|
|
140
|
+
const { stdout } = await execFileAsync(
|
|
141
|
+
tool,
|
|
142
|
+
['update', '--check', '--json'],
|
|
143
|
+
{ timeout: timeoutMs, killSignal: 'SIGKILL', env: { ...process.env, CCPG_FULL_CLI: '1' } },
|
|
144
|
+
);
|
|
145
|
+
const data = JSON.parse(String(stdout).trim());
|
|
146
|
+
return typeof data?.latest_version === 'string' ? data.latest_version : null;
|
|
147
|
+
}
|
|
148
|
+
if (tool === 'lark-cli') {
|
|
149
|
+
const { stdout } = await execFileAsync(
|
|
150
|
+
'npm',
|
|
151
|
+
['view', '@larksuite/cli', 'version'],
|
|
152
|
+
{ timeout: timeoutMs, killSignal: 'SIGKILL' },
|
|
153
|
+
);
|
|
154
|
+
return parseCliVersion(stdout);
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 触发 CLI 官方自升级(`tool update`):ccpg-cli 从更新服务器下载二进制
|
|
161
|
+
* (~17MB),lark-cli 自动探测 npm/pnpm/手动安装方式(手动时只打印
|
|
162
|
+
* GitHub 下载地址)。返回输出尾部给前端展示(含手动安装的下载链接)。
|
|
163
|
+
*/
|
|
164
|
+
export async function upgradeCliTool(tool, { timeoutMs = 300_000 } = {}) {
|
|
165
|
+
const { stdout } = await runCliTool(tool, ['update'], {
|
|
166
|
+
timeoutMs,
|
|
167
|
+
env: tool === 'ccpg-cli' ? { ...process.env, CCPG_FULL_CLI: '1' } : process.env,
|
|
168
|
+
});
|
|
169
|
+
const lines = String(stdout).split('\n').map((l) => l.trim()).filter(Boolean);
|
|
170
|
+
return lines.slice(-6).join('\n');
|
|
81
171
|
}
|
|
82
172
|
|
|
83
173
|
/** lark-cli 形态:identities 下的 tokenStatus 或新版 status/available 组合。 */
|
package/package.json
CHANGED