dsh-ssh-tui 0.7.2 → 0.7.4
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.en.md +113 -27
- package/README.md +90 -27
- package/cordis.patch.yml +15 -0
- package/docs/remote-ops.md +27 -1
- package/docs/terminals.md +126 -0
- package/docs/windows.md +128 -0
- package/lib/attach.js +14 -1
- package/lib/attach.js.map +1 -1
- package/lib/commands.js +1 -0
- package/lib/commands.js.map +1 -1
- package/lib/copy-text.js +2 -0
- package/lib/copy-text.js.map +1 -1
- package/lib/diag.js +38 -0
- package/lib/diag.js.map +1 -1
- package/lib/dialogs.js.map +1 -1
- package/lib/display-sock.js +572 -44
- package/lib/display-sock.js.map +1 -1
- package/lib/doctor.js +71 -37
- package/lib/doctor.js.map +1 -1
- package/lib/dsh-compat.js +252 -88
- package/lib/dsh-compat.js.map +1 -1
- package/lib/footer.js +4 -2
- package/lib/footer.js.map +1 -1
- package/lib/i18n/en.js +54 -10
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/index.js +18 -7
- package/lib/i18n/index.js.map +1 -1
- package/lib/i18n/zh.js +54 -10
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +111 -26
- package/lib/index.js.map +1 -1
- package/lib/line-mode.js +4 -0
- package/lib/line-mode.js.map +1 -1
- package/lib/paint.js +37 -3
- package/lib/paint.js.map +1 -1
- package/lib/picker.js +109 -18
- package/lib/picker.js.map +1 -1
- package/lib/plan.js +8 -0
- package/lib/plan.js.map +1 -1
- package/lib/platform.js +377 -11
- package/lib/platform.js.map +1 -1
- package/lib/preset-authoring.js +10 -14
- package/lib/preset-authoring.js.map +1 -1
- package/lib/preset-compat.js +100 -0
- package/lib/preset-compat.js.map +1 -0
- package/lib/preset-picker.js +5 -1
- package/lib/preset-picker.js.map +1 -1
- package/lib/preset-rows.js +119 -19
- package/lib/preset-rows.js.map +1 -1
- package/lib/provider-catalog.js +4 -4
- package/lib/question-wait.js +418 -0
- package/lib/question-wait.js.map +1 -0
- package/lib/route-memory.js +3 -3
- package/lib/route-memory.js.map +1 -1
- package/lib/selection.js +26 -8
- package/lib/selection.js.map +1 -1
- package/lib/session-index.js +5 -0
- package/lib/session-index.js.map +1 -1
- package/lib/session-lock.js +4 -1
- package/lib/session-lock.js.map +1 -1
- package/lib/session-route.js +3 -0
- package/lib/session-route.js.map +1 -1
- package/lib/settings-routes.js +10 -0
- package/lib/settings-routes.js.map +1 -0
- package/lib/settings-subagent.js +10 -0
- package/lib/settings-subagent.js.map +1 -0
- package/lib/subagent-model.js +5 -5
- package/lib/subagent-model.js.map +1 -1
- package/lib/supergrok-token.js +4 -0
- package/lib/supergrok-token.js.map +1 -1
- package/lib/term-text.js +79 -8
- package/lib/term-text.js.map +1 -1
- package/lib/terminal-caps.js +358 -0
- package/lib/terminal-caps.js.map +1 -0
- package/lib/terminal-input.js +13 -0
- package/lib/terminal-input.js.map +1 -1
- package/lib/tui.js +775 -165
- package/lib/tui.js.map +1 -1
- package/lib/types/attach.d.ts +8 -0
- package/lib/types/commands.d.ts +3 -0
- package/lib/types/diag.d.ts +20 -1
- package/lib/types/dialogs.d.ts +16 -0
- package/lib/types/display-sock.d.ts +141 -24
- package/lib/types/doctor.d.ts +8 -1
- package/lib/types/dsh-compat.d.ts +173 -44
- package/lib/types/footer.d.ts +3 -1
- package/lib/types/i18n/index.d.ts +31 -15
- package/lib/types/index.d.ts +45 -0
- package/lib/types/paint.d.ts +21 -0
- package/lib/types/picker.d.ts +25 -0
- package/lib/types/platform.d.ts +197 -10
- package/lib/types/preset-authoring.d.ts +8 -14
- package/lib/types/preset-compat.d.ts +58 -0
- package/lib/types/preset-picker.d.ts +1 -1
- package/lib/types/preset-rows.d.ts +63 -13
- package/lib/types/question-wait.d.ts +221 -0
- package/lib/types/selection.d.ts +12 -4
- package/lib/types/settings-routes.d.ts +16 -0
- package/lib/types/settings-subagent.d.ts +24 -0
- package/lib/types/subagent-model.d.ts +10 -10
- package/lib/types/term-text.d.ts +14 -0
- package/lib/types/terminal-caps.d.ts +105 -0
- package/lib/types/terminal-input.d.ts +10 -0
- package/lib/types/transcript-types.d.ts +22 -0
- package/lib/types/tui.d.ts +167 -10
- package/lib/types/update-check.d.ts +19 -4
- package/lib/types/workspace-changes.d.ts +135 -0
- package/lib/update-check.js +25 -8
- package/lib/update-check.js.map +1 -1
- package/lib/workspace-changes.js +120 -0
- package/lib/workspace-changes.js.map +1 -0
- package/package.json +124 -59
package/lib/platform.js
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* *comparison* outside this file — so the next one fails in review rather than on
|
|
14
14
|
* a desktop.
|
|
15
15
|
*/
|
|
16
|
+
import { spawnSync } from 'node:child_process';
|
|
17
|
+
import { chmodSync, existsSync } from 'node:fs';
|
|
16
18
|
import { homedir } from 'node:os';
|
|
17
19
|
import { join } from 'node:path';
|
|
18
20
|
/** This process is running on Windows. */
|
|
@@ -47,21 +49,184 @@ export function usesProcessIdentity(platform = process.platform) {
|
|
|
47
49
|
* POSIX wants `detached: true` (setsid) so the Host survives the launcher and a
|
|
48
50
|
* hung-up terminal.
|
|
49
51
|
*
|
|
50
|
-
* Windows is the opposite: `detached: true` maps to
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* not
|
|
52
|
+
* Windows is the opposite, and the trade-off is forced: `detached: true` maps to
|
|
53
|
+
* DETACHED_PROCESS, and Windows ignores CREATE_NO_WINDOW (what `windowsHide`
|
|
54
|
+
* sets) when DETACHED_PROCESS is present. Every console child the Host then
|
|
55
|
+
* starts — each tool call, every shell, node, git — allocates its own console,
|
|
56
|
+
* which is a visible window flashing over the TUI. So the Host is spawned
|
|
57
|
+
* non-detached and inherits the launcher's console; descendants inherit it too
|
|
58
|
+
* instead of creating one.
|
|
59
|
+
*
|
|
60
|
+
* The price *was* on the lifecycle side, and it was measured (on the Windows CI
|
|
61
|
+
* leg by `scripts/tui-mock-probe.mjs --busy`, not reasoned about): libuv assigns
|
|
62
|
+
* a non-detached child to its global job object, which is created with
|
|
63
|
+
* KILL_ON_JOB_CLOSE. The launcher died, the job closed, and the Host was
|
|
64
|
+
* terminated with it — a closed terminal window ended the session's compute.
|
|
65
|
+
*
|
|
66
|
+
* This function still returns `detached: false` there, because it describes a
|
|
67
|
+
* *direct* spawn, and a direct spawn cannot have both properties: Node exposes
|
|
68
|
+
* `detached` (DETACHED_PROCESS, which makes Windows ignore CREATE_NO_WINDOW) and
|
|
69
|
+
* `windowsHide`, but not `CREATE_NEW_CONSOLE`. The way out is to not spawn the
|
|
70
|
+
* Host directly on Windows: {@link hostBootstrapCommand} starts it through the
|
|
71
|
+
* OS PowerShell, which *can* ask for a new console and hide it. This direct path
|
|
72
|
+
* stays as the fallback for a Windows box without PowerShell.
|
|
60
73
|
*/
|
|
61
74
|
export function hostSpawnOptions(platform = process.platform) {
|
|
62
75
|
const windows = platform === 'win32';
|
|
63
76
|
return { detached: !windows, windowsHide: true };
|
|
64
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The Windows PowerShell that ships with the operating system.
|
|
80
|
+
*
|
|
81
|
+
* `powershell.exe` and not `pwsh.exe`: the latter is PowerShell 7, an optional
|
|
82
|
+
* install, while 5.1 is part of Windows 10/11. `%SystemRoot%` is where it lives
|
|
83
|
+
* (`%windir%` is the legacy spelling of the same thing); when neither is set
|
|
84
|
+
* there is nothing to find, and the caller falls back to a direct spawn.
|
|
85
|
+
*/
|
|
86
|
+
export function windowsPowerShellPath(env = process.env) {
|
|
87
|
+
const raw = env.SystemRoot?.trim() || env.windir?.trim();
|
|
88
|
+
if (raw === undefined || raw === '')
|
|
89
|
+
return undefined;
|
|
90
|
+
// Spelled with backslashes explicitly rather than with `join`: this is a
|
|
91
|
+
// Windows path no matter which platform is asking, and the tests assert it on
|
|
92
|
+
// Linux. A trailing separator (or a forward slash, which Windows accepts) is
|
|
93
|
+
// normalised away so the result is the same string everywhere.
|
|
94
|
+
const root = raw.replaceAll('/', '\\').replace(/\\+$/u, '');
|
|
95
|
+
return `${root}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
96
|
+
}
|
|
97
|
+
/** A PowerShell single-quoted literal; `''` is the only escape inside one. */
|
|
98
|
+
export function psQuote(value) {
|
|
99
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Quote one argv array into a Windows command line, the way `CreateProcess`
|
|
103
|
+
* parses it back (the rule from "Everyone quotes command line arguments the
|
|
104
|
+
* wrong way"): wrap in double quotes when the argument is empty or contains
|
|
105
|
+
* whitespace or a quote, double the backslashes that precede a quote, and double
|
|
106
|
+
* trailing backslashes before the closing quote. `Start-Process -ArgumentList`
|
|
107
|
+
* joins its array with spaces and adds no quoting of its own, so the line has to
|
|
108
|
+
* be right before it is handed over — a `DSH_HOME` with a space in it is the
|
|
109
|
+
* normal case, not the exotic one.
|
|
110
|
+
*/
|
|
111
|
+
export function windowsCommandLine(argv) {
|
|
112
|
+
return argv.map(quoteWindowsArgument).join(' ');
|
|
113
|
+
}
|
|
114
|
+
function quoteWindowsArgument(argument) {
|
|
115
|
+
if (argument !== '' && !/[\s"]/u.test(argument))
|
|
116
|
+
return argument;
|
|
117
|
+
let quoted = '"';
|
|
118
|
+
let backslashes = 0;
|
|
119
|
+
for (const character of argument) {
|
|
120
|
+
if (character === '\\') {
|
|
121
|
+
backslashes += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (character === '"') {
|
|
125
|
+
quoted += '\\'.repeat(backslashes * 2 + 1) + '"';
|
|
126
|
+
backslashes = 0;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
quoted += '\\'.repeat(backslashes) + character;
|
|
130
|
+
backslashes = 0;
|
|
131
|
+
}
|
|
132
|
+
return quoted + '\\'.repeat(backslashes * 2) + '"';
|
|
133
|
+
}
|
|
134
|
+
/** `-EncodedCommand` wants base64 of UTF-16LE, which is also what dodges quoting. */
|
|
135
|
+
export function encodePowerShellCommand(script) {
|
|
136
|
+
return Buffer.from(script, 'utf16le').toString('base64');
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The bootstrap script: start the Host with **its own console, hidden**, and
|
|
140
|
+
* print its pid so the launcher can watch it.
|
|
141
|
+
*
|
|
142
|
+
* `Start-Process -WindowStyle Hidden` is the only way to ask for this from a
|
|
143
|
+
* Node process. It is ShellExecuteEx/CreateProcess with `CREATE_NEW_CONSOLE` and
|
|
144
|
+
* `SW_HIDE`: the Host gets a console of its own, so closing the user's terminal
|
|
145
|
+
* no longer takes it down, and that console is invisible, so the tool calls that
|
|
146
|
+
* inherit it do not flash. `-PassThru` gives the object whose `Id` is printed;
|
|
147
|
+
* `-RedirectStandardError` keeps the Host's stderr log, which the direct spawn
|
|
148
|
+
* used to feed through an inherited fd.
|
|
149
|
+
*
|
|
150
|
+
* The whole script travels as an encoded command, so nothing in it is ever
|
|
151
|
+
* re-parsed by a shell.
|
|
152
|
+
*/
|
|
153
|
+
export function hiddenConsoleHostScript(options) {
|
|
154
|
+
const redirect = options.stderrFile === undefined
|
|
155
|
+
? ''
|
|
156
|
+
: ` -RedirectStandardError ${psQuote(options.stderrFile)}`;
|
|
157
|
+
return [
|
|
158
|
+
`$ErrorActionPreference = 'Stop'`,
|
|
159
|
+
`$dshHost = Start-Process -FilePath ${psQuote(options.execPath)}`
|
|
160
|
+
+ ` -ArgumentList ${psQuote(windowsCommandLine(options.argv))}`
|
|
161
|
+
+ ` -WindowStyle Hidden -PassThru${redirect}`,
|
|
162
|
+
// Written to a file rather than to stdout: the launcher's pipe to this
|
|
163
|
+
// script would be inherited by the Host, and whoever reads that pipe waits
|
|
164
|
+
// for the Host to exit. A pid file has no such handle.
|
|
165
|
+
`[IO.File]::WriteAllText(${psQuote(options.pidFile)}, [string]$dshHost.Id)`,
|
|
166
|
+
].join('; ');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Set on the Host's environment by the bootstrap below, through
|
|
170
|
+
* {@link bootstrapEnv}.
|
|
171
|
+
*
|
|
172
|
+
* The Host cannot ask whether it has a console of its own — Node exposes no such
|
|
173
|
+
* question — so the launcher tells it. That is what lets the one build where the
|
|
174
|
+
* lifecycle promise does not hold (Windows, no PowerShell, direct child) say so
|
|
175
|
+
* at boot instead of letting the user discover it by closing the window.
|
|
176
|
+
*/
|
|
177
|
+
export const TUI_HOST_START_ENV = 'DSH_TUI_HOST_START';
|
|
178
|
+
/** Marker value for a Host started with a hidden console of its own. */
|
|
179
|
+
export const TUI_HOST_START_BOOTSTRAP = 'hidden-console';
|
|
180
|
+
/** The environment the bootstrap hands to the Host: the marker is added here. */
|
|
181
|
+
export function bootstrapEnv(env = process.env) {
|
|
182
|
+
return { ...env, [TUI_HOST_START_ENV]: TUI_HOST_START_BOOTSTRAP };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Whether this Host survives its terminal being closed.
|
|
186
|
+
*
|
|
187
|
+
* POSIX always does (`setsid`); Windows does exactly when the bootstrap started
|
|
188
|
+
* it. A Windows Host without the marker is the fallback direct child, which
|
|
189
|
+
* libuv's `KILL_ON_JOB_CLOSE` job takes down with the launcher — the one case
|
|
190
|
+
* worth a boot notice.
|
|
191
|
+
*/
|
|
192
|
+
export function hostHasOwnConsole(env = process.env, platform = process.platform) {
|
|
193
|
+
return platform !== 'win32' || env[TUI_HOST_START_ENV] === TUI_HOST_START_BOOTSTRAP;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The hidden-console bootstrap for this platform, or `undefined` when the Host
|
|
197
|
+
* should be spawned directly.
|
|
198
|
+
*
|
|
199
|
+
* Windows only, and only when the OS PowerShell is really there: the caller
|
|
200
|
+
* falls back to {@link hostSpawnOptions}, which keeps the boot working (without
|
|
201
|
+
* the survival property) on a machine where PowerShell is missing or blocked.
|
|
202
|
+
* `exists` is injectable so the decision is assertable on Linux.
|
|
203
|
+
*/
|
|
204
|
+
export function hostBootstrapCommand(options) {
|
|
205
|
+
if ((options.platform ?? process.platform) !== 'win32')
|
|
206
|
+
return undefined;
|
|
207
|
+
const powerShell = windowsPowerShellPath(options.env ?? process.env);
|
|
208
|
+
if (powerShell === undefined)
|
|
209
|
+
return undefined;
|
|
210
|
+
if (!(options.exists ?? existsSync)(powerShell))
|
|
211
|
+
return undefined;
|
|
212
|
+
const script = hiddenConsoleHostScript({
|
|
213
|
+
execPath: options.execPath,
|
|
214
|
+
argv: options.argv,
|
|
215
|
+
pidFile: options.pidFile,
|
|
216
|
+
...options.stderrFile === undefined ? {} : { stderrFile: options.stderrFile },
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
command: powerShell,
|
|
220
|
+
args: [
|
|
221
|
+
'-NoProfile',
|
|
222
|
+
'-NonInteractive',
|
|
223
|
+
'-ExecutionPolicy',
|
|
224
|
+
'Bypass',
|
|
225
|
+
'-EncodedCommand',
|
|
226
|
+
encodePowerShellCommand(script),
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
65
230
|
/**
|
|
66
231
|
* A path a human can read, with the platform's own shorthand.
|
|
67
232
|
*
|
|
@@ -79,10 +244,211 @@ export function displayHomePath(home, file, options = {}) {
|
|
|
79
244
|
return `${home}\\${file}`.replaceAll('/', '\\');
|
|
80
245
|
}
|
|
81
246
|
const userHome = options.userHome ?? homedir();
|
|
247
|
+
// `home` is the DSH home, so equality means DSH_HOME *is* the user's home (a
|
|
248
|
+
// real, if unusual, configuration): the file sits directly in `~`, not in a
|
|
249
|
+
// `.dsh` directory that is not there.
|
|
82
250
|
if (home === userHome)
|
|
83
|
-
return
|
|
251
|
+
return `~/${file}`;
|
|
84
252
|
if (home.startsWith(`${userHome}/`))
|
|
85
253
|
return `~/${home.slice(userHome.length + 1)}/${file}`;
|
|
86
254
|
return join(home, file);
|
|
87
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Who owns the files this plugin writes, and how to keep it that way.
|
|
258
|
+
*
|
|
259
|
+
* On POSIX the `mode` passed to `writeFile`/`mkdir` (`0o600`, `0o700`) is the
|
|
260
|
+
* whole story. **On Windows it is silently ignored**, and the files that matter
|
|
261
|
+
* here are not cosmetic: `env.cmd` carries API keys, the SuperGrok token file
|
|
262
|
+
* carries an OAuth grant, and the lock/socket directories carry session
|
|
263
|
+
* metadata. What they get instead is the ACL inherited from their parent — fine
|
|
264
|
+
* under `%USERPROFILE%\.dsh`, and *not* fine when `DSH_HOME` points somewhere
|
|
265
|
+
* shared (`C:\dsh`, a network share, a machine where `Users` can read the
|
|
266
|
+
* directory), which is exactly when nobody notices.
|
|
267
|
+
*
|
|
268
|
+
* So the intent is applied explicitly: `icacls` with inheritance removed and a
|
|
269
|
+
* single grant to the current user. The argv is built by a pure function so it
|
|
270
|
+
* can be asserted on Linux; applying it is best-effort by design — a machine
|
|
271
|
+
* without `icacls`, or a path held open by another process, must not fail the
|
|
272
|
+
* write that just succeeded. Failing closed here would mean a TUI that cannot
|
|
273
|
+
* save its own settings.
|
|
274
|
+
*/
|
|
275
|
+
/**
|
|
276
|
+
* The account `icacls` should grant, or undefined when the environment has none.
|
|
277
|
+
*
|
|
278
|
+
* `%USERDOMAIN%\%USERNAME%` where both are present: on a domain-joined machine a
|
|
279
|
+
* bare name can resolve to the machine-local account of the same name, and a
|
|
280
|
+
* grant to the wrong account — with inheritance already removed — would leave
|
|
281
|
+
* the user's own API keys inaccessible to them.
|
|
282
|
+
*/
|
|
283
|
+
export function aclUserName(env = process.env, platform = process.platform) {
|
|
284
|
+
if (platform !== 'win32')
|
|
285
|
+
return undefined;
|
|
286
|
+
const name = (env.USERNAME ?? env.USER ?? '').trim();
|
|
287
|
+
if (name === '')
|
|
288
|
+
return undefined;
|
|
289
|
+
const domain = (env.USERDOMAIN ?? '').trim();
|
|
290
|
+
// A name that already carries a domain (`DOMAIN\user`) is left alone.
|
|
291
|
+
if (domain === '' || name.includes('\\'))
|
|
292
|
+
return name;
|
|
293
|
+
return `${domain}\\${name}`;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* The exact `icacls` arguments for one path.
|
|
297
|
+
*
|
|
298
|
+
* `/inheritance:r` drops what the parent offered (this is the part that matters:
|
|
299
|
+
* adding a grant without removing inheritance leaves `Users` in place), and
|
|
300
|
+
* `(OI)(CI)` makes a directory's grant apply to what is created inside it —
|
|
301
|
+
* without it every new lock file would need its own call.
|
|
302
|
+
*/
|
|
303
|
+
export function restrictPathArgs(path, user, options = {}) {
|
|
304
|
+
const grant = options.directory === true ? `${user}:(OI)(CI)F` : `${user}:F`;
|
|
305
|
+
return [path, '/inheritance:r', '/grant:r', grant];
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Apply `mode` on POSIX, a single-user ACL on Windows.
|
|
309
|
+
*
|
|
310
|
+
* Returns whether the restriction was applied. Never throws: the caller has
|
|
311
|
+
* already written the file, and a permission call is not worth losing it over.
|
|
312
|
+
*/
|
|
313
|
+
export async function restrictPathToUser(path, options = { mode: 0o600 }) {
|
|
314
|
+
const platform = options.platform ?? process.platform;
|
|
315
|
+
try {
|
|
316
|
+
if (platform !== 'win32') {
|
|
317
|
+
const { chmod } = await import('node:fs/promises');
|
|
318
|
+
await chmod(path, options.mode);
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
const user = aclUserName(options.env, platform);
|
|
322
|
+
if (user === undefined)
|
|
323
|
+
return false;
|
|
324
|
+
const args = restrictPathArgs(path, user, { directory: options.directory === true });
|
|
325
|
+
if (options.run !== undefined)
|
|
326
|
+
return options.run('icacls', args);
|
|
327
|
+
return await runIcaclsAsync('icacls', args);
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/** Synchronous twin, for the sites that create their file with `openSync`. */
|
|
334
|
+
export function restrictPathToUserSync(path, options = { mode: 0o600 }) {
|
|
335
|
+
const platform = options.platform ?? process.platform;
|
|
336
|
+
try {
|
|
337
|
+
if (platform !== 'win32') {
|
|
338
|
+
chmodSync(path, options.mode);
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
const user = aclUserName(options.env, platform);
|
|
342
|
+
if (user === undefined)
|
|
343
|
+
return false;
|
|
344
|
+
const run = options.run ?? runIcaclsSync;
|
|
345
|
+
return run('icacls', restrictPathArgs(path, user, { directory: options.directory === true }));
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Whether the terminal in front of us can paint UTF-8 at all.
|
|
353
|
+
*
|
|
354
|
+
* The chrome this TUI draws — rules, status dots, the warning glyph — is
|
|
355
|
+
* Unicode. A console whose output code page is not UTF-8 (a legacy Windows
|
|
356
|
+
* console left on CP936 or CP437) and a POSIX locale that is not UTF-8 both
|
|
357
|
+
* render those bytes as mojibake, which is worse than plain ASCII: the frame
|
|
358
|
+
* still has to line up. This answers only "can it decode UTF-8", never "which
|
|
359
|
+
* terminal", so the capability table stays where it is.
|
|
360
|
+
*
|
|
361
|
+
* The decision is read from the environment, which is all a portable test can
|
|
362
|
+
* assert: `DSH_TUI_ASCII=1` forces the fallback and `=0` forbids it; otherwise
|
|
363
|
+
* a locale naming a non-UTF-8 charset (or the bare `C` / `POSIX`) opts in,
|
|
364
|
+
* and so does a Windows console whose output code page is recorded as anything
|
|
365
|
+
* but 65001. An unset locale is left alone — a UTF-8 SSH session often exports
|
|
366
|
+
* nothing, and guessing "ASCII" there would strip the chrome from the audience
|
|
367
|
+
* this TUI is built for.
|
|
368
|
+
* @param env - the environment to read (tests pass their own).
|
|
369
|
+
* @param platform - the platform the decision is for.
|
|
370
|
+
* @returns true when chrome must be drawn in ASCII.
|
|
371
|
+
*/
|
|
372
|
+
export function asciiFallbackEnabled(env = process.env, platform = process.platform) {
|
|
373
|
+
const forced = (env.DSH_TUI_ASCII ?? '').trim().toLowerCase();
|
|
374
|
+
if (['1', 'true', 'on', 'yes', 'ascii'].includes(forced))
|
|
375
|
+
return true;
|
|
376
|
+
if (['0', 'false', 'off', 'no', 'utf8', 'utf-8'].includes(forced))
|
|
377
|
+
return false;
|
|
378
|
+
if (localeCharsetIsUtf8(env) === false)
|
|
379
|
+
return true;
|
|
380
|
+
if (platform === 'win32' && windowsOutputIsUtf8(env) === false)
|
|
381
|
+
return true;
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* `true` when a locale variable names UTF-8, `false` when it names something
|
|
386
|
+
* else, and `undefined` when none of them says — so the caller can tell "the
|
|
387
|
+
* user told us" apart from "nobody said".
|
|
388
|
+
*
|
|
389
|
+
* `LC_ALL` wins over `LC_CTYPE` over `LANG`, matching the order a libc consults
|
|
390
|
+
* them in. `LC_MESSAGES` is deliberately absent: it picks the translation, not
|
|
391
|
+
* the encoding the terminal decodes bytes with.
|
|
392
|
+
*/
|
|
393
|
+
function localeCharsetIsUtf8(env) {
|
|
394
|
+
for (const key of ['LC_ALL', 'LC_CTYPE', 'LANG']) {
|
|
395
|
+
const raw = (env[key] ?? '').trim();
|
|
396
|
+
if (raw === '')
|
|
397
|
+
continue;
|
|
398
|
+
const value = raw.toLowerCase().replace(/-/gu, '');
|
|
399
|
+
if (value === 'c' || value === 'posix')
|
|
400
|
+
return false;
|
|
401
|
+
const charset = value.split('.')[1] ?? '';
|
|
402
|
+
if (charset === '')
|
|
403
|
+
return undefined;
|
|
404
|
+
return charset === 'utf8';
|
|
405
|
+
}
|
|
406
|
+
return undefined;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* `true` when a Windows console says its output code page is UTF-8 (65001),
|
|
410
|
+
* `false` for any other recorded page, `undefined` when nothing was recorded.
|
|
411
|
+
*
|
|
412
|
+
* Node does not expose `GetConsoleOutputCP`, and spawning `chcp` on every paint
|
|
413
|
+
* is not an option, so the decision reads the variables a launcher can set:
|
|
414
|
+
* `DSH_TUI_CODEPAGE` (our own, for a wrapper that already asked) and
|
|
415
|
+
* `PYTHONIOENCODING` (a `cp936`-style tag is the one portable signal the wider
|
|
416
|
+
* ecosystem agrees on). An unset value is not evidence of a legacy page.
|
|
417
|
+
*/
|
|
418
|
+
function windowsOutputIsUtf8(env) {
|
|
419
|
+
const own = (env.DSH_TUI_CODEPAGE ?? '').trim().toLowerCase();
|
|
420
|
+
if (own !== '')
|
|
421
|
+
return own === '65001' || own === 'utf8' || own === 'utf-8';
|
|
422
|
+
const python = (env.PYTHONIOENCODING ?? '').trim().toLowerCase();
|
|
423
|
+
const charset = python.split(':')[0] ?? '';
|
|
424
|
+
if (charset === '')
|
|
425
|
+
return undefined;
|
|
426
|
+
if (charset === 'utf8' || charset === 'utf-8' || charset === 'cp65001')
|
|
427
|
+
return true;
|
|
428
|
+
if (/^(cp|oem)\d+$/u.test(charset) || charset === 'mbcs' || charset === 'ascii')
|
|
429
|
+
return false;
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
/** `icacls`, synchronously, with a hidden window and a short leash. */
|
|
433
|
+
function runIcaclsSync(command, args) {
|
|
434
|
+
const result = spawnSync(command, args, { stdio: 'ignore', windowsHide: true, timeout: 5_000 });
|
|
435
|
+
return result.status === 0;
|
|
436
|
+
}
|
|
437
|
+
/** Async form, so a slow `icacls` never blocks a paint. */
|
|
438
|
+
function runIcaclsAsync(command, args) {
|
|
439
|
+
return import('node:child_process').then(({ spawn }) => new Promise(resolve => {
|
|
440
|
+
try {
|
|
441
|
+
const child = spawn(command, args, { stdio: 'ignore', windowsHide: true });
|
|
442
|
+
const timer = setTimeout(() => { try {
|
|
443
|
+
child.kill();
|
|
444
|
+
}
|
|
445
|
+
catch { /* gone */ } resolve(false); }, 5_000);
|
|
446
|
+
child.on('error', () => { clearTimeout(timer); resolve(false); });
|
|
447
|
+
child.on('exit', code => { clearTimeout(timer); resolve(code === 0); });
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
resolve(false);
|
|
451
|
+
}
|
|
452
|
+
}));
|
|
453
|
+
}
|
|
88
454
|
//# sourceMappingURL=platform.js.map
|
package/lib/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,0CAA0C;AAC1C,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAA;AAEtD,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,WAA4B,OAAO,CAAC,QAAQ;IACvE,OAAO,QAAQ,KAAK,OAAO,CAAA;AAC7B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,WAA4B,OAAO,CAAC,QAAQ;IACtE,OAAO,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;AACpD,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,SAAS,CAAC,WAA4B,OAAO,CAAC,QAAQ;IACpE,OAAO,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAA;AACrD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAA4B,OAAO,CAAC,QAAQ;IAC9E,OAAO,QAAQ,KAAK,OAAO,CAAA;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAA4B,OAAO,CAAC,QAAQ;IAI3E,MAAM,OAAO,GAAG,QAAQ,KAAK,OAAO,CAAA;IACpC,OAAO,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAA;AAClD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,IAAY,EACZ,UAAsF,EAAE;IAExF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAA;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAA;IACtC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,CAAA;QAC/B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YACpG,OAAO,gBAAgB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QACpF,CAAC;QACD,OAAO,GAAG,IAAI,KAAK,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACjD,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,EAAE,CAAA;IAC9C,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,IAAI,EAAE,CAAA;IAC9C,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC;QAAE,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,EAAE,CAAA;IAC1F,OAAO,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
package/lib/preset-authoring.js
CHANGED
|
@@ -20,25 +20,14 @@
|
|
|
20
20
|
* every later launch falls back to the default.
|
|
21
21
|
* @module dsh-ssh-tui/preset-authoring
|
|
22
22
|
*/
|
|
23
|
-
import {
|
|
24
|
-
|
|
23
|
+
import { presetDirectory, renderPresetMetadata, } from './preset-compat.js';
|
|
24
|
+
export { presetDirectory };
|
|
25
25
|
/** The id shape discovery accepts; a directory that fails it is skipped. */
|
|
26
26
|
export const PRESET_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/u;
|
|
27
27
|
/** Whether an id would be discovered at all. */
|
|
28
28
|
export function validatePresetId(id) {
|
|
29
29
|
return PRESET_ID_PATTERN.test(id);
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
32
|
-
* The directory a preset owns.
|
|
33
|
-
*
|
|
34
|
-
* `AgentPreset.path` is the composition file the preset publishes, not the
|
|
35
|
-
* directory, so anything that writes beside it (display metadata) starts here.
|
|
36
|
-
* @param preset - a discovered preset.
|
|
37
|
-
* @returns the absolute preset directory.
|
|
38
|
-
*/
|
|
39
|
-
export function presetDirectory(preset) {
|
|
40
|
-
return dirname(preset.path);
|
|
41
|
-
}
|
|
42
31
|
export function isRefusal(value) {
|
|
43
32
|
return 'error' in value;
|
|
44
33
|
}
|
|
@@ -90,7 +79,12 @@ export function planMetadata(input) {
|
|
|
90
79
|
// publish, and silently deleting the file is not this command's business.
|
|
91
80
|
if (text === undefined)
|
|
92
81
|
return { error: 'empty-metadata', id: input.preset.id };
|
|
93
|
-
|
|
82
|
+
// The directory can only be absent on a host that lists declarative presets,
|
|
83
|
+
// which the trust check above already refused; this keeps the type honest.
|
|
84
|
+
const directory = presetDirectory(input.preset);
|
|
85
|
+
if (directory === undefined)
|
|
86
|
+
return { error: 'managed', id: input.preset.id };
|
|
87
|
+
return { kind: 'metadata', id: input.preset.id, directory, text };
|
|
94
88
|
}
|
|
95
89
|
/**
|
|
96
90
|
* Plan a deletion.
|
|
@@ -101,6 +95,8 @@ export function planMetadata(input) {
|
|
|
101
95
|
export function planDelete(input) {
|
|
102
96
|
if (!input.authorable)
|
|
103
97
|
return { error: 'read-only' };
|
|
98
|
+
if (input.preset.trust === undefined)
|
|
99
|
+
return { error: 'managed', id: input.preset.id };
|
|
104
100
|
if (input.preset.trust !== 'user')
|
|
105
101
|
return { error: 'system', id: input.preset.id };
|
|
106
102
|
if (input.current)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset-authoring.js","sourceRoot":"","sources":["../src/preset-authoring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"preset-authoring.js","sourceRoot":"","sources":["../src/preset-authoring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent-preset vocabulary, kept local on purpose.
|
|
3
|
+
*
|
|
4
|
+
* 0.1.7-rc.1 renamed and split the package this used to come from:
|
|
5
|
+
* `@deepseek-ai/dsh-agent-presets` (which stops at 0.1.6-alpha.2) became
|
|
6
|
+
* `@deepseek-ai/dsh-agent-preset` (a composition row) plus
|
|
7
|
+
* `@deepseek-ai/dsh-agent-preset-registry` (the `agentPresets` service). The
|
|
8
|
+
* registry's `AgentPreset` also dropped `path` and `trust`: presets are
|
|
9
|
+
* declarative rows there, not directories on disk.
|
|
10
|
+
*
|
|
11
|
+
* Importing either line's types would pin this plugin to that line, so both are
|
|
12
|
+
* read through one structural shape in which `path` and `trust` are optional —
|
|
13
|
+
* present on the 0.1.5 line, absent on 0.1.7. The display-metadata reader lived
|
|
14
|
+
* in the package that disappeared; the ~25 lines it was are vendored here with
|
|
15
|
+
* the same semantics (the file is `preset.yml`, every failure degrades to no
|
|
16
|
+
* metadata, because presentation is not a capability).
|
|
17
|
+
*
|
|
18
|
+
* What that costs, deliberately: on 0.1.7 there is no preset directory to write
|
|
19
|
+
* beside, so the authoring half of `/preset` (copy, delete, metadata edits) has
|
|
20
|
+
* nothing to plan against and refuses with `managed` instead of pretending. The
|
|
21
|
+
* registry carries `name`/`description` itself, so the picker still shows them.
|
|
22
|
+
* @module dsh-ssh-tui/preset-compat
|
|
23
|
+
*/
|
|
24
|
+
import { readFile } from 'node:fs/promises';
|
|
25
|
+
import { dirname, join } from 'node:path';
|
|
26
|
+
import yaml from 'js-yaml';
|
|
27
|
+
/** The optional display-metadata file beside a preset's composition. */
|
|
28
|
+
export const METADATA_FILE = 'preset.yml';
|
|
29
|
+
/**
|
|
30
|
+
* The directory a preset owns, when the host still exposes one.
|
|
31
|
+
*
|
|
32
|
+
* `AgentPreset.path` is the composition file the preset publishes, not the
|
|
33
|
+
* directory, so anything that writes beside it (display metadata) starts here.
|
|
34
|
+
* `undefined` on a host that lists declarative presets instead of directories.
|
|
35
|
+
* @param preset - a discovered preset.
|
|
36
|
+
* @returns the absolute preset directory, or undefined when there is none.
|
|
37
|
+
*/
|
|
38
|
+
export function presetDirectory(preset) {
|
|
39
|
+
return preset.path === undefined ? undefined : dirname(preset.path);
|
|
40
|
+
}
|
|
41
|
+
function text(value) {
|
|
42
|
+
if (typeof value !== 'string')
|
|
43
|
+
return undefined;
|
|
44
|
+
const trimmed = value.trim();
|
|
45
|
+
return trimmed === '' ? undefined : trimmed;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Read a preset's display metadata. Every failure degrades to no metadata: a
|
|
49
|
+
* preset whose display text is missing, malformed, or unreadable still mounts.
|
|
50
|
+
* @param directory - the preset directory to read `preset.yml` from.
|
|
51
|
+
* @returns the published name/description/order, or `{}`.
|
|
52
|
+
*/
|
|
53
|
+
export async function readPresetMetadata(directory) {
|
|
54
|
+
let raw;
|
|
55
|
+
try {
|
|
56
|
+
raw = await readFile(join(directory, METADATA_FILE), 'utf8');
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
let parsed;
|
|
62
|
+
try {
|
|
63
|
+
parsed = yaml.load(raw);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
69
|
+
return {};
|
|
70
|
+
const record = parsed;
|
|
71
|
+
const name = text(record.name);
|
|
72
|
+
const description = text(record.description);
|
|
73
|
+
const order = typeof record.order === 'number' && Number.isFinite(record.order) ? record.order : undefined;
|
|
74
|
+
return {
|
|
75
|
+
...name === undefined ? {} : { name },
|
|
76
|
+
...description === undefined ? {} : { description },
|
|
77
|
+
...order === undefined ? {} : { order },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Render display metadata as the file's contents.
|
|
82
|
+
*
|
|
83
|
+
* Absent fields are omitted rather than written empty, so a preset with no
|
|
84
|
+
* description does not ship a key that reads as an intentional blank.
|
|
85
|
+
* @param metadata - the display text to store.
|
|
86
|
+
* @returns the YAML document, or undefined when there is nothing to store.
|
|
87
|
+
*/
|
|
88
|
+
export function renderPresetMetadata(metadata) {
|
|
89
|
+
const name = text(metadata.name);
|
|
90
|
+
const description = text(metadata.description);
|
|
91
|
+
const { order } = metadata;
|
|
92
|
+
if (name === undefined && description === undefined && order === undefined)
|
|
93
|
+
return undefined;
|
|
94
|
+
return yaml.dump({
|
|
95
|
+
...name === undefined ? {} : { name },
|
|
96
|
+
...description === undefined ? {} : { description },
|
|
97
|
+
...order === undefined ? {} : { order },
|
|
98
|
+
}, { lineWidth: -1 });
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=preset-compat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-compat.js","sourceRoot":"","sources":["../src/preset-compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,IAAI,MAAM,SAAS,CAAA;AAE1B,wEAAwE;AACxE,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAA;AAmCzC;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,MAAmB;IACjD,OAAO,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AACrE,CAAC;AAED,SAAS,IAAI,CAAC,KAAc;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAA;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,SAAiB;IACxD,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAA;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAA;IACrF,MAAM,MAAM,GAAG,MAAiC,CAAA;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC9B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IAC5C,MAAM,KAAK,GAAG,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1G,OAAO;QACL,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;QACrC,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE;QACnD,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE;KACxC,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAwB;IAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAChC,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IAC9C,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAA;IAC1B,IAAI,IAAI,KAAK,SAAS,IAAI,WAAW,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC5F,OAAO,IAAI,CAAC,IAAI,CAAC;QACf,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;QACrC,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE;QACnD,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE;KACxC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAA;AACvB,CAAC"}
|
package/lib/preset-picker.js
CHANGED
|
@@ -60,7 +60,11 @@ export function presetOptionLabel(preset) {
|
|
|
60
60
|
export function groupPresets(presets, currentId) {
|
|
61
61
|
const groups = [];
|
|
62
62
|
for (const trust of ['system', 'user']) {
|
|
63
|
-
|
|
63
|
+
// A host that lists declarative presets records no trust at all (0.1.7);
|
|
64
|
+
// those are the deployment's own, so they group with the shipped ones.
|
|
65
|
+
// Filtering on a missing field would drop every preset and leave `/mode`
|
|
66
|
+
// empty on that line.
|
|
67
|
+
const members = presets.filter(preset => (preset.trust ?? 'system') === trust).sort(comparePresets);
|
|
64
68
|
if (members.length === 0)
|
|
65
69
|
continue;
|
|
66
70
|
groups.push({
|
package/lib/preset-picker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset-picker.js","sourceRoot":"","sources":["../src/preset-picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAyB/C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAiB,EAAE,KAAkB;IAClE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAA;IAC5B,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAA;IAC9B,IAAI,SAAS,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QACpF,OAAO,SAAS,GAAG,UAAU,CAAA;IAC/B,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC,CAAA;IAClE,IAAI,SAAS,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,CAAC,CAAA;IACjE,OAAO,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAmB,EAAE,SAAiB;IAC5E,MAAM,KAAK,GAAa,CAAC,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAC/F,IAAI,MAAM,CAAC,EAAE,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAA;IAC7D,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAC/D,CAAC;SAAM,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAChF,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAA;IACvC,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACvF,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAC1B,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,OAAO,WAAW,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,OAA+B,EAC/B,SAAiB;IAEjB,MAAM,MAAM,GAAwB,EAAE,CAAA;IACtC,KAAK,MAAM,KAAK,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAU,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"preset-picker.js","sourceRoot":"","sources":["../src/preset-picker.ts"],"names":[],"mappings":"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"}
|