dsh-autorestart 0.6.2 → 0.7.0
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/README.md +329 -12
- package/assets/screenshot-1.png +0 -0
- package/assets/screenshot-2.png +0 -0
- package/assets/screenshot-3.png +0 -0
- package/assets/screenshot-4.png +0 -0
- package/assets/screenshot-5.png +0 -0
- package/client.js +5911 -3832
- package/cordis.patch.yml +37 -1
- package/docs/README.zh.md +219 -9
- package/index.js +2266 -193
- package/lib/access.js +212 -0
- package/lib/autostart-advice.js +198 -0
- package/lib/backup.js +131 -4
- package/lib/blockers.js +228 -0
- package/lib/checkpoint.js +147 -0
- package/lib/diagnostics.js +180 -0
- package/lib/health.js +264 -0
- package/lib/host-kind.js +145 -0
- package/lib/node-runtime.js +144 -0
- package/lib/restart-verdict.js +327 -0
- package/lib/restart.js +823 -56
- package/lib/sleep.js +46 -49
- package/lib/startup-mode.js +491 -0
- package/lib/supervisor-config.js +735 -0
- package/lib/supervisor-install.js +557 -0
- package/lib/supervisor.js +1276 -0
- package/locale/en.json +247 -64
- package/locale/zh.json +247 -64
- package/package.json +7 -2
- package/screenshots.json +6 -4
- package/scripts/emergency-disable.cmd +27 -0
- package/scripts/emergency-toggle.ps1 +302 -0
- package/scripts/run-tests.ps1 +46 -0
- package/supervisor/supervisor.js +884 -0
package/locale/en.json
CHANGED
|
@@ -1,48 +1,99 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
2
|
+
"label": "Pending restart",
|
|
3
|
+
"busyPrefix": " · waiting for ",
|
|
4
|
+
"busySuffix": " task(s) to finish",
|
|
5
|
+
"idle": " · restarts once idle",
|
|
6
|
+
"goalActive": " · held by a self-continuing goal",
|
|
7
|
+
"goalExhausted": " · a goal ran out of rounds",
|
|
8
|
+
"settling": " · still waiting for the quiet window",
|
|
9
|
+
"parkedGoals": " · {n} goal(s) parked, they continue after the restart",
|
|
10
|
+
"parkButton": "Park & restart",
|
|
11
|
+
"parkButtonAria": "Park that session goal and request the restart now",
|
|
12
|
+
"parkBusy": "Parking…",
|
|
13
|
+
"parkError": "Park failed: {message}",
|
|
14
|
+
"tooltip": "Waits for the other sessions to become idle, then restarts automatically; this session resumes its progress after the restart.",
|
|
15
|
+
"tooltipBusy": "Waits for the other tasks to finish (the count includes work that is queued but not started yet), then restarts automatically; this session resumes its progress after the restart.",
|
|
16
|
+
"tooltipWithError": "Waits for the other sessions to become idle, then restarts automatically; this session resumes its progress after the restart. {message}",
|
|
17
|
+
"dotAria": "Pending restart",
|
|
18
|
+
"panelHeading": "Pending restart",
|
|
19
|
+
"panelStateBusy": "Waiting for the other sessions to become idle",
|
|
20
|
+
"panelStateIdle": "Waiting for the idle window",
|
|
21
|
+
"panelStateGoalActive": "Waiting for the other sessions to park their self-continuing goal",
|
|
22
|
+
"panelStateGoalExhausted": "Waiting for the other sessions' goal (its round budget is used up)",
|
|
23
|
+
"panelBlockedTitle": "Blocking session: {title}",
|
|
24
|
+
"panelBlockedGeneric": "One task is running",
|
|
25
|
+
"panelBlockedSession": "Session \"{label}\" is running",
|
|
26
|
+
"panelBlockedSubagent": "{name} is running",
|
|
27
|
+
"panelBlockedSubagentGeneric": "One subagent is running",
|
|
28
|
+
"panelBlockedSubagentGroup": "The {n} subagents of session \"{label}\" are running",
|
|
29
|
+
"panelBlockedSubagentGroupGeneric": "{n} subagents are running",
|
|
30
|
+
"panelBlockedRestartPending": "Waiting for the pending restart to finish before shutting down",
|
|
31
|
+
"panelGoalActiveLine": "Blocking session \"{title}\": the goal keeps driving rounds by itself (goal-active)",
|
|
32
|
+
"panelGoalExhaustedLine": "Blocking session \"{title}\": the goal ran out of rounds and cannot continue (goal-exhausted)",
|
|
33
|
+
"panelParkedTitle": "Parked goals (they continue after the restart):",
|
|
34
|
+
"panelParkedGoal": "session {session} · {goal}",
|
|
35
|
+
"panelWaiting": "Waiting for {duration}",
|
|
36
|
+
"panelResume": "This session resumes its progress automatically after the restart.",
|
|
37
|
+
"longWaitPill": " · waiting for {duration} · {n} task(s) running",
|
|
38
|
+
"longWaitTooltip": "Waiting for {duration}. Running: {blockers}",
|
|
39
|
+
"blockerGeneric": "another session",
|
|
40
|
+
"blockerJoin": ", ",
|
|
41
|
+
"blockerGroupSubagents": "{label} ({n} subagents)",
|
|
42
|
+
"blockerGroupSubagentsGeneric": "{n} subagents",
|
|
43
|
+
"blockerMore": "… and {n} more",
|
|
44
|
+
"blockersTitleLine": "Blocking sessions: {blockers}",
|
|
45
|
+
"durationSeconds": "{n}s",
|
|
46
|
+
"durationMinutes": "{n}m {m}s",
|
|
47
|
+
"durationHours": "{n}h {m}m",
|
|
48
|
+
"durationMinutesShort": "{n} min",
|
|
49
|
+
"durationHoursShort": "{n}h",
|
|
50
|
+
"durationHoursMinutesShort": "{n}h {m}m",
|
|
51
|
+
"cancelPanelTasks": "This restart is still waiting for {n} task(s) to finish",
|
|
52
|
+
"cancelPanelOthers": "{n} other session(s) are waiting for this restart too",
|
|
53
|
+
"cancelPanelOthersAfter": "{n} session(s) are still waiting",
|
|
54
|
+
"cancelPanelAria": "Pending restart details",
|
|
55
|
+
"cancelButton": "Cancel my restart",
|
|
56
|
+
"cancelButtonAria": "Cancel my restart",
|
|
57
|
+
"cancelConfirmTitle": "Cancel your restart?",
|
|
58
|
+
"cancelConfirmOnlyMe": "After this, the restart will not happen.",
|
|
59
|
+
"cancelConfirmOthers": "{n} other session(s) are waiting for this restart too — cancelling only affects you; they will still restart.",
|
|
60
|
+
"cancelConfirmUnknown": "The current session is unknown, so its own request cannot be withdrawn.",
|
|
61
|
+
"cancelConfirmButton": "Cancel it",
|
|
62
|
+
"cancelConfirmButtonAria": "Confirm the cancellation",
|
|
63
|
+
"cancelDismissButton": "Never mind",
|
|
64
|
+
"cancelDismissButtonAria": "Keep the restart",
|
|
65
|
+
"cancelFailed": "The restart could not be cancelled.",
|
|
66
|
+
"cancelFailedWith": "The restart could not be cancelled: {message}",
|
|
67
|
+
"cancelNotRequester": "This session did not request the pending restart, so nothing was cancelled.",
|
|
68
|
+
"cancelNothingPending": "There is no pending restart to cancel.",
|
|
69
|
+
"cancelUnknownSession": "The current session is unknown, so its own request cannot be withdrawn.",
|
|
70
|
+
"cancelNoRoute": "The cancel route is not available (the Host is too old).",
|
|
71
|
+
"backupChoiceTitle": "Automatic pre-restart backup: include the installed plugins too?",
|
|
72
|
+
"backupChoiceBody": "A snapshot of the current configuration is taken before every restart, so a host that fails to come back can be rolled back automatically.",
|
|
73
|
+
"backupChoiceWithPlugins": "Configuration + installed plugins",
|
|
74
|
+
"backupChoiceWithPluginsHint": "About 3 seconds and 117 MB more; a plugin that breaks the boot can be rolled back too.",
|
|
75
|
+
"backupChoiceConfigOnly": "Configuration only",
|
|
76
|
+
"backupChoiceConfigOnlyHint": "Instant, about 66 KB; plugin problems cannot be rolled back and need a manual reinstall.",
|
|
77
|
+
"backupChoiceFootnote": "You can change this later in Settings → Auto-restart & shut down",
|
|
78
|
+
"backupChoicePickPrimary": "Use this",
|
|
79
|
+
"backupChoicePickSecondary": "Configuration only",
|
|
80
|
+
"backupChoiceRecommended": "Recommended",
|
|
81
|
+
"backupChoiceDismissAria": "Close this one-time prompt",
|
|
82
|
+
"backupChoiceError": "Could not save the choice: {message}",
|
|
83
|
+
"supervisor": {
|
|
84
|
+
"title": "Let DSH bring itself back up?",
|
|
85
|
+
"body": "This hosted DSH instance is started by hand (or by something else) — when it dies it does not come back. I can install a supervisor of my own: it starts at login, it starts the host again when the process is gone, and it restores the last snapshot when the host does not come up. The supervisor never touches your session data; it only starts, watches and rolls back.",
|
|
86
|
+
"changes": "What it changes: a new <stateDir>/supervisor/ directory (the supervisor script, its configuration, its log and its lock); on Windows a login record (the scheduled task dsh-autorestart-web), on Linux a systemd user service. What it does NOT change: your session data, your model configuration, the installed plugins.",
|
|
87
|
+
"rights": "Which rights it needs: on Windows NO administrator rights (it runs \"at login\"; if the machine forbids ordinary users from creating scheduled tasks, it falls back to the startup folder, which needs none either); on Linux it needs write access to your own home directory (systemctl --user — an ordinary user is enough).",
|
|
88
|
+
"uninstall": "How to remove it: one click on \"Start at login → Uninstall\" in the settings; or by hand: schtasks /Delete /TN \"dsh-autorestart-web\" /F (on Linux: systemctl --user disable --now dsh-autorestart-web.service && rm ~/.config/systemd/user/dsh-autorestart-web.service). Both ways also delete the <stateDir>/supervisor/ directory.",
|
|
89
|
+
"install": "Install it for me (recommended)",
|
|
90
|
+
"installHint": "It writes a supervisor script into <stateDir>/supervisor/ and registers a login record on Windows / a systemd user service on Linux. No administrator rights needed. After that the host is started again within seconds when its process is gone; when it does not come up, the snapshot of the last restart is restored. You can uninstall it with one click at any time.",
|
|
91
|
+
"keep": "Keep the way I start it",
|
|
92
|
+
"keepHint": "Nothing is changed. Whatever starts the host today keeps starting it. The price: the host does not come back by itself when its process is gone — the restart only works while something else (a docker \"restart: always\", your own systemd unit) brings it back.",
|
|
93
|
+
"later": "Later",
|
|
94
|
+
"laterHint": "This card does not come back. You can install the supervisor by hand at any time in \"Settings → Auto-restart & shut down → Start at login\".",
|
|
95
|
+
"error": "That did not work: {message}",
|
|
96
|
+
"busy": "Working…"
|
|
46
97
|
},
|
|
47
98
|
"sleep": {
|
|
48
99
|
"button": {
|
|
@@ -61,26 +112,26 @@
|
|
|
61
112
|
},
|
|
62
113
|
"countdown": {
|
|
63
114
|
"caption": {
|
|
64
|
-
"20": "The whale is casting her net",
|
|
65
|
-
"19": "Your backup is already packed",
|
|
66
|
-
"18": "One unfinished thought, one more",
|
|
67
|
-
"17": "Everyone is quieting down",
|
|
68
|
-
"16": "One last lap around the room",
|
|
69
|
-
"15": "The sea is going dark",
|
|
70
|
-
"14": "One light at a time goes out",
|
|
71
|
-
"13": "The room has gone quiet",
|
|
72
|
-
"12": "A little bit left",
|
|
73
|
-
"11": "One last look at the screen",
|
|
74
|
-
"10": "The whale counts the last seconds",
|
|
75
|
-
"9": "Half the lights are out",
|
|
76
|
-
"8": "The voices get quieter",
|
|
77
|
-
"7": "The goodbyes are close",
|
|
78
|
-
"6": "One last sip of the sea",
|
|
79
|
-
"5": "The night wind gets lighter",
|
|
80
|
-
"4": "Say good night",
|
|
81
|
-
"3": "Three seconds, really",
|
|
82
|
-
"2": "Time for one last goodbye",
|
|
83
115
|
"1": "Good night 🌙",
|
|
116
|
+
"2": "Time for one last goodbye",
|
|
117
|
+
"3": "Three seconds, really",
|
|
118
|
+
"4": "Say good night",
|
|
119
|
+
"5": "The night wind gets lighter",
|
|
120
|
+
"6": "One last sip of the sea",
|
|
121
|
+
"7": "The goodbyes are close",
|
|
122
|
+
"8": "The voices get quieter",
|
|
123
|
+
"9": "Half the lights are out",
|
|
124
|
+
"10": "The whale counts the last seconds",
|
|
125
|
+
"11": "One last look at the screen",
|
|
126
|
+
"12": "A little bit left",
|
|
127
|
+
"13": "The room has gone quiet",
|
|
128
|
+
"14": "One light at a time goes out",
|
|
129
|
+
"15": "The sea is going dark",
|
|
130
|
+
"16": "One last lap around the room",
|
|
131
|
+
"17": "Everyone is quieting down",
|
|
132
|
+
"18": "One unfinished thought, one more",
|
|
133
|
+
"19": "Your backup is already packed",
|
|
134
|
+
"20": "The whale is casting her net",
|
|
84
135
|
"default": "Good night 🌙"
|
|
85
136
|
},
|
|
86
137
|
"remaining": "{n} seconds left",
|
|
@@ -117,11 +168,143 @@
|
|
|
117
168
|
"error": "Could not save the choice: {message}"
|
|
118
169
|
}
|
|
119
170
|
},
|
|
171
|
+
"nav": "Auto-restart & shut down",
|
|
172
|
+
"settings": {
|
|
173
|
+
"groups": {
|
|
174
|
+
"restart": "Restart settings",
|
|
175
|
+
"shutdown": "Shutdown settings",
|
|
176
|
+
"supervisor": "Start at login",
|
|
177
|
+
"report": "Report a problem"
|
|
178
|
+
},
|
|
179
|
+
"seconds": {
|
|
180
|
+
"label": "Shutdown countdown seconds",
|
|
181
|
+
"immediate": "No countdown (switch off right away)",
|
|
182
|
+
"value": "{n} seconds",
|
|
183
|
+
"hint": "Once every conversation is done, this countdown is shown on screen (pick \"Never mind, keep it on\" to cancel); the machine goes off when it reaches zero. Raise it when you want more time to write."
|
|
184
|
+
},
|
|
185
|
+
"restartGrace": {
|
|
186
|
+
"label": "Wait before the restart",
|
|
187
|
+
"hint": "A restart has no countdown: the host restarts as soon as the conversations are done. Raise this if you rely on a memory or summary plugin that writes its notes after the conversation ended (default 0 seconds).",
|
|
188
|
+
"value": "{n} seconds",
|
|
189
|
+
"immediate": "Do not wait (restart right away)"
|
|
190
|
+
},
|
|
191
|
+
"backup": {
|
|
192
|
+
"label": "Backup before a restart",
|
|
193
|
+
"hint": "Take a snapshot of the current configuration before every restart, so a host that cannot boot again is rolled back automatically.",
|
|
194
|
+
"list": "What is backed up right now",
|
|
195
|
+
"count": "{n} copies right now",
|
|
196
|
+
"keep": "At most {n} copies are kept; the oldest one is deleted when there are more.",
|
|
197
|
+
"empty": "No backup yet; one is written before every restart.",
|
|
198
|
+
"path": "Where they live",
|
|
199
|
+
"copyPath": "Copy the path",
|
|
200
|
+
"copied": "The path is on the clipboard",
|
|
201
|
+
"copyFailed": "The copy failed — please select the path above by hand",
|
|
202
|
+
"withPlugins": "includes the installed plugins",
|
|
203
|
+
"withoutPlugins": "configuration only"
|
|
204
|
+
},
|
|
205
|
+
"backupPlugins": {
|
|
206
|
+
"label": "Include the installed plugins in the backup",
|
|
207
|
+
"hint": "Off: only the state of DSH itself is backed up — the plugin directory of the profile is left out."
|
|
208
|
+
},
|
|
209
|
+
"supervisor": {
|
|
210
|
+
"status": "Started by",
|
|
211
|
+
"installed": "the supervisor of this plugin",
|
|
212
|
+
"external": "your own way",
|
|
213
|
+
"unknown": "nobody knows",
|
|
214
|
+
"off": "switched off",
|
|
215
|
+
"capable": "Restart capability",
|
|
216
|
+
"capableYes": "available: the host comes back on its own",
|
|
217
|
+
"capableNo": "not available: the host does not come back on its own",
|
|
218
|
+
"install": "Install the supervisor",
|
|
219
|
+
"uninstall": "Uninstall the supervisor",
|
|
220
|
+
"hint": "The supervisor is a process of its own: it starts at login, it starts the host again when its process is gone, and it restores the last snapshot when the host does not come up. No administrator rights needed.",
|
|
221
|
+
"noInstallDesktop": "The desktop app restarts itself — there is no supervisor to install here.",
|
|
222
|
+
"noInstallExternal": "Another supervisor is already in charge of this host. A second one would start the host as well and both would fight over the port, so there is nothing to install here.",
|
|
223
|
+
"noInstallUnknown": "The host did not say who starts it, so there is nothing to install here.",
|
|
224
|
+
"access": "The current access link",
|
|
225
|
+
"accessHint": "The token changes with every restart, and so does the link. This one is always the current one.",
|
|
226
|
+
"pending": "The uninstall did not finish. Please run it by hand: {commands}",
|
|
227
|
+
"reason": "Why",
|
|
228
|
+
"command": "The restart command",
|
|
229
|
+
"derive": "Derive the restart command",
|
|
230
|
+
"apply": "Use this command",
|
|
231
|
+
"revert": "Undo",
|
|
232
|
+
"noCommand": "No replayable restart command could be derived",
|
|
233
|
+
"plan": "Dry run",
|
|
234
|
+
"reason.ok": "This command starts this DSH again.",
|
|
235
|
+
"reason.desktop-app": "The desktop app restarts itself; there is no command to write.",
|
|
236
|
+
"reason.not-a-web-host": "This DSH is not a web host: it has no port and no listener.",
|
|
237
|
+
"reason.argv-unavailable": "The command line cannot be read back (it was started in a special way), so nothing is guessed.",
|
|
238
|
+
"reason.external-supervisor": "Another supervisor is already in charge: a second one would fight over the port. The restart becomes \"exit gracefully and let it start me again\".",
|
|
239
|
+
"reason.container-no-init": "This machine has no \"at login\" (a container), so no automatic start is promised.",
|
|
240
|
+
"reason.port-not-fixable": "The port changes every time, so the browser would be logged out; the port has to be fixed first.",
|
|
241
|
+
"reason.supervised": "The supervisor of this plugin is in charge: the restart is handed to it.",
|
|
242
|
+
"status.external": "How I remember you were started: by your own script (the restart is its job — I stay out of it)",
|
|
243
|
+
"status.bareWritten": "How I remember you were started: restarted with the command you confirmed",
|
|
244
|
+
"status.bareUnknown": "I don't know yet how to start you again",
|
|
245
|
+
"status.unknown": "I cannot tell how you were started (I don't guess, and I won't start you on my own)",
|
|
246
|
+
"status.selfSupervised": "How I remember you were started: my own supervisor is in charge (the restart is its job)",
|
|
247
|
+
"status.container": "There is no 'at login' here: the orchestrator is in charge",
|
|
248
|
+
"redetect": "Detect again",
|
|
249
|
+
"deriveHelp": "Help me find out how to restart",
|
|
250
|
+
"technical": "Technical details",
|
|
251
|
+
"technicalHint": "What the plugin would actually run",
|
|
252
|
+
"previous": "What the undo restores",
|
|
253
|
+
"app": "the desktop app itself",
|
|
254
|
+
"startup": "How it was started",
|
|
255
|
+
"startup.bare": "started by hand (nobody manages it)",
|
|
256
|
+
"startup.external": "another supervisor is in charge",
|
|
257
|
+
"startup.container": "in a container (the orchestrator is in charge)",
|
|
258
|
+
"startup.desktop": "the desktop app restarts itself",
|
|
259
|
+
"startup.selfSupervised": "the supervisor of this plugin is in charge",
|
|
260
|
+
"startup.unknown": "it could not be established who started it",
|
|
261
|
+
"autostart": "How \"start at login\" works here"
|
|
262
|
+
},
|
|
263
|
+
"report": {
|
|
264
|
+
"label": "Something wrong?",
|
|
265
|
+
"github": "Copy the diagnostics and open a GitHub issue",
|
|
266
|
+
"hint": "The diagnostics carry no access token and no secrets — paste them into the issue and we can see what went wrong.",
|
|
267
|
+
"email": "No GitHub account? Write to {email}",
|
|
268
|
+
"copy": "Copy the diagnostics",
|
|
269
|
+
"copyFailed": "The copy failed — please copy this by hand",
|
|
270
|
+
"issueTitle": "A problem with the restart / start at login",
|
|
271
|
+
"issueBody": "Please paste the diagnostics you just copied below.\n\n## What happened\n\n\n## What you expected\n\n\n## How to reproduce\n"
|
|
272
|
+
},
|
|
273
|
+
"error": "Could not save the settings: {message}",
|
|
274
|
+
"autostartKind": {
|
|
275
|
+
"other": "something else",
|
|
276
|
+
"windows-task": "Windows scheduled task",
|
|
277
|
+
"windows-startup": "Windows startup folder",
|
|
278
|
+
"systemd-user": "systemd user service",
|
|
279
|
+
"systemd-system": "systemd system service",
|
|
280
|
+
"wsl-systemd": "systemd inside WSL",
|
|
281
|
+
"wsl-no-systemd": "WSL without systemd",
|
|
282
|
+
"container": "a container",
|
|
283
|
+
"desktop": "the desktop app itself",
|
|
284
|
+
"unknown": "cannot tell",
|
|
285
|
+
"pm2": "pm2 (a process manager)"
|
|
286
|
+
},
|
|
287
|
+
"startupWhy": {
|
|
288
|
+
"desktop": "this process is the desktop client itself — it relaunches itself",
|
|
289
|
+
"bare": "it was started by hand in a terminal; nobody manages it",
|
|
290
|
+
"external": "another supervisor started it",
|
|
291
|
+
"container": "it runs in a container; the orchestrator is in charge",
|
|
292
|
+
"unknown": "it could not be established how it was started",
|
|
293
|
+
"self-supervised": "the supervisor of this plugin started it"
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
"meta": {
|
|
297
|
+
"title": "dsh-autorestart — restart continuation",
|
|
298
|
+
"description": "Keeps agent work alive across a DeepSeek Harness restart: a durable checkpoint, goal re-arm or a continuation message on boot, and an idle-aware graceful restart the agent can request itself."
|
|
299
|
+
},
|
|
300
|
+
"text": {
|
|
301
|
+
"missing": "(this text is missing yet)"
|
|
302
|
+
},
|
|
120
303
|
"announcement": {
|
|
121
304
|
"title": "New feature: graceful shutdown",
|
|
122
305
|
"line1": "There is a new 🌙 \"Shut down\" button at the very bottom of the left sidebar.",
|
|
123
306
|
"line2": "Click it → once every conversation is done it counts down {n} seconds and powers the machine off.",
|
|
124
|
-
"
|
|
307
|
+
"line2.generic": "Click it → once every conversation is done it counts down a few seconds and powers the machine off.",
|
|
125
308
|
"line3": "To change that, go to \"Settings → Auto-restart & shut down\".",
|
|
126
309
|
"disabled": "The button is currently switched off; you can turn it on in the settings.",
|
|
127
310
|
"button": "Got it",
|