dsh-shutdown-guard 1.6.1-beta.1 → 1.6.1-beta.2
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/CHANGELOG.md +11 -0
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/lib/client.js +50 -19
- package/lib/index.mjs +28 -1
- package/package.json +1 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.6.1-beta.2 — 2026-11-08
|
|
4
|
+
|
|
5
|
+
> 测试版(beta):`latest` 仍指向 1.6.0,`npm i dsh-shutdown-guard` 不会装到它。装这个版本请用 `npm i dsh-shutdown-guard@1.6.1-beta.2`。
|
|
6
|
+
|
|
7
|
+
- ⚠️ `~` **默认配置下,锁屏 / 注销 / 自定义脚本不再可用**:这三件事会让人离开机器、或让插件去执行一段你未必盯着的代码,所以现在要先开启「自定义配置」。设置页会写明原因「开启「自定义配置」后才能用这个动作」;即使绕过界面直接调接口武装,判定闸也同样拦住,不会进倒计时。
|
|
8
|
+
- `~` **守夜页的行序换了**:倒计时挪到最上面,动作紧随其后;「会话都停下来」移到「后台任务跑完」与「有会话报错就跳过」之前,与判定实际执行的顺序一致。功能没少,位置变了。
|
|
9
|
+
- `+` **不再限制安装平台**:移除了包里的平台限制,macOS 与 Linux 现在能正常安装(此前 npm 会直接以 `EBADPLATFORM` 拒绝);Windows 用户不受影响。
|
|
10
|
+
- 修复:自定义脚本还没上传文件时,下拉里那一项以前是灰的、点不动也没有说明 → 现在可以先选中再挑文件,不能武装时会说清原因。
|
|
11
|
+
- 修复:关闭「自定义配置」后,脚本上传行与确认框整体收起;如果此前选的就是自定义脚本,动作下拉显示一个禁用的「自定义脚本(不可用)」占位,而不是空白。
|
|
12
|
+
- **测试与体积**:宿主断言 **66 项**、客户端 **15 项**全绿;本版 npm 包约 363 kB(未压缩解包约 763 kB),仍是 11 个文件。
|
|
13
|
+
|
|
3
14
|
## v1.6.1-beta.1 — 2026-11-06
|
|
4
15
|
|
|
5
16
|
> 测试版(beta):`latest` 仍指向 1.6.0,`npm i dsh-shutdown-guard` 不会装到它。装这个版本请用 `npm i dsh-shutdown-guard@1.6.1-beta.1`。
|
package/README.en.md
CHANGED
|
@@ -7,7 +7,7 @@ Stable `v1.6.0` · beta `v1.6.1-beta.1` (closed beta) · [Changelog](https://git
|
|
|
7
7
|
|
|
8
8
|
[](https://github.com/ltayshark0637/dsh-Shutdown-Guard/blob/main/CHANGELOG.md) [](https://github.com/deepseek-ai)
|
|
9
9
|
[](https://ltayshark0637.github.io/dsh-Shutdown-Guard/demo.html) [](LICENSE)
|
|
10
|
-
[](https://dshbase.com/plugins/dsh-shutdown-guard/) [](https://awesome-dsh-plugin.com/p/ltayshark0637/dsh-Shutdown-Guard/)
|
|
10
|
+
[](https://dshbase.com/plugins/dsh-shutdown-guard/) [](https://www.dsh.so/artifact/dsh-shutdown-guard/?ref=badge) [](https://dsh-plugin.org/plugins/ltayshark0637/dsh-shutdown-guard) [](https://awesome-dsh-plugin.com/p/ltayshark0637/dsh-Shutdown-Guard/) [](https://dpharness.com/plugin/ltayshark0637/dsh-Shutdown-Guard)
|
|
11
11
|
|
|
12
12
|
Adds a "shut down when every task is done" card to the DeepSeek Harness sidebar — right under Skill Centre and above the session list, following the task state live.
|
|
13
13
|
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
[](https://github.com/ltayshark0637/dsh-Shutdown-Guard/blob/main/CHANGELOG.md) [](https://github.com/deepseek-ai)
|
|
9
9
|
[](https://ltayshark0637.github.io/dsh-Shutdown-Guard/demo.html) [](LICENSE)
|
|
10
|
-
[](https://dshbase.com/plugins/dsh-shutdown-guard/) [](https://awesome-dsh-plugin.com/p/ltayshark0637/dsh-Shutdown-Guard/)
|
|
10
|
+
[](https://dshbase.com/plugins/dsh-shutdown-guard/) [](https://www.dsh.so/artifact/dsh-shutdown-guard/?ref=badge) [](https://dsh-plugin.org/plugins/ltayshark0637/dsh-shutdown-guard) [](https://awesome-dsh-plugin.com/p/ltayshark0637/dsh-Shutdown-Guard/) [](https://dpharness.com/plugin/ltayshark0637/dsh-Shutdown-Guard)
|
|
11
11
|
|
|
12
12
|
为 DeepSeek Harness 的侧边栏加一张「所有任务完成后自动关机」的卡片,就在「技能中心」下方、会话列表上方,随任务状态实时变化。
|
|
13
13
|
|
package/README.ru.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
[](https://github.com/ltayshark0637/dsh-Shutdown-Guard/blob/main/CHANGELOG.md) [](https://github.com/deepseek-ai)
|
|
9
9
|
[](https://ltayshark0637.github.io/dsh-Shutdown-Guard/demo.html) [](LICENSE)
|
|
10
|
-
[](https://dshbase.com/plugins/dsh-shutdown-guard/) [](https://awesome-dsh-plugin.com/p/ltayshark0637/dsh-Shutdown-Guard/)
|
|
10
|
+
[](https://dshbase.com/plugins/dsh-shutdown-guard/) [](https://www.dsh.so/artifact/dsh-shutdown-guard/?ref=badge) [](https://dsh-plugin.org/plugins/ltayshark0637/dsh-shutdown-guard) [](https://awesome-dsh-plugin.com/p/ltayshark0637/dsh-Shutdown-Guard/) [](https://dpharness.com/plugin/ltayshark0637/dsh-Shutdown-Guard)
|
|
11
11
|
|
|
12
12
|
Добавляет в боковую панель DeepSeek Harness карточку «выключить, когда все задачи готовы» — сразу под «Центром навыков» и над списком сессий, в реальном времени следуя за состоянием задач.
|
|
13
13
|
|
package/lib/client.js
CHANGED
|
@@ -198,6 +198,7 @@
|
|
|
198
198
|
'action.signout': '注销',
|
|
199
199
|
'action.restart-dsh': '重启 DSH',
|
|
200
200
|
'action.custom': '自定义脚本',
|
|
201
|
+
'action.custom.off': '自定义脚本(不可用)',
|
|
201
202
|
'action.unknown': '可能需要授权;如果失败,会告诉你原因。',
|
|
202
203
|
'action.reason.no-cli': '没有找到这个操作需要的系统命令。',
|
|
203
204
|
'action.reason.may-need-permissions': '可能需要管理员权限;执行失败时会说明原因。',
|
|
@@ -205,6 +206,7 @@
|
|
|
205
206
|
'action.reason.unsupported-on-platform': '当前系统不支持这个操作。',
|
|
206
207
|
'action.reason.no-host-restart': 'DSH 不支持自重启,请手动退出后重开。',
|
|
207
208
|
'action.reason.no-script': '还没有选择脚本文件。',
|
|
209
|
+
'action.reason.no-custom-profile': '开启「自定义配置」后才能用这个动作。',
|
|
208
210
|
'action.reason.missing-interpreter': '找不到运行这个脚本所需的解释器。',
|
|
209
211
|
'action.reason.script-not-acknowledged': '脚本还没有确认,先在下面勾选确认。',
|
|
210
212
|
'set.halt': '熔断后关机',
|
|
@@ -377,6 +379,7 @@
|
|
|
377
379
|
'action.signout': 'Sign out',
|
|
378
380
|
'action.restart-dsh': 'Restart DSH',
|
|
379
381
|
'action.custom': 'Custom script',
|
|
382
|
+
'action.custom.off': 'Custom script (unavailable)',
|
|
380
383
|
'action.unknown': 'It may need permission; if it fails, you will see why.',
|
|
381
384
|
'action.reason.no-cli': 'The system command this action needs was not found.',
|
|
382
385
|
'action.reason.may-need-permissions': 'It may need administrator rights; if it fails, you will see why.',
|
|
@@ -384,6 +387,7 @@
|
|
|
384
387
|
'action.reason.unsupported-on-platform': 'This system does not support this action.',
|
|
385
388
|
'action.reason.no-host-restart': 'DSH cannot restart itself; quit and open it again manually.',
|
|
386
389
|
'action.reason.no-script': 'No script file has been chosen yet.',
|
|
390
|
+
'action.reason.no-custom-profile': 'Turn on custom profiles to use this action.',
|
|
387
391
|
'action.reason.missing-interpreter': 'The interpreter needed to run this script was not found.',
|
|
388
392
|
'action.reason.script-not-acknowledged': 'The script is not confirmed yet; tick the box below first.',
|
|
389
393
|
'set.halt': 'Shut down after a halt',
|
|
@@ -556,6 +560,7 @@
|
|
|
556
560
|
'action.signout': 'Выйти из системы',
|
|
557
561
|
'action.restart-dsh': 'Перезапустить DSH',
|
|
558
562
|
'action.custom': 'Свой скрипт',
|
|
563
|
+
'action.custom.off': 'Свой скрипт (недоступен)',
|
|
559
564
|
'action.unknown': 'Может потребоваться разрешение; при ошибке будет указана причина.',
|
|
560
565
|
'action.reason.no-cli': 'Системная команда для этого действия не найдена.',
|
|
561
566
|
'action.reason.may-need-permissions': 'Может потребоваться разрешение администратора; при ошибке будет указана причина.',
|
|
@@ -563,6 +568,7 @@
|
|
|
563
568
|
'action.reason.unsupported-on-platform': 'Эта система не поддерживает такое действие.',
|
|
564
569
|
'action.reason.no-host-restart': 'DSH не может перезапустить себя; закройте и откройте его вручную.',
|
|
565
570
|
'action.reason.no-script': 'Файл скрипта ещё не выбран.',
|
|
571
|
+
'action.reason.no-custom-profile': 'Включите собственные профили, чтобы использовать это действие.',
|
|
566
572
|
'action.reason.missing-interpreter': 'Не найден интерпретатор для запуска этого скрипта.',
|
|
567
573
|
'action.reason.script-not-acknowledged': 'Скрипт ещё не подтверждён; сначала отметьте поле ниже.',
|
|
568
574
|
'set.halt': 'Выключить после сбоя',
|
|
@@ -2386,19 +2392,36 @@
|
|
|
2386
2392
|
}
|
|
2387
2393
|
|
|
2388
2394
|
function actionPicker(snap, current, onPick) {
|
|
2395
|
+
var showCustom = snap.customEnabled === true
|
|
2396
|
+
var ids = showCustom
|
|
2397
|
+
? ACTION_IDS
|
|
2398
|
+
: ACTION_IDS.filter(function (id) { return id !== 'custom' })
|
|
2399
|
+
// When custom profiles were switched off after someone had picked the
|
|
2400
|
+
// script action, the stored value is no longer one of the visible
|
|
2401
|
+
// options. A <select> would silently render blank, so keep a disabled
|
|
2402
|
+
// placeholder at the top that tells the truth; it disappears as soon
|
|
2403
|
+
// as the value changes to a listed action.
|
|
2404
|
+
var hiddenCustom = !showCustom && current === 'custom'
|
|
2405
|
+
? h('option', { key: 'custom-off', value: 'custom', disabled: true }, tr('action.custom.off'))
|
|
2406
|
+
: null
|
|
2389
2407
|
return h('select', {
|
|
2390
2408
|
className: 'dsp-sel',
|
|
2391
2409
|
value: current,
|
|
2392
2410
|
'aria-label': tr('set.action'),
|
|
2393
2411
|
onChange: function (ev) { onPick(ev.target.value) },
|
|
2394
|
-
},
|
|
2412
|
+
}, hiddenCustom, ids.map(function (id) {
|
|
2395
2413
|
var info = actionInfo(snap, id)
|
|
2414
|
+
// Picking an action is a saved choice, not an arming promise: the host
|
|
2415
|
+
// stores `custom` even when no script is uploaded yet, and refuses to
|
|
2416
|
+
// arm instead. So the two "set this up first" reasons stay selectable,
|
|
2417
|
+
// and the note under the picker says what is still missing. Anything
|
|
2418
|
+
// else is a hard no from the host and stays grayed out.
|
|
2419
|
+
var pending = info.reason === 'no-script'
|
|
2420
|
+
|| info.reason === 'script-not-acknowledged'
|
|
2421
|
+
|| info.reason === 'missing-interpreter'
|
|
2396
2422
|
return h('option', {
|
|
2397
2423
|
key: id, value: id,
|
|
2398
|
-
|
|
2399
|
-
// action cannot run, including a custom script we have no file for.
|
|
2400
|
-
// The reason below the picker explains it in words.
|
|
2401
|
-
disabled: info.available === false,
|
|
2424
|
+
disabled: info.available === false && !pending,
|
|
2402
2425
|
}, tr('action.' + id))
|
|
2403
2426
|
}))
|
|
2404
2427
|
}
|
|
@@ -2884,14 +2907,8 @@
|
|
|
2884
2907
|
|
|
2885
2908
|
// 3) 守夜行为:四行判定闸不再自成一组的成员,收进这里排在 AFK 前面。
|
|
2886
2909
|
var watch = [settingsGroup('watch-g', tr('set.watch'), null, 'watch')]
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
watch.push(settingsRow('c2', tr('set.jobs'), tr('set.jobs.desc') + hint,
|
|
2890
|
-
toggle(cur.blockOnJobs, function (next) { profileWrite('jobs', 'blockOnJobs', next) },
|
|
2891
|
-
locked, tr('set.jobs')), null, errFor('jobs')))
|
|
2892
|
-
watch.push(settingsRow('c1', tr('set.err'), tr('set.err.desc') + hint,
|
|
2893
|
-
toggle(cur.onlyOnSuccess, function (next) { profileWrite('err', 'onlyOnSuccess', next) },
|
|
2894
|
-
locked, tr('set.err')), null, errFor('err')))
|
|
2910
|
+
// The countdown comes first: it is the number most people open this pane
|
|
2911
|
+
// to change, and it reads better before what happens when it hits zero.
|
|
2895
2912
|
watch.push(settingsRow('cd', tr('set.cd'),
|
|
2896
2913
|
snap.countdownLocked ? tr('set.cd.locked') : tr('set.cd.desc'),
|
|
2897
2914
|
snap.countdownLocked
|
|
@@ -2899,10 +2916,10 @@
|
|
|
2899
2916
|
: seg([[30, tr('cd.30')], [60, tr('cd.60')], [120, tr('cd.120')], [300, tr('cd.300')]], cur.countdownSec, function (v) {
|
|
2900
2917
|
profileWrite('cd', 'countdownSec', v)
|
|
2901
2918
|
}), null, errFor('cd')))
|
|
2902
|
-
// 3b) Action layer: the picker, then the script block. The script
|
|
2903
|
-
//
|
|
2904
|
-
//
|
|
2905
|
-
//
|
|
2919
|
+
// 3b) Action layer: the picker, then the script block. The script block
|
|
2920
|
+
// only exists while custom profiles are on: with the feature off the
|
|
2921
|
+
// action picker does not offer 'custom' at all, so there is nothing to
|
|
2922
|
+
// upload for.
|
|
2906
2923
|
var actionId = cur.action && ACTION_IDS.indexOf(cur.action) >= 0 ? cur.action : 'shutdown'
|
|
2907
2924
|
var scr = cur.script && cur.script.ext ? cur.script : null
|
|
2908
2925
|
var scrAck = !!(scr && cur.scriptAck && cur.scriptAck.sha256 === scr.sha256)
|
|
@@ -2919,7 +2936,7 @@
|
|
|
2919
2936
|
profileWrite('action', 'action', next)
|
|
2920
2937
|
}),
|
|
2921
2938
|
actionNote(snap, actionId))))
|
|
2922
|
-
watch.push(h('div', { key: 'script', className: 'dsp-srow dsp-srow-stack' },
|
|
2939
|
+
if (customOn) watch.push(h('div', { key: 'script', className: 'dsp-srow dsp-srow-stack' },
|
|
2923
2940
|
h('div', { className: 'dsp-srow-text' },
|
|
2924
2941
|
h('span', { className: 'dsp-stitle' }, tr('set.script')),
|
|
2925
2942
|
h('span', { className: 'dsp-sdesc' }, tr('set.script.desc')),
|
|
@@ -2953,7 +2970,7 @@
|
|
|
2953
2970
|
if (ev.key === 'Enter') { ev.preventDefault(); commitScriptName(ev.target.value) }
|
|
2954
2971
|
},
|
|
2955
2972
|
})) : null)))
|
|
2956
|
-
if (scr) {
|
|
2973
|
+
if (customOn && scr) {
|
|
2957
2974
|
// The checkbox lives inside the warning block, not under it: the
|
|
2958
2975
|
// disclaimer and the confirmation must be impossible to separate.
|
|
2959
2976
|
watch.push(h('div', { key: 'ack', className: 'dsp-srow dsp-srow-stack dsp-ackrow' },
|
|
@@ -2973,6 +2990,20 @@
|
|
|
2973
2990
|
!scrAck ? h('span', { className: 'dsp-sreason' },
|
|
2974
2991
|
tr(resetHint ? 'set.script.ack.reset' : 'set.script.ack.needed')) : null)))
|
|
2975
2992
|
}
|
|
2993
|
+
// "Sessions have stopped" leads the group of three: no running session
|
|
2994
|
+
// first, then background jobs, then this round's errors — the order the
|
|
2995
|
+
// gate itself evaluates them in.
|
|
2996
|
+
watch.push(settingsRow('c3', tr('set.bottom'), tr('set.bottom.desc'),
|
|
2997
|
+
h('span', { className: 'dsp-sval' }, tr('set.always')), 'dsp-srow-ro'))
|
|
2998
|
+
// The gate rows continue: "what runs" reads above, the conditions that
|
|
2999
|
+
// hold it back below. The halt switch stays directly under its parent
|
|
3000
|
+
// ("skip this round on errors") because it is its child.
|
|
3001
|
+
watch.push(settingsRow('c2', tr('set.jobs'), tr('set.jobs.desc') + hint,
|
|
3002
|
+
toggle(cur.blockOnJobs, function (next) { profileWrite('jobs', 'blockOnJobs', next) },
|
|
3003
|
+
locked, tr('set.jobs')), null, errFor('jobs')))
|
|
3004
|
+
watch.push(settingsRow('c1', tr('set.err'), tr('set.err.desc') + hint,
|
|
3005
|
+
toggle(cur.onlyOnSuccess, function (next) { profileWrite('err', 'onlyOnSuccess', next) },
|
|
3006
|
+
locked, tr('set.err')), null, errFor('err')))
|
|
2976
3007
|
// "Shut down after a halt" is the child of "skip this round on errors":
|
|
2977
3008
|
// the stored profile value is never rewritten, so turning the parent
|
|
2978
3009
|
// back on restores what the user picked. /state already folded the
|
package/lib/index.mjs
CHANGED
|
@@ -400,8 +400,14 @@ const ACTION_REASON = {
|
|
|
400
400
|
noScript: 'no-script',
|
|
401
401
|
missingInterpreter: 'missing-interpreter',
|
|
402
402
|
scriptNotAcknowledged: 'script-not-acknowledged',
|
|
403
|
+
noCustomProfile: 'no-custom-profile',
|
|
403
404
|
}
|
|
404
405
|
|
|
406
|
+
// Actions outside the plain system-power set are gated behind custom profiles:
|
|
407
|
+
// locking a machine or running a script is not something an unattended default
|
|
408
|
+
// profile should be able to do. Restart-dsh keeps its own permanent reason.
|
|
409
|
+
const CUSTOM_PROFILE_ONLY = ['lock', 'signout', 'custom']
|
|
410
|
+
|
|
405
411
|
/** True for an absolute/relative path, which needs no PATH lookup. */
|
|
406
412
|
function looksLikePath(value) {
|
|
407
413
|
return typeof value === 'string' && value.length > 0
|
|
@@ -1979,6 +1985,10 @@ export function apply(ctx, rawConfig) {
|
|
|
1979
1985
|
// The script's interpreter is part of the answer, so resolve it first.
|
|
1980
1986
|
await resolveCurrentScript()
|
|
1981
1987
|
const next = {}
|
|
1988
|
+
// Raw per-platform answers only. The "custom profile required" gate is applied
|
|
1989
|
+
// on the way out (`gatedActions`), not stored here: a stored gate would go
|
|
1990
|
+
// stale the moment the user enables custom profiles, and /state would keep
|
|
1991
|
+
// reporting `no-custom-profile` until the async probe finished.
|
|
1982
1992
|
for (const id of ACTION_IDS) {
|
|
1983
1993
|
// `custom` has no table row: it runs the profile's script, so "no file" and
|
|
1984
1994
|
// "no interpreter" are different answers and both are worth saying.
|
|
@@ -2024,6 +2034,9 @@ export function apply(ctx, rawConfig) {
|
|
|
2024
2034
|
/** The active action is armed only when the probe did not mark it unavailable. */
|
|
2025
2035
|
function actionReadyNow() {
|
|
2026
2036
|
const id = resolveAction()
|
|
2037
|
+
// Same gate as the probe, but read from the store: a stale map (or no probe
|
|
2038
|
+
// yet) must not let a default profile arm lock/signout/custom.
|
|
2039
|
+
if (store.customEnabled !== true && CUSTOM_PROFILE_ONLY.includes(id)) return false
|
|
2027
2040
|
const entry = actionAvailability[id]
|
|
2028
2041
|
if (id === 'custom') {
|
|
2029
2042
|
// The script lives in the profile, so this answer must not lag behind the
|
|
@@ -2038,6 +2051,20 @@ export function apply(ctx, rawConfig) {
|
|
|
2038
2051
|
return entry.available !== false
|
|
2039
2052
|
}
|
|
2040
2053
|
|
|
2054
|
+
/**
|
|
2055
|
+
* The capability map as the settings page should see it. The probe stores the
|
|
2056
|
+
* raw per-platform answer; the custom-profile gate is applied on the way out so
|
|
2057
|
+
* a snapshot taken right after the toggle cannot show the old permission.
|
|
2058
|
+
*/
|
|
2059
|
+
function gatedActions() {
|
|
2060
|
+
if (store.customEnabled === true) return actionAvailability
|
|
2061
|
+
const out = Object.assign({}, actionAvailability)
|
|
2062
|
+
for (const id of CUSTOM_PROFILE_ONLY) {
|
|
2063
|
+
out[id] = { available: false, reason: ACTION_REASON.noCustomProfile }
|
|
2064
|
+
}
|
|
2065
|
+
return out
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2041
2068
|
/** True when the user acknowledged exactly the file that is stored now. */
|
|
2042
2069
|
function scriptAckedNow() {
|
|
2043
2070
|
const script = cfg && cfg.script
|
|
@@ -2822,7 +2849,7 @@ export function apply(ctx, rawConfig) {
|
|
|
2822
2849
|
afkAutoAnswer: cfg.afkAutoAnswer !== false,
|
|
2823
2850
|
// Capability map, keyed by the same ids `profile.action` uses (hyphen
|
|
2824
2851
|
// included). `{}` until the first probe settles; `recheck` refreshes it.
|
|
2825
|
-
actions:
|
|
2852
|
+
actions: gatedActions(),
|
|
2826
2853
|
actionReady: actionReadyNow(),
|
|
2827
2854
|
// The active profile's script, metadata only, plus whether the stored
|
|
2828
2855
|
// acknowledgement still covers exactly these bytes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-shutdown-guard",
|
|
3
|
-
"version": "1.6.1-beta.
|
|
3
|
+
"version": "1.6.1-beta.2",
|
|
4
4
|
"description": "任务结束后自动关机 —— 侧边栏守夜卡片:五道判定闸、后台任务守卫、AFK 接管、阻断态分级",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ltayshark0637",
|
|
@@ -65,8 +65,5 @@
|
|
|
65
65
|
"engines": {
|
|
66
66
|
"node": ">=22.19"
|
|
67
67
|
},
|
|
68
|
-
"os": [
|
|
69
|
-
"win32"
|
|
70
|
-
],
|
|
71
68
|
"license": "MIT"
|
|
72
69
|
}
|