docker-client-mx 1.43.0 → 1.45.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.
Files changed (93) hide show
  1. package/dist/docker-desktop-mx/browser/{chunk-BpQVP_gf.js → chunk-Af1e1d8G.js} +1 -1
  2. package/dist/docker-desktop-mx/browser/{chunk-D5njuWRz.js → chunk-B13W1gEu.js} +1 -1
  3. package/dist/docker-desktop-mx/browser/chunk-B2NHI2Ew.js +1 -0
  4. package/dist/docker-desktop-mx/browser/{chunk-k-zsJmvX.js → chunk-B3n-2UOi.js} +1 -1
  5. package/dist/docker-desktop-mx/browser/chunk-BIIdmzaV2.js +1 -0
  6. package/dist/docker-desktop-mx/browser/chunk-BJACElSf2.js +1 -0
  7. package/dist/docker-desktop-mx/browser/{chunk-BsTDUJ3m.js → chunk-BPNQPsl2.js} +1 -1
  8. package/dist/docker-desktop-mx/browser/chunk-BUtOgCv_.js +1 -0
  9. package/dist/docker-desktop-mx/browser/chunk-BZwYBA7Z.js +1 -0
  10. package/dist/docker-desktop-mx/browser/chunk-Bcx5WMyJ.js +1 -0
  11. package/dist/docker-desktop-mx/browser/{chunk-DLpC_2HL.js → chunk-Bd5-MqqR.js} +16 -16
  12. package/dist/docker-desktop-mx/browser/{chunk-xmDwFpQl.js → chunk-BxKOYZan.js} +1 -1
  13. package/dist/docker-desktop-mx/browser/{chunk-C7c_wFw-.js → chunk-BxU6JdzU.js} +1 -1
  14. package/dist/docker-desktop-mx/browser/{chunk-DS3iAefP.js → chunk-C3u9Hm0s.js} +1 -1
  15. package/dist/docker-desktop-mx/browser/chunk-CGft6wQH2.js +6 -0
  16. package/dist/docker-desktop-mx/browser/chunk-CHa6bNlc.js +6 -0
  17. package/dist/docker-desktop-mx/browser/{chunk-CpNGhf9S.js → chunk-COBWzwhz.js} +1 -1
  18. package/dist/docker-desktop-mx/browser/{chunk-75uKIulV.js → chunk-COdMLD3H.js} +1 -1
  19. package/dist/docker-desktop-mx/browser/chunk-CXpIB_qc.js +1 -0
  20. package/dist/docker-desktop-mx/browser/{chunk-Bxte2YBm.js → chunk-ClGJowQ7.js} +1 -1
  21. package/dist/docker-desktop-mx/browser/{chunk-gHmeiJFn.js → chunk-Clfbc1-d.js} +1 -1
  22. package/dist/docker-desktop-mx/browser/{chunk-yYm_2q6C.js → chunk-CoprhnOm.js} +1 -1
  23. package/dist/docker-desktop-mx/browser/chunk-CsQ_euXP.js +1 -0
  24. package/dist/docker-desktop-mx/browser/{chunk-D2SM5E9h.js → chunk-D3bHzook.js} +3 -3
  25. package/dist/docker-desktop-mx/browser/chunk-DEXem6rk.js +1 -0
  26. package/dist/docker-desktop-mx/browser/chunk-DPn8Rpg4.js +1 -0
  27. package/dist/docker-desktop-mx/browser/chunk-DVXm8E_S.js +1 -0
  28. package/dist/docker-desktop-mx/browser/chunk-DX-Zo-hw.js +1 -0
  29. package/dist/docker-desktop-mx/browser/chunk-DZ-f89Uu.js +1 -0
  30. package/dist/docker-desktop-mx/browser/chunk-DZXhuKXm.js +1 -0
  31. package/dist/docker-desktop-mx/browser/{chunk-CjAbzLl3.js → chunk-DctinxSO.js} +1 -1
  32. package/dist/docker-desktop-mx/browser/chunk-DeXGJ-vD2.js +1 -0
  33. package/dist/docker-desktop-mx/browser/{chunk-D86K3m6W.js → chunk-Dy_g3RHY.js} +1 -1
  34. package/dist/docker-desktop-mx/browser/chunk-EFAsN7J4.js +1 -0
  35. package/dist/docker-desktop-mx/browser/chunk-FqKVK_Os2.js +1 -0
  36. package/dist/docker-desktop-mx/browser/{chunk-VC5gaeEq.js → chunk-IMRsZUv82.js} +1 -1
  37. package/dist/docker-desktop-mx/browser/chunk-U7tL4XEw2.js +1 -0
  38. package/dist/docker-desktop-mx/browser/chunk-YQkNtrRq.js +1 -0
  39. package/dist/docker-desktop-mx/browser/chunk-_wm6hEEA.js +1 -0
  40. package/dist/docker-desktop-mx/browser/{chunk-qxDOm16k.js → chunk-eVHlLrxe.js} +2 -2
  41. package/dist/docker-desktop-mx/browser/{chunk-B1xjyPnV.js → chunk-jpKpwesj.js} +1 -1
  42. package/dist/docker-desktop-mx/browser/{chunk-vPeKxmUB.js → chunk-m112xmv9.js} +1 -1
  43. package/dist/docker-desktop-mx/browser/chunk-u0OAgv4f.js +4 -0
  44. package/dist/docker-desktop-mx/browser/index.html +2 -2
  45. package/dist/docker-desktop-mx/browser/main-INZSNGJM.js +1 -0
  46. package/dist/docker-desktop-mx/browser/styles-BGYQU5CU.css +1 -0
  47. package/dist-electron/channels.js +8 -0
  48. package/dist-electron/channels.js.map +1 -1
  49. package/dist-electron/container-adjust.js +182 -0
  50. package/dist-electron/container-adjust.js.map +1 -0
  51. package/dist-electron/exec.js +78 -7
  52. package/dist-electron/exec.js.map +1 -1
  53. package/dist-electron/file-edit.js +206 -0
  54. package/dist-electron/file-edit.js.map +1 -0
  55. package/dist-electron/ipc.js +49 -12
  56. package/dist-electron/ipc.js.map +1 -1
  57. package/dist-electron/log-export.js +5 -4
  58. package/dist-electron/log-export.js.map +1 -1
  59. package/dist-electron/logs.js +26 -10
  60. package/dist-electron/logs.js.map +1 -1
  61. package/dist-electron/preload.js +20 -4
  62. package/dist-electron/preload.js.map +1 -1
  63. package/dist-electron/system.js +51 -1
  64. package/dist-electron/system.js.map +1 -1
  65. package/dist-electron/volume-file-edit.js +78 -0
  66. package/dist-electron/volume-file-edit.js.map +1 -0
  67. package/dist-electron/volume-files.js +8 -6
  68. package/dist-electron/volume-files.js.map +1 -1
  69. package/package.json +1 -1
  70. package/dist/docker-desktop-mx/browser/chunk-B-8njY4y.js +0 -1
  71. package/dist/docker-desktop-mx/browser/chunk-BE5ctn7s.js +0 -1
  72. package/dist/docker-desktop-mx/browser/chunk-BNz89YmT2.js +0 -1
  73. package/dist/docker-desktop-mx/browser/chunk-BYBsnx9-2.js +0 -1
  74. package/dist/docker-desktop-mx/browser/chunk-CUSsr_nJ.js +0 -1
  75. package/dist/docker-desktop-mx/browser/chunk-CaR7Z77S2.js +0 -1
  76. package/dist/docker-desktop-mx/browser/chunk-Cau4mGeC.js +0 -1
  77. package/dist/docker-desktop-mx/browser/chunk-CjySsWe4.js +0 -1
  78. package/dist/docker-desktop-mx/browser/chunk-CpIMiI_l.js +0 -4
  79. package/dist/docker-desktop-mx/browser/chunk-CphQt55R.js +0 -1
  80. package/dist/docker-desktop-mx/browser/chunk-Cpz4zP5G.js +0 -1
  81. package/dist/docker-desktop-mx/browser/chunk-Cx-uRg2m.js +0 -1
  82. package/dist/docker-desktop-mx/browser/chunk-DCYTwazU.js +0 -1
  83. package/dist/docker-desktop-mx/browser/chunk-DGnxa3ti2.js +0 -1
  84. package/dist/docker-desktop-mx/browser/chunk-DNfiG-TG.js +0 -1
  85. package/dist/docker-desktop-mx/browser/chunk-DdTfHpyK.js +0 -1
  86. package/dist/docker-desktop-mx/browser/chunk-DnY7-enP.js +0 -1
  87. package/dist/docker-desktop-mx/browser/chunk-Q3Ig-PJ6.js +0 -1
  88. package/dist/docker-desktop-mx/browser/chunk-hJqozYgS.js +0 -1
  89. package/dist/docker-desktop-mx/browser/chunk-hXmaVpBu2.js +0 -1
  90. package/dist/docker-desktop-mx/browser/chunk-iWbUoQd52.js +0 -6
  91. package/dist/docker-desktop-mx/browser/chunk-sxrgZH0E.js +0 -3
  92. package/dist/docker-desktop-mx/browser/main-UUO74KHR.js +0 -1
  93. package/dist/docker-desktop-mx/browser/styles-IM2TO7ZD.css +0 -1
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EXEC_USER = exports.STOP_SCRIPT = void 0;
4
+ exports.terminalMark = terminalMark;
3
5
  exports.wrapSession = wrapSession;
4
6
  exports.openExec = openExec;
7
+ const node_crypto_1 = require("node:crypto");
5
8
  const docker_client_1 = require("./docker-client");
6
9
  const i18n_1 = require("./i18n");
7
10
  /**
@@ -12,6 +15,31 @@ const i18n_1 = require("./i18n");
12
15
  * vragen het eerst. Dat kost één korte exec, en het antwoord is eenduidig.
13
16
  */
14
17
  const PROBE = 'for c in /bin/bash /usr/bin/bash /bin/sh /usr/bin/sh /bin/ash; do [ -x "$c" ] && echo "$c" && exit 0; done; exit 1';
18
+ /**
19
+ * Ruimt de processen van één terminal op (fase 79): alles met dit kenmerk in de omgeving, dus de
20
+ * shell en wat erin gestart is. Eerst SIGHUP, zoals een terminal die ophangt; wat na een seconde nog
21
+ * leeft, SIGKILL. `$1` is het kenmerk; het script zelf heeft het niet in zijn omgeving.
22
+ */
23
+ exports.STOP_SCRIPT = [
24
+ 'm="MX_TERMINAL=$1"',
25
+ 'hit() { for p in /proc/[0-9]*; do tr \'\\000\' \'\\n\' < "$p/environ" 2>/dev/null | grep -qx "$m" && echo "${p#/proc/}"; done; }',
26
+ 'pids=$(hit); [ -z "$pids" ] && exit 0',
27
+ 'kill -HUP $pids 2>/dev/null; sleep 1',
28
+ 'pids=$(hit); [ -n "$pids" ] && kill -KILL $pids 2>/dev/null; exit 0',
29
+ ].join('\n');
30
+ /** Het kenmerk dat een terminal meekrijgt: zo vindt het opruimen precies zijn eigen processen terug. */
31
+ function terminalMark() {
32
+ return (0, node_crypto_1.randomUUID)();
33
+ }
34
+ /**
35
+ * Wat Docker als gebruiker van een exec aanneemt: een naam of een uid, eventueel met `:groep`.
36
+ * Strenger dan nodig, zodat er nooit iets anders in de API-aanroep terechtkomt.
37
+ */
38
+ exports.EXEC_USER = /^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,31}(:[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,31})?$/;
39
+ /** Alleen meegeven als er een gebruiker gekozen is; zonder `User` neemt Docker die van de image. */
40
+ function asUser(user) {
41
+ return user === null ? {} : { User: user };
42
+ }
15
43
  /** Leest de uitvoer van een korte exec uit; de shell-probe schrijft één regel. */
16
44
  function readAll(stream) {
17
45
  return new Promise((resolve) => {
@@ -22,10 +50,12 @@ function readAll(stream) {
22
50
  });
23
51
  }
24
52
  const dockerBackend = {
25
- detectShell: async (containerId) => {
53
+ detectShell: async (containerId, user) => {
26
54
  const { docker } = (0, docker_client_1.getConnection)();
27
55
  try {
56
+ // Als dezelfde gebruiker als de shell straks: een naam die er niet is, valt dan hier al op.
28
57
  const probe = await docker.getContainer(containerId).exec({
58
+ ...asUser(user),
29
59
  Cmd: ['/bin/sh', '-c', PROBE],
30
60
  AttachStdout: true,
31
61
  AttachStderr: false,
@@ -40,15 +70,18 @@ const dockerBackend = {
40
70
  return null;
41
71
  }
42
72
  },
43
- start: async (containerId, shell, size) => {
73
+ start: async (containerId, shell, size, user) => {
44
74
  const { docker } = (0, docker_client_1.getConnection)();
75
+ const mark = terminalMark();
45
76
  const exec = await docker.getContainer(containerId).exec({
77
+ ...asUser(user),
46
78
  Cmd: [shell],
47
79
  AttachStdin: true,
48
80
  AttachStdout: true,
49
81
  AttachStderr: true,
50
82
  Tty: true,
51
- Env: ['TERM=xterm-256color'],
83
+ // Het kenmerk erven ook de programma's die je in de shell start; zie STOP_SCRIPT.
84
+ Env: ['TERM=xterm-256color', `MX_TERMINAL=${mark}`],
52
85
  });
53
86
  // Drie dingen moeten hier staan, en het derde is een valkuil:
54
87
  // - hijack maakt van de HTTP-verbinding een tweerichtingskanaal in plaats van een antwoord;
@@ -65,15 +98,35 @@ const dockerBackend = {
65
98
  // Een resize op een exec die net gestopt is geeft een 404; dat is geen fout om te melden.
66
99
  void exec.resize({ h: rows, w: cols }).catch(() => undefined);
67
100
  },
101
+ // Met een TTY hangt Docker de terminal niet op als de verbinding sluit: de shell bleef dan
102
+ // draaien, ook als root (fase 79). Dus zelf opruimen, als dezelfde gebruiker.
103
+ stop: () => void stopTerminal(containerId, mark, user),
68
104
  };
69
105
  },
70
106
  };
107
+ /** Draait STOP_SCRIPT in de container. Lukt het niet (container al weg), dan is er ook niets meer op te ruimen. */
108
+ async function stopTerminal(containerId, mark, user) {
109
+ try {
110
+ const exec = await (0, docker_client_1.getConnection)().docker.getContainer(containerId).exec({
111
+ ...asUser(user),
112
+ Cmd: ['/bin/sh', '-c', exports.STOP_SCRIPT, 'mx', mark],
113
+ AttachStdout: true,
114
+ AttachStderr: true,
115
+ });
116
+ const stream = await exec.start({ hijack: true, stdin: false });
117
+ await readAll(stream);
118
+ }
119
+ catch {
120
+ // Niets aan te doen, en niets om te melden: de terminal is al dicht.
121
+ }
122
+ }
71
123
  /**
72
124
  * Maakt van een ruwe TTY-stream een sessie. `stop` ruimt op wat er naast de stream nog draait,
73
125
  * zoals de hulpcontainer van de host-shell.
74
126
  */
75
127
  function wrapSession(shell, stream, resize, onData, onEnd, stop = () => undefined) {
76
128
  let closed = false;
129
+ let destroyed = false;
77
130
  const finish = () => {
78
131
  if (!closed) {
79
132
  closed = true;
@@ -93,6 +146,11 @@ function wrapSession(shell, stream, resize, onData, onEnd, stop = () => undefine
93
146
  },
94
147
  resize,
95
148
  destroy: () => {
149
+ // Sluiten kan van twee kanten komen (de tab en het venster); opruimen hoeft maar één keer.
150
+ if (destroyed) {
151
+ return;
152
+ }
153
+ destroyed = true;
96
154
  closed = true;
97
155
  stream.destroy();
98
156
  stop();
@@ -106,8 +164,21 @@ function wrapSession(shell, stream, resize, onData, onEnd, stop = () => undefine
106
164
  * inclusief de escape-codes, want díé zijn hier juist de bedoeling. Dat is het verschil met de
107
165
  * logweergave, waar we ze er juist uit halen.
108
166
  */
109
- async function openExec(containerId, size, onData, onEnd, backend = dockerBackend) {
110
- const shell = await backend.detectShell(containerId);
167
+ async function openExec(containerId, size, onData, onEnd, backend = dockerBackend, user = null) {
168
+ const shell = await backend.detectShell(containerId, user);
169
+ // Met een gekozen gebruiker is "geen shell" meestal "geen gebruiker met die naam".
170
+ if (shell === null && user !== null) {
171
+ return {
172
+ ok: false,
173
+ problem: (0, i18n_1.t)({
174
+ en: `No shell could be started as ${user}. Does that user exist in this container?`,
175
+ nl: `Als ${user} kon geen shell gestart worden. Bestaat die gebruiker in deze container?`,
176
+ de: `Als ${user} konnte keine Shell gestartet werden. Gibt es diesen Benutzer in diesem Container?`,
177
+ fr: `Aucun shell n’a pu être démarré en tant que ${user}. Cet utilisateur existe-t-il dans ce conteneur ?`,
178
+ es: `No se ha podido iniciar un shell como ${user}. ¿Existe ese usuario en este contenedor?`,
179
+ }),
180
+ };
181
+ }
111
182
  if (shell === null) {
112
183
  return {
113
184
  ok: false,
@@ -121,8 +192,8 @@ async function openExec(containerId, size, onData, onEnd, backend = dockerBacken
121
192
  };
122
193
  }
123
194
  try {
124
- const { stream, resize } = await backend.start(containerId, shell, size);
125
- return { ok: true, session: wrapSession(shell, stream, resize, onData, onEnd) };
195
+ const { stream, resize, stop } = await backend.start(containerId, shell, size, user);
196
+ return { ok: true, session: wrapSession(shell, stream, resize, onData, onEnd, stop) };
126
197
  }
127
198
  catch (err) {
128
199
  const reason = err instanceof Error ? err.message : String(err);
@@ -1 +1 @@
1
- {"version":3,"file":"exec.js","sourceRoot":"","sources":["../electron/exec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../electron/exec.ts"],"names":[],"mappings":";;;AAsDA,oCAGC;AA8GD,kCA4CC;AAaD,4BAqDC;AArRD,6CAAyC;AAEzC,mDAAgD;AAEhD,iCAA2B;AAE3B;;;;;;GAMG;AACH,MAAM,KAAK,GAAG,oHAAoH,CAAC;AA2BnI;;;;GAIG;AACU,QAAA,WAAW,GAAG;IACzB,oBAAoB;IACpB,kIAAkI;IAClI,uCAAuC;IACvC,sCAAsC;IACtC,qEAAqE;CACtE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,wGAAwG;AACxG,SAAgB,YAAY;IAE1B,OAAO,IAAA,wBAAU,GAAE,CAAC;AACtB,CAAC;AAED;;;GAGG;AACU,QAAA,SAAS,GAAG,wEAAwE,CAAC;AAElG,oGAAoG;AACpG,SAAS,MAAM,CAAC,IAAmB;IAEjC,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,kFAAkF;AAClF,SAAS,OAAO,CAAC,MAAc;IAE7B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,IAAI,GAAG,EAAE,CAAC;QAEd,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,aAAa,GAAgB;IACjC,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;QACvC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,6BAAa,GAAE,CAAC;QAEnC,IAAI,CAAC;YACH,4FAA4F;YAC5F,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC;gBACxD,GAAG,MAAM,CAAC,IAAI,CAAC;gBACf,GAAG,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC;gBAC7B,YAAY,EAAE,IAAI;gBAClB,YAAY,EAAE,KAAK;aACpB,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACjE,sFAAsF;YACtF,+EAA+E;YAC/E,MAAM,KAAK,GAAG,6BAA6B,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAExE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QAC9C,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,6BAAa,GAAE,CAAC;QACnC,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC;YACvD,GAAG,MAAM,CAAC,IAAI,CAAC;YACf,GAAG,EAAE,CAAC,KAAK,CAAC;YACZ,WAAW,EAAE,IAAI;YACjB,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,IAAI;YAClB,GAAG,EAAE,IAAI;YACT,kFAAkF;YAClF,GAAG,EAAE,CAAC,qBAAqB,EAAE,eAAe,IAAI,EAAE,CAAC;SACpD,CAAC,CAAC;QAEH,8DAA8D;QAC9D,4FAA4F;QAC5F,yEAAyE;QACzE,4FAA4F;QAC5F,0FAA0F;QAC1F,mFAAmF;QACnF,0EAA0E;QAC1E,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAA6B,CAAC;QAExG,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAElD,OAAO;YACL,MAAM;YACN,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;gBACrB,0FAA0F;gBAC1F,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAChE,CAAC;YACD,2FAA2F;YAC3F,8EAA8E;YAC9E,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,YAAY,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC;SACvD,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,mHAAmH;AACnH,KAAK,UAAU,YAAY,CAAC,WAAmB,EAAE,IAAY,EAAE,IAAmB;IAEhF,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,IAAA,6BAAa,GAAE,CAAC,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC;YACvE,GAAG,MAAM,CAAC,IAAI,CAAC;YACf,GAAG,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,mBAAW,EAAE,IAAI,EAAE,IAAI,CAAC;YAC/C,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAEhE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;IACvE,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CACzB,KAAa,EACb,MAAqB,EACrB,MAA4C,EAC5C,MAA+B,EAC/B,KAAiB,EACjB,OAAmB,GAAG,EAAE,CAAC,SAAS;IAGlC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,MAAM,MAAM,GAAG,GAAS,EAAE;QACxB,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,IAAI,CAAC;YACd,KAAK,EAAE,CAAC;QACV,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE3B,OAAO;QACL,KAAK;QACL,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;YACd,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QACD,MAAM;QACN,OAAO,EAAE,GAAG,EAAE;YACZ,2FAA2F;YAC3F,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO;YACT,CAAC;YAED,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM,GAAG,IAAI,CAAC;YACd,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,EAAE,CAAC;QACT,CAAC;KACF,CAAC;AACJ,CAAC;AAMD;;;;;;GAMG;AACI,KAAK,UAAU,QAAQ,CAC5B,WAAmB,EACnB,IAAoC,EACpC,MAA+B,EAC/B,KAAiB,EACjB,UAAuB,aAAa,EACpC,OAAsB,IAAI;IAG1B,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAE3D,mFAAmF;IACnF,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QACpC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,IAAA,QAAC,EAAC;gBACT,EAAE,EAAE,gCAAgC,IAAI,2CAA2C;gBACnF,EAAE,EAAE,OAAO,IAAI,0EAA0E;gBACzF,EAAE,EAAE,OAAO,IAAI,oFAAoF;gBACnG,EAAE,EAAE,+CAA+C,IAAI,mDAAmD;gBAC1G,EAAE,EAAE,yCAAyC,IAAI,2CAA2C;aAC7F,CAAC;SACH,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,IAAA,QAAC,EAAC;gBACT,EAAE,EAAE,0FAA0F;gBAC9F,EAAE,EAAE,2FAA2F;gBAC/F,EAAE,EAAE,4FAA4F;gBAChG,EAAE,EAAE,mGAAmG;gBACvG,EAAE,EAAE,qGAAqG;aAC1G,CAAC;SACH,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAErF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;IACxF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAEhE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAA,QAAC,EAAC;gBAC7B,EAAE,EAAE,mCAAmC,MAAM,EAAE;gBAC/C,EAAE,EAAE,qCAAqC,MAAM,EAAE;gBACjD,EAAE,EAAE,4CAA4C,MAAM,EAAE;gBACxD,EAAE,EAAE,sCAAsC,MAAM,EAAE;gBAClD,EAAE,EAAE,qCAAqC,MAAM,EAAE;aAClD,CAAC,EAAE,CAAC;IACP,CAAC;AACH,CAAC"}
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.MAX_EDIT_BYTES = void 0;
37
+ exports.asText = asText;
38
+ exports.versionOf = versionOf;
39
+ exports.onMount = onMount;
40
+ exports.readEditableFile = readEditableFile;
41
+ exports.saveEditableFile = saveEditableFile;
42
+ const node_crypto_1 = require("node:crypto");
43
+ const path = __importStar(require("node:path"));
44
+ const tar = __importStar(require("tar-stream"));
45
+ const docker_client_1 = require("./docker-client");
46
+ const files_1 = require("./files");
47
+ const i18n_1 = require("./i18n");
48
+ /**
49
+ * Een tekstbestand in een container bewerken (fase 78): lezen, en terugzetten met dezelfde rechten
50
+ * en eigenaar. Via `getArchive` en `putArchive`, zonder shell, zoals downloaden en uploaden.
51
+ *
52
+ * Alleen tekst en alleen klein: een editor in de app is voor een configuratieregel, niet voor een
53
+ * logbestand van honderd megabyte. De versie is een hash van de inhoud; opslaan leest eerst opnieuw
54
+ * en weigert als het bestand intussen veranderd is, in plaats van het stil te overschrijven.
55
+ */
56
+ /** Groter dan dit is geen bestand om in de app te bewerken; dan blijven downloaden en uploaden. */
57
+ exports.MAX_EDIT_BYTES = 512 * 1024;
58
+ const MAX_LINK_HOPS = 8;
59
+ /** De eerste entry van een archief; de inhoud alleen als het een bestand is dat klein genoeg is. */
60
+ async function firstEntry(archive) {
61
+ const extract = tar.extract();
62
+ let found = null;
63
+ archive.pipe(extract);
64
+ for await (const entry of extract) {
65
+ const header = entry.header;
66
+ if (found === null) {
67
+ const small = header.type === 'file' && (header.size ?? 0) <= exports.MAX_EDIT_BYTES;
68
+ const chunks = [];
69
+ if (small) {
70
+ for await (const chunk of entry) {
71
+ chunks.push(chunk);
72
+ }
73
+ }
74
+ else {
75
+ entry.resume();
76
+ }
77
+ found = {
78
+ type: header.type ?? 'file',
79
+ size: header.size ?? 0,
80
+ mode: header.mode ?? 0o644,
81
+ uid: header.uid ?? 0,
82
+ gid: header.gid ?? 0,
83
+ linkname: header.linkname ?? null,
84
+ content: small ? Buffer.concat(chunks) : null,
85
+ };
86
+ }
87
+ else {
88
+ entry.resume();
89
+ }
90
+ }
91
+ return found;
92
+ }
93
+ /** Of dit tekst is die we heel terugschrijven: geen nul-bytes, en UTF-8 dat heen en terug gelijk blijft. */
94
+ function asText(content) {
95
+ if (content.includes(0)) {
96
+ return null;
97
+ }
98
+ const text = content.toString('utf8');
99
+ return Buffer.from(text, 'utf8').equals(content) ? text : null;
100
+ }
101
+ /** De versie van een bestand: een hash van de inhoud, om bij opslaan te zien of het veranderde. */
102
+ function versionOf(content) {
103
+ return (0, node_crypto_1.createHash)('sha256').update(content).digest('hex');
104
+ }
105
+ /** Of een pad onder een volume of bind mount valt; dan blijft een wijziging na "Opnieuw" staan. */
106
+ function onMount(file, destinations) {
107
+ return destinations.some((destination) => file === destination || file.startsWith(`${destination.replace(/\/$/, '')}/`));
108
+ }
109
+ /** Leest het bestand, en volgt een symlink zoals de container dat zou doen. */
110
+ async function read(id, requested) {
111
+ const container = (0, docker_client_1.getConnection)().docker.getContainer(id);
112
+ let current = (0, files_1.normalizeContainerPath)(requested);
113
+ for (let hop = 0; hop <= MAX_LINK_HOPS; hop += 1) {
114
+ const entry = await firstEntry((await container.getArchive({ path: current })));
115
+ if (entry?.type === 'symlink' && entry.linkname !== null) {
116
+ current = path.posix.resolve(path.posix.dirname(current), entry.linkname);
117
+ continue;
118
+ }
119
+ if (entry === null || entry.type !== 'file') {
120
+ return { ok: false, problem: (0, i18n_1.t)({
121
+ en: `${current} is not a file.`,
122
+ nl: `${current} is geen bestand.`,
123
+ de: `${current} ist keine Datei.`,
124
+ fr: `${current} n’est pas un fichier.`,
125
+ es: `${current} no es un archivo.`,
126
+ }) };
127
+ }
128
+ return { ok: true, path: current, entry };
129
+ }
130
+ return { ok: false, problem: (0, i18n_1.t)({
131
+ en: `too many symlinks in a row from ${requested}.`,
132
+ nl: `te veel symlinks achter elkaar vanaf ${requested}.`,
133
+ de: `zu viele Symlinks hintereinander ab ${requested}.`,
134
+ fr: `trop de liens symboliques à la suite depuis ${requested}.`,
135
+ es: `demasiados symlinks seguidos desde ${requested}.`,
136
+ }) };
137
+ }
138
+ /** Leest een tekstbestand om te bewerken, met de versie om bij opslaan tegen te controleren. */
139
+ async function readEditableFile(id, requested) {
140
+ try {
141
+ const result = await read(id, requested);
142
+ if (!result.ok) {
143
+ return result;
144
+ }
145
+ const { entry } = result;
146
+ if (entry.content === null) {
147
+ return { ok: false, problem: (0, i18n_1.t)({
148
+ en: `${result.path} is larger than ${String(exports.MAX_EDIT_BYTES / 1024)} kB; download it to edit it.`,
149
+ nl: `${result.path} is groter dan ${String(exports.MAX_EDIT_BYTES / 1024)} kB; download het om het te bewerken.`,
150
+ de: `${result.path} ist größer als ${String(exports.MAX_EDIT_BYTES / 1024)} kB; laden Sie es zum Bearbeiten herunter.`,
151
+ fr: `${result.path} dépasse ${String(exports.MAX_EDIT_BYTES / 1024)} ko ; téléchargez-le pour le modifier.`,
152
+ es: `${result.path} supera los ${String(exports.MAX_EDIT_BYTES / 1024)} kB; descárgalo para editarlo.`,
153
+ }) };
154
+ }
155
+ const text = asText(entry.content);
156
+ if (text === null) {
157
+ return { ok: false, problem: (0, i18n_1.t)({
158
+ en: `${result.path} is not text (or not UTF-8); download it to edit it.`,
159
+ nl: `${result.path} is geen tekst (of geen UTF-8); download het om het te bewerken.`,
160
+ de: `${result.path} ist kein Text (oder kein UTF-8); laden Sie es zum Bearbeiten herunter.`,
161
+ fr: `${result.path} n’est pas du texte (ou pas en UTF-8) ; téléchargez-le pour le modifier.`,
162
+ es: `${result.path} no es texto (o no es UTF-8); descárgalo para editarlo.`,
163
+ }) };
164
+ }
165
+ const inspect = (await (0, docker_client_1.getConnection)().docker.getContainer(id).inspect());
166
+ const destinations = (inspect.Mounts ?? []).map((mount) => mount.Destination ?? '').filter((destination) => destination !== '');
167
+ return { ok: true, path: result.path, text, version: versionOf(entry.content), onMount: onMount(result.path, destinations) };
168
+ }
169
+ catch (err) {
170
+ return { ok: false, problem: (0, docker_client_1.readableError)(err, (0, docker_client_1.getConnection)().endpoint).message };
171
+ }
172
+ }
173
+ /**
174
+ * Zet de tekst terug, met de rechten, eigenaar en groep die het bestand had. Is het intussen
175
+ * veranderd (een andere versie dan `version`), dan niet: `conflict` zegt dat, tenzij `force`.
176
+ */
177
+ async function saveEditableFile(id, file, text, version, force) {
178
+ try {
179
+ const current = await read(id, file);
180
+ if (!current.ok) {
181
+ return { ...current, conflict: false };
182
+ }
183
+ if (!force && (current.entry.content === null || versionOf(current.entry.content) !== version)) {
184
+ return { ok: false, conflict: true, problem: (0, i18n_1.t)({
185
+ en: `${current.path} has changed in the container since you opened it.`,
186
+ nl: `${current.path} is in de container veranderd sinds je het opende.`,
187
+ de: `${current.path} hat sich im Container geändert, seit Sie es geöffnet haben.`,
188
+ fr: `${current.path} a changé dans le conteneur depuis que vous l’avez ouvert.`,
189
+ es: `${current.path} ha cambiado en el contenedor desde que lo abriste.`,
190
+ }) };
191
+ }
192
+ const content = Buffer.from(text, 'utf8');
193
+ const pack = tar.pack();
194
+ pack.entry({ name: path.posix.basename(current.path), size: content.length, mode: current.entry.mode, uid: current.entry.uid, gid: current.entry.gid, mtime: new Date() }, content);
195
+ pack.finalize();
196
+ // `copyUIDGID`: de eigenaar en groep uit het archief, en dat zijn die van het oude bestand.
197
+ await (0, docker_client_1.getConnection)()
198
+ .docker.getContainer(id)
199
+ .putArchive(pack, { path: path.posix.dirname(current.path), copyUIDGID: '1' });
200
+ return { ok: true, version: versionOf(content) };
201
+ }
202
+ catch (err) {
203
+ return { ok: false, conflict: false, problem: (0, docker_client_1.readableError)(err, (0, docker_client_1.getConnection)().endpoint).message };
204
+ }
205
+ }
206
+ //# sourceMappingURL=file-edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-edit.js","sourceRoot":"","sources":["../electron/file-edit.ts"],"names":[],"mappings":"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"}
@@ -65,8 +65,10 @@ const run_command_1 = require("./run-command");
65
65
  const memory_limit_1 = require("./memory-limit");
66
66
  const cpu_limit_1 = require("./cpu-limit");
67
67
  const container_rename_1 = require("./container-rename");
68
+ const file_edit_1 = require("./file-edit");
68
69
  const processes_1 = require("./processes");
69
70
  const container_duplicate_1 = require("./container-duplicate");
71
+ const container_adjust_1 = require("./container-adjust");
70
72
  const container_commit_1 = require("./container-commit");
71
73
  const environment_1 = require("./environment");
72
74
  const log_export_1 = require("./log-export");
@@ -79,6 +81,7 @@ const container_events_1 = require("./container-events");
79
81
  const event_store_1 = require("./event-store");
80
82
  const events_1 = require("./events");
81
83
  const files_1 = require("./files");
84
+ const volume_file_edit_1 = require("./volume-file-edit");
82
85
  const volume_files_1 = require("./volume-files");
83
86
  const self_update_1 = require("./self-update");
84
87
  const settings_1 = require("./settings");
@@ -120,6 +123,14 @@ const restartPolicy = zod_1.z.object({
120
123
  maximumRetryCount: zod_1.z.number().int().min(0).max(100),
121
124
  });
122
125
  const containerIds = zod_1.z.array(containerId).max(1000);
126
+ // Een variabele is NAAM=waarde; de naam begint niet met een cijfer en bevat geen =.
127
+ const adjustOptions = zod_1.z.object({
128
+ env: zod_1.z.array(zod_1.z.string().max(32_768).regex(/^[A-Za-z_][^=]*=/s)).max(1000),
129
+ ports: zod_1.z
130
+ .array(zod_1.z.object({ containerPort: zod_1.z.number().int().min(1).max(65535), protocol: zod_1.z.enum(['tcp', 'udp']), hostPort: zod_1.z.number().int().min(1).max(65535).nullable() }))
131
+ .max(100),
132
+ restart: zod_1.z.enum(['no', 'always', 'unless-stopped', 'on-failure']),
133
+ });
123
134
  const duplicateOptions = zod_1.z.object({
124
135
  name: zod_1.z.string().min(1).max(128),
125
136
  ports: zod_1.z
@@ -133,6 +144,11 @@ const memoryBytes = zod_1.z.number().int().positive().max(1024 ** 4);
133
144
  const cpuCount = zod_1.z.number().positive().max(1024);
134
145
  const streamId = zod_1.z.string().min(1).max(64);
135
146
  const logTail = zod_1.z.number().int().min(0).max(10_000);
147
+ // Een tijdvak in seconden sinds 1970; `until` voor `since` weigert Docker zelf ook, maar dan onduidelijk.
148
+ const logRange = zod_1.z
149
+ .object({ since: zod_1.z.number().int().min(0), until: zod_1.z.number().int().min(0).nullable() })
150
+ .refine((range) => range.until === null || range.until >= range.since)
151
+ .nullable();
136
152
  // Een referentie mag een registryhost, een pad, een tag en een digest bevatten; ruimer dan
137
153
  // dit wordt het niet, en alles wat er niet in past wijst de daemon toch af.
138
154
  const imageReference = zod_1.z.string().min(1).max(256).regex(/^[\w./:@-]+$/);
@@ -165,6 +181,17 @@ const wslDistro = zod_1.z.string().min(1).max(64).regex(/^[\w.-]+$/);
165
181
  // Volumenamen, netwerk-id's en contextnamen komen uit onze eigen lijst; dit is een grens,
166
182
  // geen validatie van wat Docker toelaat.
167
183
  const resourceName = zod_1.z.string().min(1).max(256);
184
+ /** Te bewerken tekst: iets ruimer dan het maximum van het lezen, want bewerken mag het bestand iets groter maken. */
185
+ const editText = zod_1.z.string().max(2 * 1024 * 1024);
186
+ /** De versie van een gelezen bestand (sha256), om bij opslaan tegen te controleren. */
187
+ const editVersion = zod_1.z.string().regex(/^[0-9a-f]{64}$/);
188
+ /** Laat kiezen welke bestanden erin gaan; leeg als er niets gekozen is. */
189
+ async function chooseFilesToUpload(sender) {
190
+ const owner = electron_1.BrowserWindow.fromWebContents(sender);
191
+ const options = { properties: ['openFile', 'multiSelections'] };
192
+ const choice = owner === null ? await electron_1.dialog.showOpenDialog(options) : await electron_1.dialog.showOpenDialog(owner, options);
193
+ return choice.canceled ? [] : choice.filePaths;
194
+ }
168
195
  // Een extra naam in een netwerk. Docker laat hier hetzelfde toe als bij een containernaam.
169
196
  const networkAlias = zod_1.z
170
197
  .string()
@@ -663,13 +690,15 @@ function registerIpcHandlers() {
663
690
  handle(channels_1.CHANNELS.containersRename, (_event, id, name) => (0, container_rename_1.renameContainer)(containerId.parse(id), resourceName.parse(name)));
664
691
  handle(channels_1.CHANNELS.containersDuplicatePlan, (_event, id) => (0, container_duplicate_1.readDuplicatePlan)(containerId.parse(id)));
665
692
  handle(channels_1.CHANNELS.containersDuplicate, (_event, id, options) => (0, container_duplicate_1.duplicateContainer)(containerId.parse(id), duplicateOptions.parse(options)));
693
+ handle(channels_1.CHANNELS.containersAdjustPlan, (_event, id) => (0, container_adjust_1.readAdjustPlan)(containerId.parse(id)));
694
+ handle(channels_1.CHANNELS.containersAdjust, (_event, id, options) => (0, container_adjust_1.adjustContainer)(containerId.parse(id), adjustOptions.parse(options)));
666
695
  handle(channels_1.CHANNELS.containersProcesses, (_event, id) => (0, processes_1.readProcesses)(containerId.parse(id)));
667
696
  handle(channels_1.CHANNELS.containersCommit, (_event, id, reference) => (0, container_commit_1.commitContainer)(containerId.parse(id), imageReference.parse(reference)));
668
697
  handle(channels_1.CHANNELS.containersCpuLimit, (_event, id) => (0, cpu_limit_1.readCpuLimit)(containerId.parse(id)));
669
698
  handle(channels_1.CHANNELS.containersEnvironment, (_event, id) => (0, environment_1.readEnvironment)(containerId.parse(id)));
670
699
  handle(channels_1.CHANNELS.containersSetCpuLimit, (_event, id, cpus) => (0, cpu_limit_1.applyCpuLimit)(containerId.parse(id), cpuCount.parse(cpus)));
671
700
  handle(channels_1.CHANNELS.containersSetMemoryLimit, (_event, id, bytes) => (0, memory_limit_1.applyMemoryLimit)(containerId.parse(id), memoryBytes.parse(bytes)));
672
- handle(channels_1.CHANNELS.containersSaveLogs, (event, id, name) => (0, log_export_1.exportLogs)(containerId.parse(id), resourceName.parse(name), async (suggestedName) => {
701
+ handle(channels_1.CHANNELS.containersSaveLogs, (event, id, name, range) => (0, log_export_1.exportLogs)(containerId.parse(id), resourceName.parse(name), logRange.parse(range ?? null), async (suggestedName) => {
673
702
  const owner = electron_1.BrowserWindow.fromWebContents(event.sender);
674
703
  const options = { defaultPath: suggestedName, filters: [{ name: 'Log', extensions: ['log', 'txt'] }] };
675
704
  const choice = owner === null ? await electron_1.dialog.showSaveDialog(options) : await electron_1.dialog.showSaveDialog(owner, options);
@@ -743,6 +772,14 @@ function registerIpcHandlers() {
743
772
  handle(channels_1.CHANNELS.volumesPrune, (_event, all) => (0, volumes_1.pruneVolumes)(truthy.parse(all)));
744
773
  handle(channels_1.CHANNELS.volumesBrowse, (_event, name, dir) => (0, volume_files_1.listVolumeDirectory)(resourceName.parse(name), containerPath.parse(dir)));
745
774
  handle(channels_1.CHANNELS.volumesCloseBrowser, () => (0, volume_files_1.closeVolumeBrowser)());
775
+ handle(channels_1.CHANNELS.volumesReadText, (_event, name, file) => (0, volume_file_edit_1.readVolumeFile)(resourceName.parse(name), containerPath.parse(file)));
776
+ handle(channels_1.CHANNELS.volumesSaveText, (_event, name, file, text, version, force) => (0, volume_file_edit_1.saveVolumeFile)(resourceName.parse(name), containerPath.parse(file), editText.parse(text), editVersion.parse(version), truthy.parse(force)));
777
+ handle(channels_1.CHANNELS.volumesChooseAndUpload, async (event, name, dir) => {
778
+ const volume = resourceName.parse(name);
779
+ const target = containerPath.parse(dir);
780
+ const choice = await chooseFilesToUpload(event.sender);
781
+ return choice.length === 0 ? { ok: true, count: 0 } : (0, volume_file_edit_1.uploadToVolume)(volume, target, choice);
782
+ });
746
783
  handle(channels_1.CHANNELS.volumesBackup, (event, name) => (0, volume_backup_1.backupVolume)(resourceName.parse(name), async (suggestedName) => {
747
784
  const owner = electron_1.BrowserWindow.fromWebContents(event.sender);
748
785
  const options = { defaultPath: suggestedName, filters: [{ name: (0, i18n_1.t)({ en: 'Backup', nl: 'Back-up', de: 'Sicherung', fr: 'Sauvegarde', es: 'Copia de seguridad' }), extensions: ['tar'] }] };
@@ -802,6 +839,7 @@ function registerIpcHandlers() {
802
839
  handle(channels_1.CHANNELS.composeBuildable, (_event, name) => (0, service_build_1.listBuildable)(resourceName.parse(name)));
803
840
  handle(channels_1.CHANNELS.composeRebuild, (_event, name, service) => (0, service_build_1.rebuildService)(resourceName.parse(name), resourceName.parse(service)));
804
841
  handle(channels_1.CHANNELS.systemDiskUsage, () => (0, system_1.readDiskUsage)());
842
+ handle(channels_1.CHANNELS.systemSpaceUsage, () => (0, system_1.readSpaceUsage)());
805
843
  handle(channels_1.CHANNELS.systemPrune, (_event, selection) => (0, system_1.pruneSystem)(pruneSelection.parse(selection)));
806
844
  handle(channels_1.CHANNELS.navigationTakePending, () => (0, navigation_1.takePendingRoute)());
807
845
  handle(channels_1.CHANNELS.assistantStatus, () => ({ configured: (0, assistant_key_1.hasApiKey)(), storageAvailable: (0, assistant_key_1.keyStoreAvailable)() }));
@@ -839,14 +877,11 @@ function registerIpcHandlers() {
839
877
  return choice.canceled || choice.filePath === undefined ? null : choice.filePath;
840
878
  }));
841
879
  handle(channels_1.CHANNELS.filesUpload, (_event, id, dir, locals) => (0, files_1.uploadFiles)(containerId.parse(id), containerPath.parse(dir), localPaths.parse(locals)));
880
+ handle(channels_1.CHANNELS.filesReadText, (_event, id, file) => (0, file_edit_1.readEditableFile)(containerId.parse(id), containerPath.parse(file)));
881
+ handle(channels_1.CHANNELS.filesSaveText, (_event, id, file, text, version, force) => (0, file_edit_1.saveEditableFile)(containerId.parse(id), containerPath.parse(file), editText.parse(text), editVersion.parse(version), truthy.parse(force)));
842
882
  handle(channels_1.CHANNELS.filesChooseAndUpload, async (event, id, dir) => {
843
- const owner = electron_1.BrowserWindow.fromWebContents(event.sender);
844
- const options = { properties: ['openFile', 'multiSelections'] };
845
- const choice = owner === null ? await electron_1.dialog.showOpenDialog(options) : await electron_1.dialog.showOpenDialog(owner, options);
846
- if (choice.canceled) {
847
- return { ok: true, count: 0 };
848
- }
849
- return (0, files_1.uploadFiles)(containerId.parse(id), containerPath.parse(dir), choice.filePaths);
883
+ const choice = await chooseFilesToUpload(event.sender);
884
+ return choice.length === 0 ? { ok: true, count: 0 } : (0, files_1.uploadFiles)(containerId.parse(id), containerPath.parse(dir), choice);
850
885
  });
851
886
  handle(channels_1.CHANNELS.contextsList, () => (0, contexts_1.listContexts)(activeContext));
852
887
  handle(channels_1.CHANNELS.contextsUse, (_event, name) => useContext(resourceName.parse(name)));
@@ -854,9 +889,11 @@ function registerIpcHandlers() {
854
889
  handle(channels_1.CHANNELS.contextsRemove, (_event, name) => (0, contexts_1.removeContext)(resourceName.parse(name), activeContext));
855
890
  handle(channels_1.CHANNELS.contextsTest, (_event, target) => testTarget(serverTestTarget.parse(target)));
856
891
  handle(channels_1.CHANNELS.contextsTrustHostKey, (_event, name, shown) => hostKeys.trust(hostKeyName.parse(name), hostKeyFingerprint.parse(shown)));
857
- handle(channels_1.CHANNELS.execOpen, (event, id, cols, rows) => {
892
+ handle(channels_1.CHANNELS.execOpen, (event, id, cols, rows, user) => {
858
893
  const target = containerId.parse(id);
859
- return openShell(event.sender.id, cols, rows, (size, onData, onEnd) => (0, exec_1.openExec)(target, size, onData, onEnd));
894
+ // Een gebruiker (fase 77), of `null` voor die van de image.
895
+ const as = zod_1.z.string().regex(exec_1.EXEC_USER).nullable().parse(user ?? null);
896
+ return openShell(event.sender.id, cols, rows, (size, onData, onEnd) => (0, exec_1.openExec)(target, size, onData, onEnd, undefined, as));
860
897
  });
861
898
  handle(channels_1.CHANNELS.execOpenHost, (event, cols, rows) => {
862
899
  const transcript = new terminal_transcript_1.TerminalTranscript();
@@ -876,7 +913,7 @@ function registerIpcHandlers() {
876
913
  handle(channels_1.CHANNELS.execClose, (event, id) => {
877
914
  windows.get(event.sender.id)?.streams.close(streamId.parse(id));
878
915
  });
879
- handle(channels_1.CHANNELS.logsOpen, async (event, id, tail) => {
916
+ handle(channels_1.CHANNELS.logsOpen, async (event, id, tail, range) => {
880
917
  const state = requireState(event.sender.id);
881
918
  // Het id wordt uitgegeven vóór de stream bestaat, zodat een close() tijdens het openen
882
919
  // niet in het luchtledige valt. Zie StreamRegistry.
@@ -884,7 +921,7 @@ function registerIpcHandlers() {
884
921
  const channel = `${channels_1.CHANNELS.logsLines}:${reserved}`;
885
922
  const stream = await (0, logs_1.openLogStream)(containerId.parse(id), logTail.parse(tail), (lines) => {
886
923
  state.streams.send(channel, lines);
887
- });
924
+ }, undefined, logRange.parse(range ?? null));
888
925
  state.streams.attach(reserved, stream);
889
926
  return reserved;
890
927
  });