docker-client-mx 1.35.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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/bin/docker-client-mx.js +42 -0
  4. package/build/icons/icon-1024.png +0 -0
  5. package/build/icons/icon-128.png +0 -0
  6. package/build/icons/icon-16.png +0 -0
  7. package/build/icons/icon-256.png +0 -0
  8. package/build/icons/icon-32.png +0 -0
  9. package/build/icons/icon-48.png +0 -0
  10. package/build/icons/icon-512.png +0 -0
  11. package/build/icons/icon-64.png +0 -0
  12. package/build/icons/icon.ico +0 -0
  13. package/build/icons/icon.iconset/icon_128x128.png +0 -0
  14. package/build/icons/icon.iconset/icon_128x128@2x.png +0 -0
  15. package/build/icons/icon.iconset/icon_16x16.png +0 -0
  16. package/build/icons/icon.iconset/icon_16x16@2x.png +0 -0
  17. package/build/icons/icon.iconset/icon_256x256.png +0 -0
  18. package/build/icons/icon.iconset/icon_256x256@2x.png +0 -0
  19. package/build/icons/icon.iconset/icon_32x32.png +0 -0
  20. package/build/icons/icon.iconset/icon_32x32@2x.png +0 -0
  21. package/build/icons/icon.iconset/icon_512x512.png +0 -0
  22. package/build/icons/icon.iconset/icon_512x512@2x.png +0 -0
  23. package/build/icons/icon.png +0 -0
  24. package/dist/docker-desktop-mx/browser/chunk-64WWLMUP.js +39 -0
  25. package/dist/docker-desktop-mx/browser/chunk-N4VBBC4W.js +1 -0
  26. package/dist/docker-desktop-mx/browser/favicon.ico +0 -0
  27. package/dist/docker-desktop-mx/browser/index.html +14 -0
  28. package/dist/docker-desktop-mx/browser/main-CTIRTSOI.js +5 -0
  29. package/dist/docker-desktop-mx/browser/styles-JS3HDDI4.css +1 -0
  30. package/dist-electron/api.js +10 -0
  31. package/dist-electron/api.js.map +1 -0
  32. package/dist-electron/assistant-key.js +70 -0
  33. package/dist-electron/assistant-key.js.map +1 -0
  34. package/dist-electron/assistant-models.js +19 -0
  35. package/dist-electron/assistant-models.js.map +1 -0
  36. package/dist-electron/assistant-tools.js +335 -0
  37. package/dist-electron/assistant-tools.js.map +1 -0
  38. package/dist-electron/assistant.js +302 -0
  39. package/dist-electron/assistant.js.map +1 -0
  40. package/dist-electron/attention.js +75 -0
  41. package/dist-electron/attention.js.map +1 -0
  42. package/dist-electron/channels.js +113 -0
  43. package/dist-electron/channels.js.map +1 -0
  44. package/dist-electron/compose-files.js +104 -0
  45. package/dist-electron/compose-files.js.map +1 -0
  46. package/dist-electron/compose.js +271 -0
  47. package/dist-electron/compose.js.map +1 -0
  48. package/dist-electron/container-events.js +188 -0
  49. package/dist-electron/container-events.js.map +1 -0
  50. package/dist-electron/containers.js +165 -0
  51. package/dist-electron/containers.js.map +1 -0
  52. package/dist-electron/context-menu.js +56 -0
  53. package/dist-electron/context-menu.js.map +1 -0
  54. package/dist-electron/contexts.js +346 -0
  55. package/dist-electron/contexts.js.map +1 -0
  56. package/dist-electron/crash.js +129 -0
  57. package/dist-electron/crash.js.map +1 -0
  58. package/dist-electron/diagnosis.js +338 -0
  59. package/dist-electron/diagnosis.js.map +1 -0
  60. package/dist-electron/docker-client.js +284 -0
  61. package/dist-electron/docker-client.js.map +1 -0
  62. package/dist-electron/engine.js +145 -0
  63. package/dist-electron/engine.js.map +1 -0
  64. package/dist-electron/event-store.js +146 -0
  65. package/dist-electron/event-store.js.map +1 -0
  66. package/dist-electron/events.js +132 -0
  67. package/dist-electron/events.js.map +1 -0
  68. package/dist-electron/exec.js +138 -0
  69. package/dist-electron/exec.js.map +1 -0
  70. package/dist-electron/files.js +407 -0
  71. package/dist-electron/files.js.map +1 -0
  72. package/dist-electron/health.js +92 -0
  73. package/dist-electron/health.js.map +1 -0
  74. package/dist-electron/host-overview.js +50 -0
  75. package/dist-electron/host-overview.js.map +1 -0
  76. package/dist-electron/host-shell.js +196 -0
  77. package/dist-electron/host-shell.js.map +1 -0
  78. package/dist-electron/i18n.js +58 -0
  79. package/dist-electron/i18n.js.map +1 -0
  80. package/dist-electron/image-history.js +87 -0
  81. package/dist-electron/image-history.js.map +1 -0
  82. package/dist-electron/images.js +141 -0
  83. package/dist-electron/images.js.map +1 -0
  84. package/dist-electron/ipc.js +783 -0
  85. package/dist-electron/ipc.js.map +1 -0
  86. package/dist-electron/links.js +46 -0
  87. package/dist-electron/links.js.map +1 -0
  88. package/dist-electron/logs.js +153 -0
  89. package/dist-electron/logs.js.map +1 -0
  90. package/dist-electron/main.js +494 -0
  91. package/dist-electron/main.js.map +1 -0
  92. package/dist-electron/merged-logs.js +127 -0
  93. package/dist-electron/merged-logs.js.map +1 -0
  94. package/dist-electron/navigation.js +37 -0
  95. package/dist-electron/navigation.js.map +1 -0
  96. package/dist-electron/ndjson.js +30 -0
  97. package/dist-electron/ndjson.js.map +1 -0
  98. package/dist-electron/networks.js +507 -0
  99. package/dist-electron/networks.js.map +1 -0
  100. package/dist-electron/port-conflicts.js +106 -0
  101. package/dist-electron/port-conflicts.js.map +1 -0
  102. package/dist-electron/preload.js +257 -0
  103. package/dist-electron/preload.js.map +1 -0
  104. package/dist-electron/pull.js +220 -0
  105. package/dist-electron/pull.js.map +1 -0
  106. package/dist-electron/redact.js +129 -0
  107. package/dist-electron/redact.js.map +1 -0
  108. package/dist-electron/registry-auth.js +150 -0
  109. package/dist-electron/registry-auth.js.map +1 -0
  110. package/dist-electron/renew.js +162 -0
  111. package/dist-electron/renew.js.map +1 -0
  112. package/dist-electron/restart-policy.js +58 -0
  113. package/dist-electron/restart-policy.js.map +1 -0
  114. package/dist-electron/run-command.js +146 -0
  115. package/dist-electron/run-command.js.map +1 -0
  116. package/dist-electron/run.js +151 -0
  117. package/dist-electron/run.js.map +1 -0
  118. package/dist-electron/self-update.js +163 -0
  119. package/dist-electron/self-update.js.map +1 -0
  120. package/dist-electron/servers.js +108 -0
  121. package/dist-electron/servers.js.map +1 -0
  122. package/dist-electron/settings.js +146 -0
  123. package/dist-electron/settings.js.map +1 -0
  124. package/dist-electron/ssh.js +574 -0
  125. package/dist-electron/ssh.js.map +1 -0
  126. package/dist-electron/stats.js +209 -0
  127. package/dist-electron/stats.js.map +1 -0
  128. package/dist-electron/streams.js +63 -0
  129. package/dist-electron/streams.js.map +1 -0
  130. package/dist-electron/system.js +123 -0
  131. package/dist-electron/system.js.map +1 -0
  132. package/dist-electron/terminal-transcript.js +55 -0
  133. package/dist-electron/terminal-transcript.js.map +1 -0
  134. package/dist-electron/updates.js +136 -0
  135. package/dist-electron/updates.js.map +1 -0
  136. package/dist-electron/volume-backup.js +203 -0
  137. package/dist-electron/volume-backup.js.map +1 -0
  138. package/dist-electron/volume-files.js +124 -0
  139. package/dist-electron/volume-files.js.map +1 -0
  140. package/dist-electron/volumes.js +253 -0
  141. package/dist-electron/volumes.js.map +1 -0
  142. package/dist-electron/wiring-watch.js +93 -0
  143. package/dist-electron/wiring-watch.js.map +1 -0
  144. package/dist-electron/wiring.js +71 -0
  145. package/dist-electron/wiring.js.map +1 -0
  146. package/dist-electron/wsl.js +167 -0
  147. package/dist-electron/wsl.js.map +1 -0
  148. package/package.json +125 -0
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WiringWatch = exports.CHECK_DELAY_MS = void 0;
4
+ /**
5
+ * Een melding als een container zijn netwerk of zijn poorten kwijt is.
6
+ *
7
+ * De kaart op het dashboard (fase 40) zie je alleen als je kijkt, en traefik hing dagen aan geen
8
+ * enkel netwerk zonder dat iemand het merkte. Voor een crash en unhealthy is er al een melding;
9
+ * dit is het gat daartussen.
10
+ *
11
+ * Alleen wat er nieuw bij komt. Een container die al een uur zonder netwerk draait, meldt hij niet
12
+ * elke minuut opnieuw; is het opgelost en gebeurt het weer, dan wel. Een crashlus niet: de
13
+ * crashmelding meldt elke crash al.
14
+ */
15
+ /** Na een event zo lang wachten: een `compose up` stuurt er tientallen, en dan is één keer nakijken genoeg. */
16
+ exports.CHECK_DELAY_MS = 3000;
17
+ function isWiring(item) {
18
+ return item.kind === 'network' || item.kind === 'ports';
19
+ }
20
+ /** Per naam en soort, niet per id: na opnieuw opbouwen is het dezelfde container met een nieuw id. */
21
+ function keyOf(item) {
22
+ return `${item.kind}:${item.name}`;
23
+ }
24
+ function defaultSchedule(callback, delayMs) {
25
+ const timer = setTimeout(callback, delayMs);
26
+ timer.unref();
27
+ return { cancel: () => clearTimeout(timer) };
28
+ }
29
+ class WiringWatch {
30
+ deps;
31
+ /** Wat er de vorige keer mis was; wat hier al in staat, is al gemeld. */
32
+ known = new Set();
33
+ pending = null;
34
+ running = false;
35
+ again = false;
36
+ stopped = false;
37
+ constructor(deps) {
38
+ this.deps = deps;
39
+ }
40
+ /** Of een event ertoe doet: containers en netwerken, want daar gaat het om. */
41
+ static relevant(raw) {
42
+ const type = raw?.Type;
43
+ return type === 'container' || type === 'network';
44
+ }
45
+ /** Er gebeurde iets: over een paar seconden nakijken, samen met wat er daarna nog komt. */
46
+ poke() {
47
+ if (this.stopped || this.pending !== null) {
48
+ return;
49
+ }
50
+ this.pending = (this.deps.schedule ?? defaultSchedule)(() => {
51
+ this.pending = null;
52
+ void this.run();
53
+ }, exports.CHECK_DELAY_MS);
54
+ }
55
+ stop() {
56
+ this.stopped = true;
57
+ this.pending?.cancel();
58
+ this.pending = null;
59
+ }
60
+ /** Eén keer nakijken. Loopt er al een, dan daarna nog eens, zodat niets tussen wal en schip valt. */
61
+ async run() {
62
+ if (this.running) {
63
+ this.again = true;
64
+ return;
65
+ }
66
+ this.running = true;
67
+ try {
68
+ const items = (await this.deps.check()).filter(isWiring);
69
+ if (this.stopped) {
70
+ return;
71
+ }
72
+ const now = new Set(items.map(keyOf));
73
+ for (const item of items) {
74
+ if (!this.known.has(keyOf(item))) {
75
+ this.deps.notify(item);
76
+ }
77
+ }
78
+ this.known = now;
79
+ }
80
+ catch {
81
+ // Niet na te kijken (de daemon is even weg): de volgende keer weer.
82
+ }
83
+ finally {
84
+ this.running = false;
85
+ if (this.again) {
86
+ this.again = false;
87
+ void this.run();
88
+ }
89
+ }
90
+ }
91
+ }
92
+ exports.WiringWatch = WiringWatch;
93
+ //# sourceMappingURL=wiring-watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiring-watch.js","sourceRoot":"","sources":["../electron/wiring-watch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.missingNetworkOf = missingNetworkOf;
4
+ exports.missingPortsOf = missingPortsOf;
5
+ exports.toContainerWiring = toContainerWiring;
6
+ exports.readContainerWiring = readContainerWiring;
7
+ const docker_client_1 = require("./docker-client");
8
+ /**
9
+ * Een container die draait, maar niet aangesloten is op wat hij zelf zegt te gebruiken.
10
+ *
11
+ * Twee manieren waarop dat gebeurt, allebei gezien bij traefik:
12
+ *
13
+ * - Het netwerk werd weggehaald en opnieuw gemaakt terwijl de container bestond. `NetworkMode`
14
+ * noemt het nog, `NetworkSettings.Networks` is leeg. Hij draait, en niets bereikt hem.
15
+ * - Na een mislukte start op een bezette poort laat Docker 29 een tweede start slagen zonder de
16
+ * poort te publiceren. `HostConfig.PortBindings` noemt hem, `NetworkSettings.Ports` niet.
17
+ *
18
+ * Docker zelf ziet in geen van beide een fout, dus zonder deze vergelijking zie je het pas als
19
+ * iets niet werkt en je gaat zoeken.
20
+ */
21
+ /** Wat `NetworkMode` kan zijn zonder dat het een netwerk is om aan te hangen. */
22
+ const NOT_A_NETWORK = new Set(['', 'host', 'none']);
23
+ /**
24
+ * Het netwerk uit `NetworkMode` waar een draaiende container niet aan hangt, of `null`.
25
+ * `default` is `bridge`; `container:<id>` deelt de stack van een ander en hangt nergens zelf aan.
26
+ */
27
+ function missingNetworkOf(mode, attached, running) {
28
+ const wanted = mode === 'default' ? 'bridge' : (mode ?? '');
29
+ if (!running || NOT_A_NETWORK.has(wanted) || wanted.startsWith('container:')) {
30
+ return null;
31
+ }
32
+ return attached.includes(wanted) ? null : wanted;
33
+ }
34
+ /** De ingestelde poorten die bij een draaiende container niet gepubliceerd zijn. */
35
+ function missingPortsOf(bindings, published, running) {
36
+ if (!running) {
37
+ return [];
38
+ }
39
+ const gaps = [];
40
+ for (const [key, wanted] of Object.entries(bindings ?? {})) {
41
+ if ((wanted ?? []).length === 0 || (published?.[key] ?? []).length > 0) {
42
+ continue;
43
+ }
44
+ const [port, protocol = 'tcp'] = key.split('/');
45
+ for (const binding of wanted ?? []) {
46
+ // Een lege HostPort betekent: Docker kiest er een. Welke, weten we dan niet.
47
+ const hostPort = Number(binding.HostPort);
48
+ gaps.push({
49
+ containerPort: Number(port),
50
+ protocol,
51
+ hostIp: binding.HostIp === undefined || binding.HostIp === '' ? null : binding.HostIp,
52
+ hostPort: binding.HostPort === undefined || binding.HostPort === '' || !Number.isFinite(hostPort) ? null : hostPort,
53
+ });
54
+ }
55
+ }
56
+ return gaps.sort((a, b) => (a.hostPort ?? a.containerPort) - (b.hostPort ?? b.containerPort));
57
+ }
58
+ /** Zet inspect om. Los van Docker, zodat het met gewone objecten te testen is. */
59
+ function toContainerWiring(raw) {
60
+ const running = raw.State?.Running === true;
61
+ return {
62
+ missingNetwork: missingNetworkOf(raw.HostConfig?.NetworkMode, Object.keys(raw.NetworkSettings?.Networks ?? {}), running),
63
+ missingPorts: missingPortsOf(raw.HostConfig?.PortBindings, raw.NetworkSettings?.Ports, running),
64
+ };
65
+ }
66
+ /** Of deze container aangesloten is op wat hij zegt te gebruiken. */
67
+ async function readContainerWiring(id) {
68
+ const raw = (await (0, docker_client_1.getConnection)().docker.getContainer(id).inspect());
69
+ return toContainerWiring(raw);
70
+ }
71
+ //# sourceMappingURL=wiring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiring.js","sourceRoot":"","sources":["../electron/wiring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decodeWslOutput = decodeWslOutput;
4
+ exports.parseDefaultDistro = parseDefaultDistro;
5
+ exports.parseDistroNames = parseDistroNames;
6
+ exports.readDefaultDistro = readDefaultDistro;
7
+ exports.findStoppedDefaultDistro = findStoppedDefaultDistro;
8
+ exports.isLocalEndpoint = isLocalEndpoint;
9
+ exports.wslOffer = wslOffer;
10
+ exports.startDistro = startDistro;
11
+ exports.startDaemonInDistro = startDaemonInDistro;
12
+ exports.daemonCommand = daemonCommand;
13
+ const node_child_process_1 = require("node:child_process");
14
+ const node_util_1 = require("node:util");
15
+ const i18n_1 = require("./i18n");
16
+ /**
17
+ * Docker in WSL, zonder Docker Desktop. De daemon draait in een distro en is vanaf Windows
18
+ * bereikbaar via een context op `tcp://localhost:2375`. Staat die distro stil, dan luistert
19
+ * er niets en zegt de app alleen dat er geen daemon is; hier zit wat nodig is om de
20
+ * oplossing aan te bieden in plaats van alleen het probleem te melden.
21
+ *
22
+ * Alles via `execFile` en nooit via een shell: de distronaam gaat als los argument mee.
23
+ */
24
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
25
+ /** Een byte-order mark; als code en niet letterlijk, want onzichtbaar in de bron. */
26
+ const BOM = String.fromCharCode(0xfeff);
27
+ const LIST_TIMEOUT_MS = 10_000;
28
+ /** Een koude start van de WSL-VM kan tientallen seconden duren. */
29
+ const START_TIMEOUT_MS = 60_000;
30
+ /**
31
+ * `wsl.exe` schrijft UTF-16LE, ook als je hem via een pipe leest. Met `WSL_UTF8=1` wordt dat
32
+ * UTF-8, maar oudere versies kennen die variabele niet. Een nulbyte verraadt UTF-16: in
33
+ * gewone UTF-8-tekst komt die niet voor.
34
+ */
35
+ function decodeWslOutput(raw) {
36
+ const text = raw.includes(0) ? raw.toString('utf16le') : raw.toString('utf8');
37
+ return text.startsWith(BOM) ? text.slice(1) : text;
38
+ }
39
+ /**
40
+ * De standaard-distro uit `wsl -l -v`: de regel met een `*` ervoor. We lezen alleen die ster
41
+ * en de naam erachter, niet de kolom STATE: die is vertaald op een Windows in een andere taal.
42
+ */
43
+ function parseDefaultDistro(listVerbose) {
44
+ for (const line of listVerbose.split(/\r?\n/)) {
45
+ const match = /^\s*\*\s+(\S+)/.exec(line);
46
+ if (match !== null) {
47
+ return match[1];
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+ /** De namen uit `wsl -l -q`, één per regel. */
53
+ function parseDistroNames(listQuiet) {
54
+ return listQuiet
55
+ .split(/\r?\n/)
56
+ .map((line) => line.trim())
57
+ .filter((line) => line !== '');
58
+ }
59
+ /** Docker Desktop's eigen distro's. Die starten helpt niet: daar draait zonder Desktop niets. */
60
+ function isDockerDesktopDistro(name) {
61
+ return name.startsWith('docker-desktop');
62
+ }
63
+ const runWsl = async (args, timeoutMs) => {
64
+ const { stdout } = await execFileAsync('wsl.exe', [...args], {
65
+ encoding: 'buffer',
66
+ env: { ...process.env, WSL_UTF8: '1' },
67
+ timeout: timeoutMs,
68
+ windowsHide: true,
69
+ });
70
+ return decodeWslOutput(stdout);
71
+ };
72
+ /**
73
+ * De standaard-distro, met de mededeling of hij draait.
74
+ *
75
+ * Alleen de standaard: de context zegt niet in welke distro de daemon zit, en om dat uit te
76
+ * zoeken zou je elke gestopte distro moeten opstarten. Wie Docker in WSL draait, heeft hem
77
+ * vrijwel altijd in de standaard-distro.
78
+ */
79
+ async function readDefaultDistro(run = runWsl) {
80
+ let distro;
81
+ try {
82
+ distro = parseDefaultDistro(await run(['-l', '-v'], LIST_TIMEOUT_MS));
83
+ }
84
+ catch {
85
+ // Geen WSL, of een WSL zonder distro's: dan valt er niets aan te bieden.
86
+ return null;
87
+ }
88
+ if (distro === null || isDockerDesktopDistro(distro)) {
89
+ return null;
90
+ }
91
+ let running;
92
+ try {
93
+ running = parseDistroNames(await run(['-l', '--running', '-q'], LIST_TIMEOUT_MS));
94
+ }
95
+ catch {
96
+ // Zonder draaiende distro's eindigt `--running` bij sommige versies met een foutcode.
97
+ running = [];
98
+ }
99
+ return { name: distro, running: running.includes(distro) };
100
+ }
101
+ /** De standaard-distro als die stilstaat, anders `null`. */
102
+ async function findStoppedDefaultDistro(run = runWsl) {
103
+ const found = await readDefaultDistro(run);
104
+ return found !== null && !found.running ? found.name : null;
105
+ }
106
+ /** Of dit adres de daemon op deze machine is. Een daemon op een ander adres staat niet in WSL. */
107
+ function isLocalEndpoint(endpoint) {
108
+ const host = endpoint.replace(/:\d+$/, '').replace(/^\[|\]$/g, '');
109
+ return host === 'localhost' || host === '127.0.0.1' || host === '::1';
110
+ }
111
+ /**
112
+ * Welk aanbod hier past, of `null` als er niets te bieden valt.
113
+ *
114
+ * Alleen op Windows, alleen als er niets luistert (`ECONNREFUSED`, niet een weigering of een
115
+ * time-out) en alleen op een lokaal TCP-adres: bij een named pipe is het Docker Desktop.
116
+ */
117
+ async function wslOffer(err, endpoint, platform = process.platform, read = readDefaultDistro) {
118
+ const code = err?.code;
119
+ if (platform !== 'win32' || code !== 'ECONNREFUSED' || !isLocalEndpoint(endpoint)) {
120
+ return null;
121
+ }
122
+ const found = await read();
123
+ return found === null ? null : { distro: found.name, kind: found.running ? 'daemon' : 'distro' };
124
+ }
125
+ /**
126
+ * Start een distro. `--exec true` en geen shell: de distro komt op, systemd start docker, en
127
+ * wij hoeven er zelf niets in te draaien.
128
+ *
129
+ * De naam moet voorkomen in `wsl -l`; de renderer levert hem aan en mag `wsl.exe` niet iets
130
+ * laten starten wat wij niet zelf hebben aangeboden.
131
+ */
132
+ async function startDistro(distro, run = runWsl) {
133
+ await assertKnownDistro(distro, run);
134
+ await run(['-d', distro, '--exec', 'true'], START_TIMEOUT_MS);
135
+ }
136
+ /**
137
+ * De daemon starten in een distro die al draait.
138
+ *
139
+ * `-u root` omdat `systemctl start` root vraagt; WSL geeft die zonder wachtwoord, want de
140
+ * distro is van de gebruiker zelf. Dit is de enige plek waar de app iets ín de distro draait,
141
+ * en het is te verdedigen omdat er zonder deze knop niets te bedienen valt: de app kan de
142
+ * daemon niet bereiken en heeft dus ook niets om te tonen.
143
+ *
144
+ * Geen shell, alles als losse argumenten, en de naam moet in `wsl -l` staan.
145
+ */
146
+ async function startDaemonInDistro(distro, run = runWsl) {
147
+ await assertKnownDistro(distro, run);
148
+ await run(['-d', distro, '-u', 'root', '--exec', 'systemctl', 'start', 'docker'], START_TIMEOUT_MS);
149
+ }
150
+ /** Hetzelfde commando als tekst, voor als het niet lukt en je het zelf wil doen. */
151
+ function daemonCommand(distro) {
152
+ return `wsl -d ${distro} -u root --exec systemctl start docker`;
153
+ }
154
+ /** De renderer levert de naam aan en mag `wsl.exe` niets laten starten wat wij niet aanboden. */
155
+ async function assertKnownDistro(distro, run) {
156
+ const known = parseDistroNames(await run(['-l', '-q'], LIST_TIMEOUT_MS));
157
+ if (!known.includes(distro)) {
158
+ throw new Error((0, i18n_1.t)({
159
+ en: `WSL has no distro '${distro}'.`,
160
+ nl: `WSL kent geen distro '${distro}'.`,
161
+ de: `WSL hat keine Distro '${distro}'.`,
162
+ fr: `WSL n’a pas de distro '${distro}'.`,
163
+ es: `WSL no tiene ninguna distro '${distro}'.`,
164
+ }));
165
+ }
166
+ }
167
+ //# sourceMappingURL=wsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsl.js","sourceRoot":"","sources":["../electron/wsl.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,125 @@
1
+ {
2
+ "name": "docker-client-mx",
3
+ "version": "1.35.0",
4
+ "description": "A desktop client for Docker in WSL, without Docker Desktop: containers, images, volumes, networks, compose and logs.",
5
+ "keywords": [
6
+ "docker",
7
+ "wsl",
8
+ "electron",
9
+ "desktop",
10
+ "containers",
11
+ "compose"
12
+ ],
13
+ "author": "Martijn Schimmel",
14
+ "license": "MIT",
15
+ "homepage": "https://codeberg.org/MartijnSchimmel/docker-client-mx",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://codeberg.org/MartijnSchimmel/docker-client-mx.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://codeberg.org/MartijnSchimmel/docker-client-mx/issues"
22
+ },
23
+ "private": false,
24
+ "main": "dist-electron/main.js",
25
+ "bin": {
26
+ "docker-client-mx": "bin/docker-client-mx.js",
27
+ "docker-desktop-mx": "bin/docker-client-mx.js"
28
+ },
29
+ "files": [
30
+ "bin/",
31
+ "dist/docker-desktop-mx/browser/",
32
+ "dist-electron/",
33
+ "build/icons/"
34
+ ],
35
+ "type": "commonjs",
36
+ "engines": {
37
+ "node": ">=20"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "packageManager": "npm@11.13.0",
43
+ "scripts": {
44
+ "ng": "ng",
45
+ "start": "npm run electron:build && electron .",
46
+ "dev": "concurrently -k -n angular,electron -c blue,magenta \"ng serve\" \"npm run electron:serve\"",
47
+ "build": "ng build",
48
+ "lint": "eslint .",
49
+ "lint:fix": "eslint . --fix",
50
+ "test": "ng test",
51
+ "test:electron": "vitest run --config electron/vitest.config.mts",
52
+ "smoke": "npm run electron:build && electron scripts/smoke.js",
53
+ "release": "semantic-release",
54
+ "release:installer": "node scripts/publish-installer.js",
55
+ "electron:compile": "tsc -p electron/tsconfig.json && npm run electron:preload",
56
+ "electron:preload": "esbuild electron/preload.ts --bundle --platform=node --target=node20 --format=cjs --external:electron --outfile=dist-electron/preload.js",
57
+ "electron:serve": "wait-on http-get://localhost:4200 && npm run electron:compile && cross-env ELECTRON_RENDERER_URL=http://localhost:4200 electron .",
58
+ "electron:build": "npm run build && npm run electron:compile",
59
+ "electron:package": "npm run electron:build && electron-builder",
60
+ "icons": "electron scripts/build-icons.js",
61
+ "package:win": "npm run icons && npm run electron:build && electron-builder --win",
62
+ "package:linux": "npm run icons && npm run electron:build && electron-builder --linux",
63
+ "package:mac": "npm run icons && npm run electron:build && electron-builder --mac",
64
+ "prepare": "husky",
65
+ "prepack": "npm run electron:build"
66
+ },
67
+ "dependencies": {
68
+ "@anthropic-ai/sdk": "^0.127.0",
69
+ "dockerode": "^5.0.1",
70
+ "electron-updater": "^6.8.9",
71
+ "ssh2": "^1.17.0",
72
+ "tar-stream": "^3.2.1",
73
+ "zod": "^4.6.5"
74
+ },
75
+ "peerDependencies": {
76
+ "electron": "^44.4.1"
77
+ },
78
+ "devDependencies": {
79
+ "@angular/build": "^22.1.8",
80
+ "@angular/cli": "^22.1.8",
81
+ "@angular/common": "^22.1.0",
82
+ "@angular/compiler": "^22.1.0",
83
+ "@angular/compiler-cli": "^22.1.0",
84
+ "@angular/core": "^22.1.0",
85
+ "@angular/forms": "^22.1.0",
86
+ "@angular/platform-browser": "^22.1.0",
87
+ "@angular/router": "^22.1.0",
88
+ "@commitlint/cli": "^21.2.2",
89
+ "@commitlint/config-conventional": "^21.2.2",
90
+ "@eslint/js": "^10.0.1",
91
+ "@floating-ui/dom": "^1.8.0",
92
+ "@radix-ng/primitives": "^1.2.0",
93
+ "@ribbon-studios/semantic-release-forgejo": "^0.1.3",
94
+ "@semantic-release/changelog": "^7.0.0",
95
+ "@semantic-release/commit-analyzer": "^13.0.1",
96
+ "@semantic-release/git": "^11.0.1",
97
+ "@semantic-release/npm": "^13.1.5",
98
+ "@semantic-release/release-notes-generator": "^14.1.1",
99
+ "@tailwindcss/postcss": "^4.3.3",
100
+ "@types/dockerode": "^4.0.1",
101
+ "@types/node": "^24.13.5",
102
+ "@types/ssh2": "^1.15.6",
103
+ "@types/tar-stream": "^3.1.4",
104
+ "@xterm/addon-fit": "^0.11.0",
105
+ "@xterm/xterm": "^6.0.0",
106
+ "angular-eslint": "^22.5.0",
107
+ "concurrently": "^10.0.5",
108
+ "cross-env": "^10.1.0",
109
+ "electron": "^44.4.1",
110
+ "electron-builder": "^26.15.3",
111
+ "esbuild": "^0.28.2",
112
+ "eslint": "^10.10.0",
113
+ "eslint-plugin-jsdoc": "^64.5.2",
114
+ "husky": "^9.1.7",
115
+ "jsdom": "^28.0.0",
116
+ "rxjs": "~7.8.0",
117
+ "semantic-release": "^25.0.9",
118
+ "tailwindcss": "^4.3.3",
119
+ "tslib": "^2.3.0",
120
+ "typescript": "~6.0.2",
121
+ "typescript-eslint": "^8.70.0",
122
+ "vitest": "^4.1.11",
123
+ "wait-on": "^9.1.0"
124
+ }
125
+ }