@noob-stupid/dsh-plugin-console 0.5.94 → 0.5.97
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 +397 -0
- package/lib/server/domain/framework-cleanup.js +52 -1
- package/lib/server/domain/framework-relaunch.js +462 -0
- package/lib/server/domain/framework-stale-repair.js +31 -0
- package/lib/server/domain/framework.js +23 -13
- package/lib/server/domain/patch-yaml-check.js +10 -0
- package/lib/server/infra/fw-integrity-check.js +5 -1
- package/lib/server/routes/compat.js +14 -3
- package/lib/server/routes/framework-upgrade.js +20 -19
- package/lib/server/routes/framework.js +11 -5
- package/package.json +1 -1
|
@@ -39,6 +39,17 @@ import { preflightRoots } from './framework-preflight.js'
|
|
|
39
39
|
|
|
40
40
|
const PREFLIGHT_TIMEOUT_ENV = 'DSH_AUTO_PREFLIGHT_TIMEOUT_MS'
|
|
41
41
|
/** 自动预检的预算:到点降级成「未完成,可手动重跑」,状态查询照常返回(env 可调)。 */
|
|
42
|
+
/**
|
|
43
|
+
* 安全启动的命令行兜底:拼成**可直接复制运行**的绝对路径命令。
|
|
44
|
+
* 现场问题(2026-10-29):原先给的是相对路径 `node scripts/safe-boot.mjs --list`,
|
|
45
|
+
* 只有先 cd 到插件安装目录才成立 —— 而那是 node_modules 里的深层哈希目录,
|
|
46
|
+
* 服务起不来、界面打不开的时候,用户根本无从找到它。救火命令必须是能直接粘的。
|
|
47
|
+
*/
|
|
48
|
+
function safeBootCommand(args = []) {
|
|
49
|
+
const script = join(pluginRoot(), 'scripts', 'safe-boot.mjs')
|
|
50
|
+
return ['node', JSON.stringify(script), ...args].join(' ')
|
|
51
|
+
}
|
|
52
|
+
|
|
42
53
|
function preflightTimeoutMs() {
|
|
43
54
|
const raw = Number(process.env[PREFLIGHT_TIMEOUT_ENV])
|
|
44
55
|
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_TIMEOUT_MS
|
|
@@ -218,7 +229,7 @@ function autoDisableNotice(entry = lastAutoDisable, { enabled = null } = {}) {
|
|
|
218
229
|
if (entry === null || entry === undefined) return null
|
|
219
230
|
if (on !== true && entry.code === 'auto-disabled') return null
|
|
220
231
|
const added = Array.isArray(entry.disabled) ? entry.disabled : []
|
|
221
|
-
const restore = '
|
|
232
|
+
const restore = safeBootCommand(['--restore-last-good'])
|
|
222
233
|
if (added.length > 0) {
|
|
223
234
|
return {
|
|
224
235
|
at: entry.at ?? null,
|
|
@@ -318,7 +329,7 @@ async function routeCompatStatusGet(req, res, rc) {
|
|
|
318
329
|
onEvidence: readCompatGate().autoDisableOnEvidence === true,
|
|
319
330
|
switch: { file: 'compat-gate.json', key: 'autoDisableOnEvidence', route: '/plugin-console/compat-ack', body: { autoDisableOnEvidence: false } },
|
|
320
331
|
notice: autoDisableNotice(),
|
|
321
|
-
restore: { route: { path: '/plugin-console/safe-boot', body: { action: 'restore-last-good' } }, command: '
|
|
332
|
+
restore: { route: { path: '/plugin-console/safe-boot', body: { action: 'restore-last-good' } }, command: safeBootCommand(['--restore-last-good']) },
|
|
322
333
|
logHint: '记录落在 dshHome()/plugin-console/auto-disable.log(追加式 JSONL)',
|
|
323
334
|
},
|
|
324
335
|
// D2 加法字段:安全启动材料(last-known-good 快照 + 恢复入口提示)
|
|
@@ -326,7 +337,7 @@ async function routeCompatStatusGet(req, res, rc) {
|
|
|
326
337
|
snapshot: { written: snapshot.written === true, reason: snapshot.reason ?? null, id: snapshot.snapshot?.id ?? null, at: snapshot.snapshot?.at ?? null, sha256: snapshot.snapshot?.patchSha256 ?? null },
|
|
327
338
|
snapshots: listSnapshots().slice(0, 5),
|
|
328
339
|
lastGoodId: readSafeBootState().lastGoodId,
|
|
329
|
-
command: { list: '
|
|
340
|
+
command: { list: safeBootCommand(['--list']), restore: safeBootCommand(['--restore-last-good']), disable: safeBootCommand(['--disable-suspects']) },
|
|
330
341
|
route: { action: 'restore-last-good | disable-suspects | list | mark-good', path: '/plugin-console/safe-boot' },
|
|
331
342
|
},
|
|
332
343
|
})
|
|
@@ -11,7 +11,9 @@ import { pathToFileURL } from 'node:url'
|
|
|
11
11
|
import { createRequire } from 'node:module'
|
|
12
12
|
import { readCompatMode, writeCompatMode } from '../domain/compat-state.js'
|
|
13
13
|
import { FRAMEWORK_BACKUP_ROOT, backupProfileSnapshot, checkpointFrameworkTree, platformUpgradeRefusal, preflightDisableIncompatible, relaunchPrelude, resolveDshBin, resolveFrameworkRootNodeModules, resolveFrameworkUpgradePlan, shellHostedRefusal } from '../domain/framework.js'
|
|
14
|
+
import { relaunchLauncherSource, relaunchVerdictBlock, relaunchVerdictHelperSource } from '../domain/framework-relaunch.js'
|
|
14
15
|
import { fwIntegrityCheck } from '../infra/fw-integrity-check.js'
|
|
16
|
+
import { repairStaleRenameResidue } from '../domain/framework-stale-repair.js'
|
|
15
17
|
import { CORE_PATCH_ROW_IDS } from '../domain/patch.js'
|
|
16
18
|
import { migrateAgentConfigsForUpgrade } from '../domain/presets.js'
|
|
17
19
|
import { planQuarantine } from '../domain/quarantine.js'
|
|
@@ -191,6 +193,7 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
191
193
|
sendError(res, 500, `无法定位框架安装根(期望含 .pnpm 与 @deepseek-ai/dsh 的顶层 node_modules,实际:${fwRoot ?? '未找到'})——已取消升级,请修复框架安装后重试`)
|
|
192
194
|
return
|
|
193
195
|
}
|
|
196
|
+
steps.push(...repairStaleRenameResidue({ fwRoot }).steps) // 修上次失败的 .stale-* 改名残留(只 rename / 跳过已存在 / 幂等)
|
|
194
197
|
// 框架全树 checkpoint(可靠回滚点):升级前镜像全部 @deepseek-ai 版本包(自包内容)+ 顶层 scope + lock
|
|
195
198
|
let fwCheckpoint = null
|
|
196
199
|
try {
|
|
@@ -213,6 +216,7 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
213
216
|
const qHelperPath = join(dshHome(), 'plugin-console', 'fw-analyze-boot.mjs')
|
|
214
217
|
const qCandidatesPath = join(dshHome(), 'plugin-console', 'fw-quarantine-candidates.json')
|
|
215
218
|
const qRecordPath = join(dshHome(), 'plugin-console', 'fw-quarantine.json')
|
|
219
|
+
const rvHelperPath = join(dshHome(), 'plugin-console', 'fw-relaunch-verdict.mjs'), rlPath = join(dshHome(), 'plugin-console', 'fw-launch.mjs') // 判定器转发脚本 + node 服务拉起器(逻辑都在 domain/framework-relaunch.js)
|
|
216
220
|
let thirdPartyRows = []
|
|
217
221
|
try {
|
|
218
222
|
thirdPartyRows = listEntries(ctx)
|
|
@@ -236,6 +240,7 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
236
240
|
'',
|
|
237
241
|
].join('\n')
|
|
238
242
|
writeFileSync(qHelperPath, helperSource, 'utf8')
|
|
243
|
+
const rUrl = pathToFileURL(join(pluginRoot(), 'lib', 'server', 'domain', 'framework-relaunch.js')).href; writeFileSync(rvHelperPath, relaunchVerdictHelperSource({ libUrl: rUrl }), 'utf8'); writeFileSync(rlPath, relaunchLauncherSource(), 'utf8') // 判定器转发脚本 + node 拉起器(逻辑仍在插件内)
|
|
239
244
|
const candidateSnapshot = listEntries(ctx)
|
|
240
245
|
.filter((e) => typeof e.rowId === 'string' && e.rowId !== '')
|
|
241
246
|
.map((e) => ({ rowId: e.rowId, moduleName: e.moduleName, toggleable: e.toggleable === true, enabled: e.enabled === true }))
|
|
@@ -252,8 +257,10 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
252
257
|
return j.replace(/`/gu, '``').replace(/\$/gu, '`$')
|
|
253
258
|
}
|
|
254
259
|
const patchFilePath = join(profileDir, 'cordis.patch.yml')
|
|
255
|
-
//
|
|
256
|
-
|
|
260
|
+
// 拉起片段(隔离重试用;与升级/回滚共用 relaunchPrelude 里的同一份实现)。
|
|
261
|
+
// 2026-10-28:成功只认端口监听 → Note-Relaunch 置位后**立刻**收尾(以前只记"别再拉起",
|
|
262
|
+
// 循环又空转约 95 秒,最后把已起来的服务判死并回滚 —— 缺陷 ②)。
|
|
263
|
+
const launchSnippet = (tag) => `if (-not $ok -and -not $started) { $started = Note-Relaunch (Invoke-DshRelaunch '${tag}'); if ($script:relaunchListening) { $ok = $true; break } }; Beat`
|
|
257
264
|
const lines = [
|
|
258
265
|
`$state = ${ps(stateFile)}`,
|
|
259
266
|
`$log = ${ps(logFile)}`,
|
|
@@ -265,7 +272,7 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
265
272
|
" try { if ($s -eq 'failed') { Set-Content -Path $state -Value ($s + '|' + $m + '|stage=' + [string]$script:stage) -Encoding UTF8; return } } catch {}",
|
|
266
273
|
" try { if ($s -ne 'done' -and $s -ne 'idle') { $script:stage = $s }; Set-Content -Path $state -Value ($s + '|' + $m) -Encoding UTF8; Beat } catch {}",
|
|
267
274
|
"}",
|
|
268
|
-
relaunchPrelude({ nodePath, pluginDir: pluginRoot(), fwRoot, target, ps }),
|
|
275
|
+
relaunchPrelude({ nodePath, pluginDir: pluginRoot(), fwRoot, target, ps, port }),
|
|
269
276
|
// 全局异常兜底(事故教训):脚本任何未捕获异常都会无声死亡、状态永远卡住、服务没人拉起。
|
|
270
277
|
// trap 捕获后:写 failed 状态 + 日志 + 尝试拉起服务 + 自删任务 + 退出
|
|
271
278
|
"trap {",
|
|
@@ -392,7 +399,7 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
392
399
|
` SetState 'failed' ('回滚过程异常,框架可能处于混合状态:' + $_.Exception.Message)`,
|
|
393
400
|
` }`,
|
|
394
401
|
`}`,
|
|
395
|
-
`$script:rolledBack = $false`,
|
|
402
|
+
`$script:rolledBack = $false; $script:structureOk = $false; $script:topLevelOk = $false; $script:keptReady = $false; $script:relaunchListening = $false`,
|
|
396
403
|
`$code = Install-Framework 'https://registry.npmmirror.com'`,
|
|
397
404
|
`if ($code -ne 0) { Log ('npmmirror 安装失败(exit=' + $code + '),切换 registry.npmjs.org 重试一次'); $code = Install-Framework 'https://registry.npmjs.org' }`,
|
|
398
405
|
`if ($code -ne 0) {`,
|
|
@@ -421,9 +428,9 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
421
428
|
` if ($code -ne 0) { $code = Install-Framework 'https://registry.npmjs.org' }`,
|
|
422
429
|
` $vv = ''`,
|
|
423
430
|
` try { $vv = (Get-Content (Join-Path $visible 'package.json') -Raw | ConvertFrom-Json).version } catch {}`,
|
|
424
|
-
` if ($vv -eq '${target}') { Log '顶层可见版本已修正为 ${target}' } else { Log ('顶层可见版本仍为 ' + $vv + '(拉起不受影响:Node 按链接真实路径解析依赖)') }`,
|
|
431
|
+
` if ($vv -eq '${target}') { $script:topLevelOk = $true; Log '顶层可见版本已修正为 ${target}' } else { Log ('顶层可见版本仍为 ' + $vv + '(拉起不受影响:Node 按链接真实路径解析依赖)') }`,
|
|
425
432
|
` } else { Log ('顶层可见版本异常(' + $vv + ')且非实体目录,跳过改名(LinkType=' + $item.LinkType + ')') }`,
|
|
426
|
-
` } else { Log '顶层可见版本校验通过' }`,
|
|
433
|
+
` } else { $script:topLevelOk = $true; Log '顶层可见版本校验通过' }`,
|
|
427
434
|
`}`,
|
|
428
435
|
// 回滚后跳过重链/依赖修复/版本对齐(它们按新树预期运行,会对已恢复的旧树造成二次破坏)
|
|
429
436
|
`if (-not $script:rolledBack) {`,
|
|
@@ -564,7 +571,7 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
564
571
|
'$started = $false',
|
|
565
572
|
`for ($i = 0; $i -lt 20; $i++) {`,
|
|
566
573
|
` try { $c = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue; if ($c.Count -gt 0) { $ok = $true; break } } catch {}`,
|
|
567
|
-
` if (-not $ok -and -not $started) {
|
|
574
|
+
` if (-not $ok -and -not $started) { $started = Note-Relaunch (Invoke-DshRelaunch '升级后'); if ($script:relaunchListening) { $ok = $true; break } }; Beat`,
|
|
568
575
|
' Start-Sleep -Seconds 5',
|
|
569
576
|
'}',
|
|
570
577
|
// ── 启动失败隔离(用户硬要求):新框架起不来时先隔离肇事者再重试,不直接回滚 ──
|
|
@@ -620,19 +627,13 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
620
627
|
` Log ('第 ' + $q + ' 轮隔离后仍失败')`,
|
|
621
628
|
` }`,
|
|
622
629
|
`}`,
|
|
623
|
-
//
|
|
630
|
+
// 隔离也救不回:**先判定该怎么办**(2026-10-28 用户定案:自动回滚只留给"框架树确认损坏",
|
|
631
|
+
// 其余一律 ready + 手动命令,回滚交人工)。判定 = domain/framework-relaunch.js#judgeRelaunchFailure。
|
|
624
632
|
`if (-not $ok -and -not $script:rolledBack) {`,
|
|
625
|
-
|
|
626
|
-
` Invoke-Rollback`,
|
|
627
|
-
` $ok = $false`,
|
|
628
|
-
` $started = $false`,
|
|
629
|
-
` for ($i = 0; $i -lt 20; $i++) {`,
|
|
630
|
-
` try { $c = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue; if ($c.Count -gt 0) { $ok = $true; break } } catch {}`,
|
|
631
|
-
` if (-not $ok -and -not $started) { if (Invoke-DshRelaunch '回滚后') { $started = $true } }; Beat`,
|
|
632
|
-
` Start-Sleep -Seconds 5`,
|
|
633
|
-
` }`,
|
|
633
|
+
relaunchVerdictBlock({ ps, nodePath, helperPath: rvHelperPath, port }),
|
|
634
634
|
`}`,
|
|
635
|
-
|
|
635
|
+
// keptReady 时上面已写过 ready 终态,这里不再覆盖成 failed(否则又变成"整列红叉")
|
|
636
|
+
`if ($ok) { if ($script:rolledBack) { SetState 'failed' '升级失败,已自动回滚到升级前版本(服务已恢复)' ; Log '升级失败已自动回滚,服务已恢复' } else { SetState 'done' '升级完成,服务已监听 ${port}' ; Log '服务已监听 ${port},升级完成' } } elseif (-not $script:keptReady) { SetState 'failed' '拉起失败且自动回滚未生效:请手动运行 node ${binPath} web' ; Log '拉起失败且自动回滚未生效:请手动运行 node ${binPath} web(参考本日志排查)' }`,
|
|
636
637
|
`schtasks /delete /f /tn ${taskName} 2>$null`,
|
|
637
638
|
].filter((l) => l !== '').join('\r\n')
|
|
638
639
|
// 初始状态(备份完成、脚本即将执行)
|
|
@@ -695,4 +696,4 @@ async function routeFrameworkUpgrade(req, res, rc) {
|
|
|
695
696
|
return
|
|
696
697
|
}
|
|
697
698
|
|
|
698
|
-
export { routeFrameworkUpgrade }
|
|
699
|
+
export { routeFrameworkUpgrade }
|
|
@@ -9,6 +9,7 @@ import { tmpdir } from 'node:os'
|
|
|
9
9
|
import { createRequire } from 'node:module'
|
|
10
10
|
import { readCompatGate, writeCompatGate } from '../domain/compat.js'
|
|
11
11
|
import { cleanupStaleFwTasks, currentFrameworkVersion, detectHostShape, pickFrameworkTarget, platformUpgradeRefusal, relaunchPrelude, resolveDshBin, shellHostedRefusal } from '../domain/framework.js'
|
|
12
|
+
import { relaunchLauncherSource } from '../domain/framework-relaunch.js'
|
|
12
13
|
import { restartPathDecision } from '../domain/restart.js'
|
|
13
14
|
// 0.5.84 加法(用户第 5 项):独立实例的 detached 重拉 —— 计划在 domain/restart-launcher.js,
|
|
14
15
|
// 进程原语仍走 infra/egress.js 的可注入出口(单测能证明"一次真进程都没起")。
|
|
@@ -113,7 +114,9 @@ async function routeFrameworkUpgradeStatusGet(req, res, rc) {
|
|
|
113
114
|
// = 0xC000013A),终态没写成 → 界面永远卡在「回滚中…」并每 3 秒轮询。心跳 + 现实核对
|
|
114
115
|
// 能把它纠正回来:脚本不再心跳(>90 秒没动静)时,用「已装版本 vs 回滚记录的 from/to」
|
|
115
116
|
// 判断真实结果;同时清掉残留的 DSH-FW-* 计划任务。
|
|
116
|
-
|
|
117
|
+
// 2026-10-28 加法:`ready` = 框架本体已就位、只有自动拉起失败(**不回滚**,回滚交人工)。
|
|
118
|
+
// 它是**终态**(下面两处 terminal 判定都得认它,否则自愈会改写成 done、15 分钟残留清理会抹成 idle)。
|
|
119
|
+
const terminal = status.status === 'idle' || status.status === 'done' || status.status === 'failed' || status.status === 'ready'
|
|
117
120
|
if (!terminal) {
|
|
118
121
|
let hbAge = null
|
|
119
122
|
try { hbAge = Date.now() - statSync(`${f}.hb`).mtimeMs } catch { hbAge = null }
|
|
@@ -152,11 +155,13 @@ async function routeFrameworkUpgradeStatusGet(req, res, rc) {
|
|
|
152
155
|
// 视为上次升级的残留——升级脚本要么成功(done)要么失败(failed),服务重启后不可能还在
|
|
153
156
|
// 中途;任务调度失败/脚本空跑时状态会永远停在 starting,重启后不应再自动恢复进度条。
|
|
154
157
|
// (升级进行中页面断连后用户手动重启服务属边缘情况:<15 分钟不受影响,进度仍可恢复)
|
|
155
|
-
if (status.status !== 'idle' && status.status !== 'done' && status.status !== 'failed'
|
|
158
|
+
if (status.status !== 'idle' && status.status !== 'done' && status.status !== 'failed' && status.status !== 'ready'
|
|
156
159
|
&& Date.now() - at > 15 * 60 * 1000) {
|
|
157
160
|
try { writeFileSync(f, 'idle|', 'utf8') } catch {}
|
|
158
161
|
status = { status: 'idle', message: null, at: Date.now() }
|
|
159
162
|
}
|
|
163
|
+
// ready → 对客户端表现成 done + keptFrame/relaunchFailed 标记(不改 UI:done 是终态、message 原样显示)
|
|
164
|
+
if (status.status === 'ready') status = { ...status, status: 'done', keptFrame: true, relaunchFailed: true }
|
|
160
165
|
}
|
|
161
166
|
} catch {}
|
|
162
167
|
sendJson(res, 200, { ok: true, ...status })
|
|
@@ -414,6 +419,7 @@ async function routeFrameworkRollback(req, res, rc) {
|
|
|
414
419
|
const logFile = join(dshHome(), 'plugin-console', 'fw-upgrade.log')
|
|
415
420
|
const stateFile = join(dshHome(), 'plugin-console', 'fw-upgrade-state.txt')
|
|
416
421
|
const ps = (s) => JSON.stringify(s).replace(/\\\\/gu, '\\')
|
|
422
|
+
try { writeFileSync(join(dshHome(), 'plugin-console', 'fw-launch.mjs'), relaunchLauncherSource(), 'utf8') } catch {} // 2026-10-28:回滚后的拉起也走 node 拉起器(与升级脚本共用同一份生成物)
|
|
417
423
|
const lines = [
|
|
418
424
|
`$state = ${ps(stateFile)}`,
|
|
419
425
|
`$log = ${ps(logFile)}`,
|
|
@@ -422,7 +428,7 @@ async function routeFrameworkRollback(req, res, rc) {
|
|
|
422
428
|
" try { if ($s -eq 'failed') { Set-Content -Path $state -Value ($s + '|' + $m + '|stage=' + [string]$script:stage) -Encoding UTF8; return } } catch {}",
|
|
423
429
|
" try { if ($s -ne 'done' -and $s -ne 'idle') { $script:stage = $s }; Set-Content -Path $state -Value ($s + '|' + $m) -Encoding UTF8; Beat } catch {}",
|
|
424
430
|
"}",
|
|
425
|
-
relaunchPrelude({ nodePath, pluginDir: pluginRoot(), fwRoot: rec.fwRoot, target: rec.from ?? '', ps }),
|
|
431
|
+
relaunchPrelude({ nodePath, pluginDir: pluginRoot(), fwRoot: rec.fwRoot, target: rec.from ?? '', ps, port }),
|
|
426
432
|
"trap {",
|
|
427
433
|
" try { SetState 'failed' ('回滚脚本异常终止:' + $_.Exception.Message) } catch {}",
|
|
428
434
|
` schtasks /delete /f /tn ${taskName} 2>$null`,
|
|
@@ -449,10 +455,10 @@ async function routeFrameworkRollback(req, res, rc) {
|
|
|
449
455
|
`$started = $false`,
|
|
450
456
|
`for ($i = 0; $i -lt 20; $i++) {`,
|
|
451
457
|
` try { $c = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue; if ($c.Count -gt 0) { $ok = $true; break } } catch {}`,
|
|
452
|
-
` if (-not $ok -and -not $started) {
|
|
458
|
+
` if (-not $ok -and -not $started) { $started = Note-Relaunch (Invoke-DshRelaunch '回滚后'); if ($script:relaunchListening) { $ok = $true; break } }; Beat`,
|
|
453
459
|
` Start-Sleep -Seconds 5`,
|
|
454
460
|
`}`,
|
|
455
|
-
`if ($ok) { SetState 'done' ('已回滚到升级前版本 ${rec.from ?? '?'},服务正常') ; Log '回滚完成,服务已恢复' } else { SetState 'failed' '
|
|
461
|
+
`if ($ok) { SetState 'done' ('已回滚到升级前版本 ${rec.from ?? '?'},服务正常') ; Log '回滚完成,服务已恢复' } else { SetState 'failed' ('回滚后服务拉起失败:' + [string]$script:relaunchReason + ';请手动运行 node ${resolveDshBin() ?? '<bin>'} web --no-open') ; Log ('回滚后拉起失败:' + [string]$script:relaunchReason) }`,
|
|
456
462
|
`schtasks /delete /f /tn ${taskName} 2>$null`,
|
|
457
463
|
].filter((l) => l !== '').join('\r\n')
|
|
458
464
|
try { writeFileSync(stateFile, 'rollback|回滚脚本已启动…', 'utf8') } catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noob-stupid/dsh-plugin-console",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.97",
|
|
4
4
|
"description": "DSH 框架升级安全与插件升级门控:一键升级、失败自动回滚、升级后回滚上版、旧插件不适配自动禁用;内置多源插件市场为发现层,插件源全部可自定义,可指向公司内网私有源 / 私有索引 / 本地 Git 仓库,纯内网离线可用 | Framework upgrade safety & plugin version gating for DSH, with a customizable multi-source plugin market: point every source at internal mirrors or a local file:// repo for intranet-only, offline installs.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|