@linxin666/dsh-ssh 0.3.5 → 0.3.9

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 CHANGED
@@ -24,294 +24,6 @@ window.__ModuleLoader__.load({
24
24
  terminal: "/api/dsh-ssh/terminal"
25
25
  };
26
26
  //#endregion
27
- //#region src/client/api.ts
28
- /**
29
- * Browser-side API client for the /api/dsh-ssh route family. The only data
30
- * access path the panel components use — plain fetch/WebSocket, same origin.
31
- */
32
- /** Error carrying the route's JSON error message. */
33
- var SshApiError = class extends Error {
34
- constructor(message) {
35
- super(message);
36
- this.name = "SshApiError";
37
- }
38
- };
39
- /** Extract the `.error` message from a JSON error body; undefined when absent. */
40
- function extractError(text) {
41
- if (text === "") return void 0;
42
- try {
43
- const parsed = JSON.parse(text);
44
- return typeof parsed?.error === "string" ? parsed.error : void 0;
45
- } catch {
46
- return;
47
- }
48
- }
49
- /** Parse a JSON response or throw an SshApiError. */
50
- async function readJson(response) {
51
- let body;
52
- try {
53
- body = await response.json();
54
- } catch {
55
- throw new SshApiError(`HTTP ${response.status}: invalid JSON response`);
56
- }
57
- if (!response.ok) throw new SshApiError(typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : `HTTP ${response.status}`);
58
- return body;
59
- }
60
- /** Query-string helper. */
61
- function query(params) {
62
- const search = new URLSearchParams();
63
- for (const [key, value] of Object.entries(params)) if (value !== void 0 && value !== "") search.set(key, String(value));
64
- const text = search.toString();
65
- return text === "" ? "" : "?" + text;
66
- }
67
- /** The browser half's only data entry point. */
68
- var SshApi = class {
69
- async listHosts(queryText) {
70
- return (await readJson(await fetch(SSH_API.hosts + query({ query: queryText })))).hosts;
71
- }
72
- async createHost(payload) {
73
- return (await readJson(await fetch(SSH_API.hosts, {
74
- method: "POST",
75
- headers: { "content-type": "application/json" },
76
- body: JSON.stringify(payload)
77
- }))).host;
78
- }
79
- async updateHost(alias, patch) {
80
- return (await readJson(await fetch(SSH_API.hosts + query({ alias }), {
81
- method: "PATCH",
82
- headers: { "content-type": "application/json" },
83
- body: JSON.stringify(patch)
84
- }))).host;
85
- }
86
- async deleteHost(alias) {
87
- await readJson(await fetch(SSH_API.hosts + query({ alias }), { method: "DELETE" }));
88
- }
89
- async importSshConfig() {
90
- return (await readJson(await fetch(SSH_API.importSshConfig, { method: "POST" }))).result;
91
- }
92
- async testHost(alias) {
93
- return (await readJson(await fetch(SSH_API.test, {
94
- method: "POST",
95
- headers: { "content-type": "application/json" },
96
- body: JSON.stringify({ alias })
97
- }))).result;
98
- }
99
- async exec(alias, command, timeoutMs) {
100
- return (await readJson(await fetch(SSH_API.exec, {
101
- method: "POST",
102
- headers: { "content-type": "application/json" },
103
- body: JSON.stringify({
104
- alias,
105
- command,
106
- timeoutMs
107
- })
108
- }))).result;
109
- }
110
- async cluster(options) {
111
- return (await readJson(await fetch(SSH_API.cluster, {
112
- method: "POST",
113
- headers: { "content-type": "application/json" },
114
- body: JSON.stringify(options)
115
- }))).results;
116
- }
117
- async ls(alias, path) {
118
- return (await readJson(await fetch(SSH_API.ls + query({
119
- alias,
120
- path
121
- })))).entries;
122
- }
123
- async listTunnels() {
124
- return (await readJson(await fetch(SSH_API.tunnel, {
125
- method: "POST",
126
- headers: { "content-type": "application/json" },
127
- body: JSON.stringify({ action: "list" })
128
- }))).tunnels;
129
- }
130
- async startTunnel(options) {
131
- return (await readJson(await fetch(SSH_API.tunnel, {
132
- method: "POST",
133
- headers: { "content-type": "application/json" },
134
- body: JSON.stringify({
135
- action: "start",
136
- ...options
137
- })
138
- }))).tunnel;
139
- }
140
- async stopTunnel(tunnelId) {
141
- return (await readJson(await fetch(SSH_API.tunnel, {
142
- method: "POST",
143
- headers: { "content-type": "application/json" },
144
- body: JSON.stringify({
145
- action: "stop",
146
- tunnelId
147
- })
148
- }))).ok;
149
- }
150
- async stopAllTunnels(alias) {
151
- return (await readJson(await fetch(SSH_API.tunnel, {
152
- method: "POST",
153
- headers: { "content-type": "application/json" },
154
- body: JSON.stringify({
155
- action: "stop-all",
156
- alias
157
- })
158
- }))).stopped;
159
- }
160
- /**
161
- * Upload one file (raw bytes) to a remote path. Progress arrives through
162
- * the NDJSON response stream; resolves when the result frame lands.
163
- */
164
- async uploadFile(file, alias, remotePath, onProgress) {
165
- const response = await fetch(SSH_API.upload + query({
166
- alias,
167
- remotePath
168
- }), {
169
- method: "POST",
170
- body: file
171
- });
172
- if (!response.ok || response.body === null) throw new SshApiError(extractError(await response.text().catch(() => "")) ?? `upload failed: HTTP ${response.status}`);
173
- const reader = response.body.getReader();
174
- const decoder = new TextDecoder();
175
- let buffer = "";
176
- let finalError;
177
- let sawResult = false;
178
- let transferredBytes = 0;
179
- for (;;) {
180
- const { done, value } = await reader.read();
181
- if (done) break;
182
- buffer += decoder.decode(value, { stream: true });
183
- const lines = buffer.split("\n");
184
- buffer = lines.pop() ?? "";
185
- for (const line of lines) {
186
- if (line.trim() === "") continue;
187
- let parsed;
188
- try {
189
- parsed = JSON.parse(line);
190
- } catch {
191
- continue;
192
- }
193
- if (parsed.type === "progress") onProgress?.(parsed.progress);
194
- else if (parsed.type === "result") {
195
- sawResult = true;
196
- if (parsed.ok) transferredBytes = parsed.transferredBytes ?? 0;
197
- finalError = parsed.ok ? void 0 : parsed.error ?? "upload failed";
198
- }
199
- }
200
- }
201
- if (finalError !== void 0) throw new SshApiError(finalError);
202
- if (!sawResult) throw new SshApiError("upload ended without a result frame — the transfer did not complete");
203
- return { transferredBytes };
204
- }
205
- /**
206
- * Download a remote file with client-side progress. Streams straight to
207
- * disk when the File System Access API is available (no full-file RAM
208
- * copy); otherwise falls back to an in-memory Blob.
209
- */
210
- async downloadFile(alias, remotePath, onProgress) {
211
- const response = await fetch(SSH_API.download + query({
212
- alias,
213
- remotePath
214
- }));
215
- if (!response.ok || response.body === null) throw new SshApiError(extractError(await response.text().catch(() => "")) ?? `download failed: HTTP ${response.status}`);
216
- const total = Number(response.headers.get("content-length") ?? "0");
217
- const disposition = response.headers.get("content-disposition") ?? "";
218
- const filename = /filename="([^"]+)"/.exec(disposition)?.[1] ?? remotePath.split("/").pop() ?? "download";
219
- const reader = response.body.getReader();
220
- const picker = typeof window !== "undefined" ? window.showSaveFilePicker : void 0;
221
- let streamed = false;
222
- let writable;
223
- const chunks = [];
224
- let received = 0;
225
- const progress = () => {
226
- onProgress?.({
227
- phase: "transferring",
228
- file: remotePath,
229
- transferred: received,
230
- total,
231
- percent: total > 0 ? Math.round(received / total * 1e3) / 10 : 0
232
- });
233
- };
234
- try {
235
- if (picker !== void 0) {
236
- writable = await (await picker.call(window, { suggestedName: filename })).createWritable();
237
- streamed = true;
238
- }
239
- } catch {}
240
- for (;;) {
241
- const { done, value } = await reader.read();
242
- if (done) break;
243
- if (writable !== void 0) await writable.write(value);
244
- else chunks.push(value);
245
- received += value.length;
246
- progress();
247
- }
248
- if (writable !== void 0) await writable.close();
249
- onProgress?.({
250
- phase: "done",
251
- file: remotePath,
252
- transferred: received,
253
- total: received > 0 ? received : total,
254
- percent: 100
255
- });
256
- return {
257
- blob: streamed ? void 0 : new Blob(chunks),
258
- filename,
259
- streamed,
260
- bytes: received
261
- };
262
- }
263
- /** Open a WebSocket terminal session. */
264
- openTerminal(alias, cols, rows) {
265
- const url = (window.location.protocol === "https:" ? "wss" : "ws") + "://" + window.location.host + SSH_API.terminal + query({
266
- alias,
267
- cols,
268
- rows
269
- });
270
- const socket = new WebSocket(url);
271
- const connection = {
272
- onReady: void 0,
273
- onOutput: void 0,
274
- onExit: void 0,
275
- send: (data) => {
276
- if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({
277
- type: "input",
278
- data
279
- }));
280
- },
281
- resize: (cols, rows) => {
282
- if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({
283
- type: "resize",
284
- cols,
285
- rows
286
- }));
287
- },
288
- close: () => {
289
- try {
290
- socket.close();
291
- } catch {}
292
- }
293
- };
294
- socket.onmessage = (event) => {
295
- let frame;
296
- try {
297
- frame = JSON.parse(event.data);
298
- } catch {
299
- return;
300
- }
301
- if (frame.type === "ready") connection.onReady?.();
302
- else if (frame.type === "output") connection.onOutput?.(frame.data);
303
- else if (frame.type === "exit") connection.onExit?.(frame.code, frame.error);
304
- };
305
- socket.onclose = () => {
306
- connection.onExit?.(null, "connection closed");
307
- };
308
- socket.onerror = () => {
309
- connection.onExit?.(null, "connection error");
310
- };
311
- return connection;
312
- }
313
- };
314
- //#endregion
315
27
  //#region src/client/locales.ts
316
28
  /**
317
29
  * dsh-ssh surface copy: zh is the key source, en mirrors every key.
@@ -396,6 +108,11 @@ window.__ModuleLoader__.load({
396
108
  "terminal.ready": "终端已连接({alias})",
397
109
  "terminal.exited": "终端已退出({alias})",
398
110
  "terminal.error": "终端错误:{error}",
111
+ "terminal.auth.title": "二次身份验证 (2FA / 交互式认证)",
112
+ "terminal.auth.submit": "提交验证码",
113
+ "terminal.auth.cancel": "取消",
114
+ "terminal.auth.placeholder": "请输入验证码或口令",
115
+ "terminal.auth.hint": "服务器请求进行交互式二次验证(输入仅本次生效,不落盘):",
399
116
  "transfer.selectHost": "选择主机",
400
117
  "transfer.remotePath": "远程路径",
401
118
  "transfer.remotePathHint": "如 /tmp/ 或 /home/user/app.tar.gz",
@@ -455,7 +172,8 @@ window.__ModuleLoader__.load({
455
172
  "common.loading": "加载中…",
456
173
  "common.error": "出错:{error}",
457
174
  "common.refresh": "刷新",
458
- "common.confirm": "确定"
175
+ "common.confirm": "确定",
176
+ "error.disabled": "SSH 插件在宿主端未启用。请前往「设置 → Web 插件 → SSH」打开 enabled 开关。"
459
177
  };
460
178
  const en = {
461
179
  "entry.label": "SSH",
@@ -537,6 +255,11 @@ window.__ModuleLoader__.load({
537
255
  "terminal.ready": "Terminal connected ({alias})",
538
256
  "terminal.exited": "Terminal exited ({alias})",
539
257
  "terminal.error": "Terminal error: {error}",
258
+ "terminal.auth.title": "Two-Factor Authentication (2FA)",
259
+ "terminal.auth.submit": "Submit Code",
260
+ "terminal.auth.cancel": "Cancel",
261
+ "terminal.auth.placeholder": "Enter code or passphrase",
262
+ "terminal.auth.hint": "The server requested interactive 2FA (session only, never saved):",
540
263
  "transfer.selectHost": "Select host",
541
264
  "transfer.remotePath": "Remote path",
542
265
  "transfer.remotePathHint": "e.g. /tmp/ or /home/user/app.tar.gz",
@@ -596,7 +319,8 @@ window.__ModuleLoader__.load({
596
319
  "common.loading": "Loading…",
597
320
  "common.error": "Error: {error}",
598
321
  "common.refresh": "Refresh",
599
- "common.confirm": "Confirm"
322
+ "common.confirm": "Confirm",
323
+ "error.disabled": "The SSH plugin is disabled on the host. Please enable it in Settings → Web Plugins → SSH."
600
324
  };
601
325
  /** Tiny interpolation: {name} -> value. */
602
326
  function t(dictionary, key, values) {
@@ -652,8 +376,305 @@ window.__ModuleLoader__.load({
652
376
  if (value !== "") return value;
653
377
  }
654
378
  }
655
- return TERMINAL_FONT_FALLBACK;
656
- }
379
+ return TERMINAL_FONT_FALLBACK;
380
+ }
381
+ //#endregion
382
+ //#region src/client/api.ts
383
+ /**
384
+ * Browser-side API client for the /api/dsh-ssh route family. The only data
385
+ * access path the panel components use — plain fetch/WebSocket, same origin.
386
+ */
387
+ /** Error carrying the route's JSON error message. */
388
+ var SshApiError = class extends Error {
389
+ constructor(message) {
390
+ super(message);
391
+ this.name = "SshApiError";
392
+ }
393
+ };
394
+ /** Extract the `.error` message from a JSON error body; undefined when absent. */
395
+ function extractError(text) {
396
+ if (text === "") return void 0;
397
+ try {
398
+ const parsed = JSON.parse(text);
399
+ return typeof parsed?.error === "string" ? parsed.error : void 0;
400
+ } catch {
401
+ return;
402
+ }
403
+ }
404
+ /** Parse a JSON response or throw an SshApiError. */
405
+ async function readJson(response) {
406
+ let body;
407
+ try {
408
+ body = await response.json();
409
+ } catch {
410
+ if (response.status === 404) throw new SshApiError(tt("error.disabled"));
411
+ throw new SshApiError(`HTTP ${response.status}: invalid JSON response`);
412
+ }
413
+ if (!response.ok) throw new SshApiError(typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : response.status === 404 ? tt("error.disabled") : `HTTP ${response.status}`);
414
+ return body;
415
+ }
416
+ /** Query-string helper. */
417
+ function query(params) {
418
+ const search = new URLSearchParams();
419
+ for (const [key, value] of Object.entries(params)) if (value !== void 0 && value !== "") search.set(key, String(value));
420
+ const text = search.toString();
421
+ return text === "" ? "" : "?" + text;
422
+ }
423
+ /** The browser half's only data entry point. */
424
+ var SshApi = class {
425
+ async listHosts(queryText) {
426
+ return (await readJson(await fetch(SSH_API.hosts + query({ query: queryText })))).hosts;
427
+ }
428
+ async createHost(payload) {
429
+ return (await readJson(await fetch(SSH_API.hosts, {
430
+ method: "POST",
431
+ headers: { "content-type": "application/json" },
432
+ body: JSON.stringify(payload)
433
+ }))).host;
434
+ }
435
+ async updateHost(alias, patch) {
436
+ return (await readJson(await fetch(SSH_API.hosts + query({ alias }), {
437
+ method: "PATCH",
438
+ headers: { "content-type": "application/json" },
439
+ body: JSON.stringify(patch)
440
+ }))).host;
441
+ }
442
+ async deleteHost(alias) {
443
+ await readJson(await fetch(SSH_API.hosts + query({ alias }), { method: "DELETE" }));
444
+ }
445
+ async importSshConfig() {
446
+ return (await readJson(await fetch(SSH_API.importSshConfig, { method: "POST" }))).result;
447
+ }
448
+ async testHost(alias) {
449
+ return (await readJson(await fetch(SSH_API.test, {
450
+ method: "POST",
451
+ headers: { "content-type": "application/json" },
452
+ body: JSON.stringify({ alias })
453
+ }))).result;
454
+ }
455
+ async exec(alias, command, timeoutMs) {
456
+ return (await readJson(await fetch(SSH_API.exec, {
457
+ method: "POST",
458
+ headers: { "content-type": "application/json" },
459
+ body: JSON.stringify({
460
+ alias,
461
+ command,
462
+ timeoutMs
463
+ })
464
+ }))).result;
465
+ }
466
+ async cluster(options) {
467
+ return (await readJson(await fetch(SSH_API.cluster, {
468
+ method: "POST",
469
+ headers: { "content-type": "application/json" },
470
+ body: JSON.stringify(options)
471
+ }))).results;
472
+ }
473
+ async ls(alias, path) {
474
+ return (await readJson(await fetch(SSH_API.ls + query({
475
+ alias,
476
+ path
477
+ })))).entries;
478
+ }
479
+ async listTunnels() {
480
+ return (await readJson(await fetch(SSH_API.tunnel, {
481
+ method: "POST",
482
+ headers: { "content-type": "application/json" },
483
+ body: JSON.stringify({ action: "list" })
484
+ }))).tunnels;
485
+ }
486
+ async startTunnel(options) {
487
+ return (await readJson(await fetch(SSH_API.tunnel, {
488
+ method: "POST",
489
+ headers: { "content-type": "application/json" },
490
+ body: JSON.stringify({
491
+ action: "start",
492
+ ...options
493
+ })
494
+ }))).tunnel;
495
+ }
496
+ async stopTunnel(tunnelId) {
497
+ return (await readJson(await fetch(SSH_API.tunnel, {
498
+ method: "POST",
499
+ headers: { "content-type": "application/json" },
500
+ body: JSON.stringify({
501
+ action: "stop",
502
+ tunnelId
503
+ })
504
+ }))).ok;
505
+ }
506
+ async stopAllTunnels(alias) {
507
+ return (await readJson(await fetch(SSH_API.tunnel, {
508
+ method: "POST",
509
+ headers: { "content-type": "application/json" },
510
+ body: JSON.stringify({
511
+ action: "stop-all",
512
+ alias
513
+ })
514
+ }))).stopped;
515
+ }
516
+ /**
517
+ * Upload one file (raw bytes) to a remote path. Progress arrives through
518
+ * the NDJSON response stream; resolves when the result frame lands.
519
+ */
520
+ async uploadFile(file, alias, remotePath, onProgress) {
521
+ const response = await fetch(SSH_API.upload + query({
522
+ alias,
523
+ remotePath
524
+ }), {
525
+ method: "POST",
526
+ body: file
527
+ });
528
+ if (!response.ok || response.body === null) throw new SshApiError(extractError(await response.text().catch(() => "")) ?? `upload failed: HTTP ${response.status}`);
529
+ const reader = response.body.getReader();
530
+ const decoder = new TextDecoder();
531
+ let buffer = "";
532
+ let finalError;
533
+ let sawResult = false;
534
+ let transferredBytes = 0;
535
+ for (;;) {
536
+ const { done, value } = await reader.read();
537
+ if (done) break;
538
+ buffer += decoder.decode(value, { stream: true });
539
+ const lines = buffer.split("\n");
540
+ buffer = lines.pop() ?? "";
541
+ for (const line of lines) {
542
+ if (line.trim() === "") continue;
543
+ let parsed;
544
+ try {
545
+ parsed = JSON.parse(line);
546
+ } catch {
547
+ continue;
548
+ }
549
+ if (parsed.type === "progress") onProgress?.(parsed.progress);
550
+ else if (parsed.type === "result") {
551
+ sawResult = true;
552
+ if (parsed.ok) transferredBytes = parsed.transferredBytes ?? 0;
553
+ finalError = parsed.ok ? void 0 : parsed.error ?? "upload failed";
554
+ }
555
+ }
556
+ }
557
+ if (finalError !== void 0) throw new SshApiError(finalError);
558
+ if (!sawResult) throw new SshApiError("upload ended without a result frame — the transfer did not complete");
559
+ return { transferredBytes };
560
+ }
561
+ /**
562
+ * Download a remote file with client-side progress. Streams straight to
563
+ * disk when the File System Access API is available (no full-file RAM
564
+ * copy); otherwise falls back to an in-memory Blob.
565
+ */
566
+ async downloadFile(alias, remotePath, onProgress) {
567
+ const response = await fetch(SSH_API.download + query({
568
+ alias,
569
+ remotePath
570
+ }));
571
+ if (!response.ok || response.body === null) throw new SshApiError(extractError(await response.text().catch(() => "")) ?? `download failed: HTTP ${response.status}`);
572
+ const total = Number(response.headers.get("content-length") ?? "0");
573
+ const disposition = response.headers.get("content-disposition") ?? "";
574
+ const filename = /filename="([^"]+)"/.exec(disposition)?.[1] ?? remotePath.split("/").pop() ?? "download";
575
+ const reader = response.body.getReader();
576
+ const picker = typeof window !== "undefined" ? window.showSaveFilePicker : void 0;
577
+ let streamed = false;
578
+ let writable;
579
+ const chunks = [];
580
+ let received = 0;
581
+ const progress = () => {
582
+ onProgress?.({
583
+ phase: "transferring",
584
+ file: remotePath,
585
+ transferred: received,
586
+ total,
587
+ percent: total > 0 ? Math.round(received / total * 1e3) / 10 : 0
588
+ });
589
+ };
590
+ try {
591
+ if (picker !== void 0) {
592
+ writable = await (await picker.call(window, { suggestedName: filename })).createWritable();
593
+ streamed = true;
594
+ }
595
+ } catch {}
596
+ for (;;) {
597
+ const { done, value } = await reader.read();
598
+ if (done) break;
599
+ if (writable !== void 0) await writable.write(value);
600
+ else chunks.push(value);
601
+ received += value.length;
602
+ progress();
603
+ }
604
+ if (writable !== void 0) await writable.close();
605
+ onProgress?.({
606
+ phase: "done",
607
+ file: remotePath,
608
+ transferred: received,
609
+ total: received > 0 ? received : total,
610
+ percent: 100
611
+ });
612
+ return {
613
+ blob: streamed ? void 0 : new Blob(chunks),
614
+ filename,
615
+ streamed,
616
+ bytes: received
617
+ };
618
+ }
619
+ /** Open a WebSocket terminal session. */
620
+ openTerminal(alias, cols, rows) {
621
+ const url = (window.location.protocol === "https:" ? "wss" : "ws") + "://" + window.location.host + SSH_API.terminal + query({
622
+ alias,
623
+ cols,
624
+ rows
625
+ });
626
+ const socket = new WebSocket(url);
627
+ const connection = {
628
+ onReady: void 0,
629
+ onOutput: void 0,
630
+ onExit: void 0,
631
+ onAuthPrompt: void 0,
632
+ send: (data) => {
633
+ if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({
634
+ type: "input",
635
+ data
636
+ }));
637
+ },
638
+ resize: (cols, rows) => {
639
+ if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({
640
+ type: "resize",
641
+ cols,
642
+ rows
643
+ }));
644
+ },
645
+ sendAuthResponse: (responses) => {
646
+ if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({
647
+ type: "auth_response",
648
+ responses
649
+ }));
650
+ },
651
+ close: () => {
652
+ try {
653
+ socket.close();
654
+ } catch {}
655
+ }
656
+ };
657
+ socket.onmessage = (event) => {
658
+ let frame;
659
+ try {
660
+ frame = JSON.parse(event.data);
661
+ } catch {
662
+ return;
663
+ }
664
+ if (frame.type === "ready") connection.onReady?.();
665
+ else if (frame.type === "output") connection.onOutput?.(frame.data);
666
+ else if (frame.type === "exit") connection.onExit?.(frame.code, frame.error);
667
+ else if (frame.type === "auth_prompt") connection.onAuthPrompt?.(frame.name, frame.instructions, frame.prompts);
668
+ };
669
+ socket.onclose = () => {
670
+ connection.onExit?.(null, "connection closed");
671
+ };
672
+ socket.onerror = () => {
673
+ connection.onExit?.(null, "connection error");
674
+ };
675
+ return connection;
676
+ }
677
+ };
657
678
  //#endregion
658
679
  //#region \0dsh-css:packages/dsh-ssh/src/client/panel/panel.module.css.mjs
659
680
  const css = "[data-pane=conversation],[class*=centerCol]{position:relative}[data-dsh-ssh-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0}html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [data-dsh-ssh-view]{display:block}html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [data-pane=conversation]>:not([data-dsh-ssh-view]),html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [class*=centerCol]>:not([data-dsh-ssh-view]){display:none!important}.mL8Uca_entry{box-sizing:border-box;width:100%;min-height:36px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:8px;align-items:center;gap:10px;padding:0 10px;font-size:13px;display:flex}.mL8Uca_entry:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_entry[data-active]{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);font-weight:600}.mL8Uca_entryIcon{flex:none;justify-content:center;align-items:center;width:24px;height:24px;display:inline-flex}.mL8Uca_entryIcon svg{width:18px;height:18px;display:block}.mL8Uca_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entry{border-radius:50%;justify-content:center;width:36px;min-height:36px;margin:0 auto 12px;padding:0}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entryLabel{display:none}.mL8Uca_view{overflow:hidden}.mL8Uca_panel{background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:10px;padding:14px 16px 16px;display:flex}.mL8Uca_panelHeader{flex:none;align-items:center;gap:10px;display:flex}.mL8Uca_panelTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;flex:1;margin:0;font-size:16px;font-weight:700}.mL8Uca_backButton{align-items:center;gap:4px;display:inline-flex}.mL8Uca_tabBar{border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;gap:2px;display:flex}.mL8Uca_tab{color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;border-radius:6px 6px 0 0;padding:7px 14px;font-size:13px}.mL8Uca_tab:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_tab[data-active]{color:var(--dsw-alias-label-primary);border-bottom-color:var(--dsw-alias-state-business-primary);font-weight:600}.mL8Uca_panelContent{flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.mL8Uca_tabBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow-y:auto}.mL8Uca_fillBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow:hidden}.mL8Uca_toolbar,.mL8Uca_controls{flex-wrap:wrap;flex:none;align-items:center;gap:8px;display:flex}.mL8Uca_controls .mL8Uca_input{flex:0 260px;min-width:150px}.mL8Uca_toolbarSpacer{flex:1}.mL8Uca_search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 260px;padding:6px 10px;font-size:13px}.mL8Uca_search::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_groupBySelect{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;padding:6px 8px;font-size:13px}.mL8Uca_groupSection+.mL8Uca_groupSection{border-top:1px solid var(--dsw-alias-border-l1)}.mL8Uca_groupHeader{background:var(--dsw-alias-bg-base,var(--dsw-specific-input-major));align-items:center;gap:8px;padding:4px 8px;display:flex;position:sticky;top:0}.mL8Uca_groupToggle{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;background:0 0;border:none;border-radius:6px;flex:1;align-items:center;gap:6px;padding:4px 6px;font-size:12.5px;font-weight:500;display:flex}.mL8Uca_groupToggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_groupChevron{border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);flex:none;width:7px;height:7px;margin-bottom:2px;transition:transform .12s;transform:rotate(45deg)}.mL8Uca_groupChevron[data-collapsed]{margin-bottom:0;transform:rotate(-45deg)}.mL8Uca_groupName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mL8Uca_groupCount{color:var(--dsw-alias-label-tertiary);flex:none;font-weight:400}.mL8Uca_tableWrap{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex:1;min-height:0;overflow:auto}.mL8Uca_table{border-collapse:collapse;width:100%;font-size:12.5px}.mL8Uca_table th{z-index:1;text-align:left;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;padding:8px 10px;font-weight:600;position:sticky;top:0}.mL8Uca_table td{border-bottom:1px solid var(--dsw-alias-separator-primary);vertical-align:top;padding:7px 10px}.mL8Uca_table tbody tr:last-child td{border-bottom:none}.mL8Uca_table tbody tr:hover td{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellMuted{color:var(--dsw-alias-label-tertiary)}.mL8Uca_actions{white-space:nowrap;align-items:center;gap:8px;display:flex}.mL8Uca_inlineTest{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:11.5px;display:inline-flex}.mL8Uca_inlineTest[data-status=ok]{color:var(--dsw-alias-state-success-primary)}.mL8Uca_inlineTest[data-status=fail]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;line-height:1.6;display:inline-block}.mL8Uca_badge[data-kind=key]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_badge[data-kind=password]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_badge[data-kind=agent],.mL8Uca_badge[data-status=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_badge[data-status=fail]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge[data-status=timeout]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_primaryButton{color:var(--dsw-alias-label-primary-foreground);background:var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}.mL8Uca_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}.mL8Uca_primaryButton:disabled{opacity:.5;cursor:default}.mL8Uca_ghostButton{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;white-space:nowrap;background:0 0;border-radius:8px;padding:5px 12px;font-size:12px}.mL8Uca_ghostButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_ghostButton:disabled{opacity:.45;cursor:default}.mL8Uca_iconButton{width:26px;height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:13px;display:inline-flex}.mL8Uca_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_linkButton{color:var(--dsw-alias-state-business-primary);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0;font-size:12px}.mL8Uca_linkButton:hover:not(:disabled){text-decoration:underline}.mL8Uca_linkButton:disabled{opacity:.45;cursor:default}.mL8Uca_linkButton[data-danger]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_spinner{border:2px solid var(--dsw-alias-state-business-primary);vertical-align:-1px;border-top-color:#0000;border-radius:50%;flex:none;width:11px;height:11px;animation:.8s linear infinite mL8Uca_dshSshSpin;display:inline-block}@keyframes mL8Uca_dshSshSpin{to{transform:rotate(360deg)}}.mL8Uca_banner{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;border-radius:8px;padding:8px 12px;font-size:12.5px;line-height:1.5}.mL8Uca_banner[data-kind=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_banner[data-kind=error]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_banner[data-kind=info]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_empty,.mL8Uca_loading{text-align:center;color:var(--dsw-alias-label-tertiary);padding:28px 12px;font-size:12.5px}.mL8Uca_modalBackdrop{z-index:40;background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.mL8Uca_modal{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(560px,100vw - 48px);max-height:calc(100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;gap:12px;padding:18px;display:flex;overflow-y:auto}.mL8Uca_modalTitle{margin:0;font-size:15px;font-weight:700}.mL8Uca_modalFooter{justify-content:flex-end;gap:10px;margin-top:4px;display:flex}.mL8Uca_formError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px}.mL8Uca_hint{color:var(--dsw-alias-label-tertiary);font-size:11.5px}.mL8Uca_field{flex-direction:column;gap:5px;display:flex}.mL8Uca_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.mL8Uca_input{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;outline:none;padding:7px 10px;font-family:inherit;font-size:13px}.mL8Uca_input:focus{border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_input::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_input:disabled{opacity:.55}.mL8Uca_formRow{grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px 12px;display:grid}.mL8Uca_radioRow{align-items:center;gap:16px;display:flex}.mL8Uca_radioLabel{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:6px;font-size:13px;display:inline-flex}.mL8Uca_termBody{flex-direction:column;flex:1;gap:8px;min-height:0;display:flex;overflow:hidden}.mL8Uca_termWrap{border:1px solid var(--dsw-alias-border-l1);background:#0b0e14;border-radius:10px;flex:1;min-height:0;position:relative;overflow:hidden}.mL8Uca_termContainer{position:absolute;inset:8px 10px}.mL8Uca_termPlaceholder{z-index:2;text-align:center;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-base);justify-content:center;align-items:center;padding:0 24px;font-size:12.5px;display:flex;position:absolute;inset:0}.mL8Uca_hiddenFile{display:none}.mL8Uca_browsePanel{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;display:flex;overflow:hidden}.mL8Uca_browseHeader{background:var(--dsw-alias-bg-layer-2);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:8px 10px;display:flex}.mL8Uca_browsePath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;overflow:hidden}.mL8Uca_browseList{max-height:240px;overflow-y:auto}.mL8Uca_dirRow{text-align:left;width:100%;font:inherit;color:var(--dsw-alias-label-primary);border:none;border-bottom:1px solid var(--dsw-alias-separator-primary);cursor:pointer;background:0 0;grid-template-columns:minmax(0,1fr) auto auto;gap:12px;padding:5px 10px;font-size:12.5px;display:grid}.mL8Uca_dirRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_dirRow[data-type=dir] .mL8Uca_dirName{color:var(--dsw-alias-state-business-primary)}.mL8Uca_dirRow[data-up]{color:var(--dsw-alias-label-secondary)}.mL8Uca_dirName{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.mL8Uca_dirType{color:var(--dsw-alias-label-tertiary);white-space:nowrap}.mL8Uca_dirSize{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-align:right}.mL8Uca_transferBlock{flex-direction:column;flex:none;gap:6px;display:flex}.mL8Uca_progressMeta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:12px;font-size:12px;display:flex}.mL8Uca_progressTrack{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;height:8px;overflow:hidden}.mL8Uca_progressBar{background:var(--dsw-alias-state-business-primary);border-radius:999px;height:100%;transition:width .12s linear}.mL8Uca_tunnelList{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_tunnelRow{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;align-items:center;gap:10px;padding:8px 12px;display:flex}.mL8Uca_tunnelRow[data-state=forwarding]{border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_tunnelRow[data-state=connecting]{border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_tunnelRow[data-state=failed]{border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_tunnelLabel{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;overflow:hidden}.mL8Uca_formCard{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;gap:10px;padding:12px;display:flex}.mL8Uca_clusterForm{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_clusterFilters{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;display:grid}.mL8Uca_commandInput{min-height:96px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellDetails summary{cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px}.mL8Uca_cellPre{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:pre-wrap;word-break:break-all;border-radius:6px;max-height:180px;margin:4px 0 0;padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;overflow:auto}";
@@ -14786,6 +14807,8 @@ window.__ModuleLoader__.load({
14786
14807
  const [hosts, setHosts] = (0, react.useState)([]);
14787
14808
  const [alias, setAlias] = (0, react.useState)(presetAlias ?? "");
14788
14809
  const [status, setStatus] = (0, react.useState)({ kind: "idle" });
14810
+ const [authPrompt, setAuthPrompt] = (0, react.useState)(void 0);
14811
+ const [authInputs, setAuthInputs] = (0, react.useState)([]);
14789
14812
  const containerRef = (0, react.useRef)(null);
14790
14813
  const termRef = (0, react.useRef)(null);
14791
14814
  const fitRef = (0, react.useRef)(null);
@@ -14826,12 +14849,15 @@ window.__ModuleLoader__.load({
14826
14849
  if (presetAlias !== void 0) setAlias(presetAlias);
14827
14850
  }, [presetAlias, requestId]);
14828
14851
  const teardown = () => {
14852
+ setAuthPrompt(void 0);
14853
+ setAuthInputs([]);
14829
14854
  const connection = connRef.current;
14830
14855
  connRef.current = null;
14831
14856
  if (connection !== null) {
14832
14857
  connection.onReady = void 0;
14833
14858
  connection.onOutput = void 0;
14834
14859
  connection.onExit = void 0;
14860
+ connection.onAuthPrompt = void 0;
14835
14861
  connection.close();
14836
14862
  }
14837
14863
  dataSubRef.current?.dispose();
@@ -14902,7 +14928,17 @@ window.__ModuleLoader__.load({
14902
14928
  dataSubRef.current = term.onData((data) => {
14903
14929
  connection.send(data);
14904
14930
  });
14931
+ connection.onAuthPrompt = (name, instructions, prompts) => {
14932
+ setAuthPrompt({
14933
+ name,
14934
+ instructions,
14935
+ prompts
14936
+ });
14937
+ setAuthInputs(prompts.map(() => ""));
14938
+ };
14905
14939
  connection.onReady = () => {
14940
+ setAuthPrompt(void 0);
14941
+ setAuthInputs([]);
14906
14942
  setStatus({
14907
14943
  kind: "connected",
14908
14944
  alias: target
@@ -14914,6 +14950,8 @@ window.__ModuleLoader__.load({
14914
14950
  connection.onExit = (_code, error) => {
14915
14951
  if (settled) return;
14916
14952
  settled = true;
14953
+ setAuthPrompt(void 0);
14954
+ setAuthInputs([]);
14917
14955
  dataSubRef.current?.dispose();
14918
14956
  dataSubRef.current = null;
14919
14957
  term.options.disableStdin = true;
@@ -14929,6 +14967,12 @@ window.__ModuleLoader__.load({
14929
14967
  teardown();
14930
14968
  setStatus({ kind: "idle" });
14931
14969
  };
14970
+ const submitAuth = (e) => {
14971
+ e?.preventDefault();
14972
+ if (connRef.current === null || authPrompt === void 0) return;
14973
+ connRef.current.sendAuthResponse(authInputs);
14974
+ setAuthPrompt(void 0);
14975
+ };
14932
14976
  const active = status.kind === "connecting" || status.kind === "connected";
14933
14977
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14934
14978
  className: panel_module_css_default.termBody,
@@ -14993,14 +15037,73 @@ window.__ModuleLoader__.load({
14993
15037
  }),
14994
15038
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14995
15039
  className: panel_module_css_default.termWrap,
14996
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14997
- ref: containerRef,
14998
- className: panel_module_css_default.termContainer,
14999
- "data-dsh-part": "terminal"
15000
- }), status.kind === "idle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15001
- className: panel_module_css_default.termPlaceholder,
15002
- children: hosts.length === 0 ? tt("hosts.empty") : tt("terminal.placeholder")
15003
- })]
15040
+ children: [
15041
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15042
+ ref: containerRef,
15043
+ className: panel_module_css_default.termContainer,
15044
+ "data-dsh-part": "terminal"
15045
+ }),
15046
+ status.kind === "idle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15047
+ className: panel_module_css_default.termPlaceholder,
15048
+ children: hosts.length === 0 ? tt("hosts.empty") : tt("terminal.placeholder")
15049
+ }),
15050
+ authPrompt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15051
+ className: panel_module_css_default.modalBackdrop,
15052
+ style: {
15053
+ position: "absolute",
15054
+ zIndex: 10
15055
+ },
15056
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
15057
+ className: panel_module_css_default.modalCard,
15058
+ style: { maxWidth: 420 },
15059
+ onSubmit: submitAuth,
15060
+ children: [
15061
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15062
+ className: panel_module_css_default.modalHeader,
15063
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
15064
+ className: panel_module_css_default.modalTitle,
15065
+ children: authPrompt.name || tt("terminal.auth.title")
15066
+ })
15067
+ }),
15068
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
15069
+ className: panel_module_css_default.hint,
15070
+ children: authPrompt.instructions || tt("terminal.auth.hint")
15071
+ }),
15072
+ authPrompt.prompts.map((p, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
15073
+ className: panel_module_css_default.field,
15074
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15075
+ className: panel_module_css_default.fieldLabel,
15076
+ children: p.prompt.trim() || tt("terminal.auth.title")
15077
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
15078
+ autoFocus: idx === 0,
15079
+ className: panel_module_css_default.input,
15080
+ type: p.echo ? "text" : "password",
15081
+ placeholder: tt("terminal.auth.placeholder"),
15082
+ value: authInputs[idx] ?? "",
15083
+ onChange: (event) => {
15084
+ const next = [...authInputs];
15085
+ next[idx] = event.target.value;
15086
+ setAuthInputs(next);
15087
+ }
15088
+ })]
15089
+ }, idx)),
15090
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15091
+ className: panel_module_css_default.modalFooter,
15092
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15093
+ type: "button",
15094
+ className: panel_module_css_default.ghostButton,
15095
+ onClick: disconnect,
15096
+ children: tt("terminal.auth.cancel")
15097
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15098
+ type: "submit",
15099
+ className: panel_module_css_default.primaryButton,
15100
+ children: tt("terminal.auth.submit")
15101
+ })]
15102
+ })
15103
+ ]
15104
+ })
15105
+ })
15106
+ ]
15004
15107
  })
15005
15108
  ]
15006
15109
  });
@@ -15982,7 +16085,7 @@ window.__ModuleLoader__.load({
15982
16085
  /** The building package's version, when the bundle carries it. */
15983
16086
  function bakedVersion() {
15984
16087
  try {
15985
- return "0.3.5";
16088
+ return "0.3.9";
15986
16089
  } catch {
15987
16090
  return;
15988
16091
  }