@exagone313/dsh-podman 0.2.0-rc.3 → 0.2.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 (191) hide show
  1. package/README.md +14 -2
  2. package/README.zh.md +14 -2
  3. package/cordis.patch.yml +58 -0
  4. package/dist/approval-reasons.d.ts +3 -4
  5. package/dist/approval-reasons.js +40 -49
  6. package/dist/approval-reasons.test.js +43 -15
  7. package/dist/approval.js +27 -10
  8. package/dist/approval.test.js +165 -34
  9. package/dist/card-route.d.ts +3 -2
  10. package/dist/card-route.js +154 -42
  11. package/dist/card-route.test.js +85 -43
  12. package/dist/card-test-support.js +39 -66
  13. package/dist/client/ContainerCard.d.ts +1 -1
  14. package/dist/client/ContainerCard.js +92 -50
  15. package/dist/client/card-protocol.d.ts +6 -3
  16. package/dist/client/container-card-caches.js +11 -6
  17. package/dist/client/container-card-controller.d.ts +16 -18
  18. package/dist/client/container-card-controller.js +92 -40
  19. package/dist/client/container-card-create-modal.d.ts +2 -2
  20. package/dist/client/container-card-create-modal.js +13 -5
  21. package/dist/client/container-card-default-env.d.ts +10 -0
  22. package/dist/client/container-card-default-env.js +69 -0
  23. package/dist/client/container-card-directory.d.ts +2 -2
  24. package/dist/client/container-card-directory.js +12 -4
  25. package/dist/client/container-card-editors.d.ts +3 -3
  26. package/dist/client/container-card-editors.js +104 -32
  27. package/dist/client/container-card-images.d.ts +4 -4
  28. package/dist/client/container-card-images.js +19 -6
  29. package/dist/client/container-card-paths.d.ts +3 -3
  30. package/dist/client/container-card-paths.js +18 -11
  31. package/dist/client/container-card-row.d.ts +10 -6
  32. package/dist/client/container-card-row.js +82 -13
  33. package/dist/client/container-card-secrets.d.ts +3 -3
  34. package/dist/client/container-card-secrets.js +8 -6
  35. package/dist/client/container-card-shared.d.ts +4 -17
  36. package/dist/client/container-card-shared.js +9 -25
  37. package/dist/client/container-card-styles.d.ts +2 -8
  38. package/dist/client/container-card-styles.js +14 -52
  39. package/dist/client/container-card-volumes.d.ts +2 -2
  40. package/dist/client/container-card-volumes.js +9 -5
  41. package/dist/client/container-card-workspace.d.ts +8 -6
  42. package/dist/client/container-card-workspace.js +9 -3
  43. package/dist/client/directory-picker.js +2 -1
  44. package/dist/client/index.js +13141 -2036
  45. package/dist/client/locales.d.ts +6 -1
  46. package/dist/client/locales.js +106 -38
  47. package/dist/client/podman-terminal-guide.d.ts +11 -0
  48. package/dist/client/podman-terminal-guide.js +208 -0
  49. package/dist/client/podman-terminal-title.d.ts +7 -0
  50. package/dist/client/podman-terminal-title.js +30 -0
  51. package/dist/client/podman-terminal.d.ts +26 -0
  52. package/dist/client/podman-terminal.js +412 -0
  53. package/dist/client/read-only-approval.d.ts +2 -2
  54. package/dist/client/read-only-approval.js +13 -3
  55. package/dist/client/slot-contract.d.ts +0 -11
  56. package/dist/client/terminal-preference.d.ts +26 -0
  57. package/dist/client/terminal-preference.js +67 -0
  58. package/dist/client/terminal-protocol.d.ts +85 -0
  59. package/dist/client/terminal-protocol.js +14 -0
  60. package/dist/client/terminal-tab.d.ts +60 -0
  61. package/dist/client/terminal-tab.js +35 -0
  62. package/dist/client/terminal-targets.d.ts +33 -0
  63. package/dist/client/terminal-targets.js +65 -0
  64. package/dist/client/terminal-titles.d.ts +23 -0
  65. package/dist/client/terminal-titles.js +52 -0
  66. package/dist/client/terminal-transport.d.ts +63 -0
  67. package/dist/client/terminal-transport.js +207 -0
  68. package/dist/client/tool-views.js +215 -49
  69. package/dist/container-env.d.ts +11 -0
  70. package/dist/container-env.js +96 -0
  71. package/dist/container-env.test.d.ts +1 -0
  72. package/dist/container-env.test.js +93 -0
  73. package/dist/containers.test.js +196 -18
  74. package/dist/daemons.test.js +18 -4
  75. package/dist/dsh-version.d.ts +1 -0
  76. package/dist/dsh-version.js +46 -0
  77. package/dist/env-rows.d.ts +14 -0
  78. package/dist/env-rows.js +52 -0
  79. package/dist/env-rows.test.d.ts +1 -0
  80. package/dist/env-rows.test.js +43 -0
  81. package/dist/fs-provider.d.ts +1 -0
  82. package/dist/fs-provider.js +26 -12
  83. package/dist/fs-provider.test.js +51 -10
  84. package/dist/generated/version.d.ts +2 -2
  85. package/dist/generated/version.js +2 -2
  86. package/dist/grpc/proto/dshctl/v1/control.proto +11 -1
  87. package/dist/grpc/proto/dshguest/v1/guest.proto +5 -0
  88. package/dist/grpc/runtime-client.d.ts +3 -1
  89. package/dist/grpc/runtime-client.js +90 -17
  90. package/dist/guest-rpc.d.ts +15 -19
  91. package/dist/guest-rpc.js +289 -70
  92. package/dist/guest-rpc.test.d.ts +1 -0
  93. package/dist/guest-rpc.test.js +241 -0
  94. package/dist/guest-terminal.d.ts +30 -0
  95. package/dist/guest-terminal.js +196 -0
  96. package/dist/images.test.js +10 -3
  97. package/dist/index.d.ts +11 -16
  98. package/dist/index.js +42 -36
  99. package/dist/locales.test.d.ts +1 -0
  100. package/dist/locales.test.js +34 -0
  101. package/dist/misc.test.js +38 -10
  102. package/dist/mount-enums.js +2 -1
  103. package/dist/mount-input.d.ts +2 -0
  104. package/dist/mount-input.js +38 -22
  105. package/dist/mounts.test.js +81 -11
  106. package/dist/naming.test.d.ts +1 -0
  107. package/dist/naming.test.js +28 -0
  108. package/dist/output-reader.js +36 -4
  109. package/dist/package-deps.test.d.ts +1 -0
  110. package/dist/package-deps.test.js +49 -0
  111. package/dist/paths.test.js +4 -2
  112. package/dist/plugin-meta.test.d.ts +1 -0
  113. package/dist/plugin-meta.test.js +29 -0
  114. package/dist/project-path.js +33 -7
  115. package/dist/project-path.test.js +14 -0
  116. package/dist/prompts.d.ts +0 -4
  117. package/dist/prompts.js +15 -84
  118. package/dist/read-only-shell.js +3 -1
  119. package/dist/read-only-shell.test.js +80 -25
  120. package/dist/runtime-client.test.d.ts +1 -0
  121. package/dist/runtime-client.test.js +53 -0
  122. package/dist/secrets.test.js +21 -6
  123. package/dist/settings-commands.test.js +61 -14
  124. package/dist/settings-create.test.js +32 -10
  125. package/dist/settings-default-env.test.d.ts +1 -0
  126. package/dist/settings-default-env.test.js +175 -0
  127. package/dist/settings-mounts.test.js +46 -7
  128. package/dist/settings-schema.d.ts +6 -11
  129. package/dist/settings-schema.js +4 -8
  130. package/dist/settings-schema.test.d.ts +1 -0
  131. package/dist/settings-schema.test.js +35 -0
  132. package/dist/settings-workspaces.test.js +38 -63
  133. package/dist/spill-store.d.ts +1 -0
  134. package/dist/spill-store.js +17 -4
  135. package/dist/spill-store.test.js +31 -8
  136. package/dist/subprocess.d.ts +4 -0
  137. package/dist/subprocess.js +148 -162
  138. package/dist/subprocess.test.js +118 -8
  139. package/dist/terminal-preference.test.d.ts +1 -0
  140. package/dist/terminal-preference.test.js +62 -0
  141. package/dist/terminal-route.d.ts +10 -0
  142. package/dist/terminal-route.js +270 -0
  143. package/dist/terminal-route.test.d.ts +1 -0
  144. package/dist/terminal-route.test.js +265 -0
  145. package/dist/terminal-sessions.d.ts +69 -0
  146. package/dist/terminal-sessions.js +252 -0
  147. package/dist/terminal-shells.d.ts +22 -0
  148. package/dist/terminal-shells.js +99 -0
  149. package/dist/terminal-tab.test.d.ts +1 -0
  150. package/dist/terminal-tab.test.js +58 -0
  151. package/dist/terminal-targets.test.d.ts +1 -0
  152. package/dist/terminal-targets.test.js +43 -0
  153. package/dist/terminal-titles.test.d.ts +1 -0
  154. package/dist/terminal-titles.test.js +36 -0
  155. package/dist/test-support.d.ts +35 -5
  156. package/dist/test-support.js +91 -29
  157. package/dist/tool-defs.js +19 -4
  158. package/dist/tool-handlers.js +80 -28
  159. package/dist/tool-params.js +19 -5
  160. package/dist/version-compat.d.ts +4 -0
  161. package/dist/version-compat.js +29 -0
  162. package/dist/version-compat.test.d.ts +1 -0
  163. package/dist/version-compat.test.js +27 -0
  164. package/dist/volumes.test.js +1 -1
  165. package/dist/workspace-binding.d.ts +19 -10
  166. package/dist/workspace-binding.js +252 -78
  167. package/dist/workspace-binding.test.js +202 -27
  168. package/docs/architecture.md +71 -20
  169. package/docs/architecture.zh.md +62 -12
  170. package/docs/configuration.md +61 -20
  171. package/docs/configuration.zh.md +53 -19
  172. package/docs/development-prompt.md +72 -0
  173. package/docs/development-prompt.zh.md +69 -0
  174. package/docs/development.md +184 -29
  175. package/docs/development.zh.md +165 -26
  176. package/docs/install-dsh-and-dsh-podman.md +24 -21
  177. package/docs/install-dsh-and-dsh-podman.zh.md +20 -21
  178. package/docs/uninstall.md +83 -0
  179. package/docs/uninstall.zh.md +79 -0
  180. package/docs/update.md +120 -0
  181. package/docs/update.zh.md +114 -0
  182. package/docs/usage.md +108 -61
  183. package/docs/usage.zh.md +76 -38
  184. package/locale/en.json +6 -0
  185. package/locale/zh.json +6 -0
  186. package/package.json +58 -25
  187. package/quadlet/dsh-podman-orchestrator.container +46 -0
  188. package/quadlet/dsh.container +35 -0
  189. package/LICENSE.pkg +0 -17103
  190. package/dist/preferences.d.ts +0 -6
  191. package/dist/preferences.js +0 -27
@@ -3,46 +3,194 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
3
3
  //
4
4
  // SPDX-License-Identifier: MIT
5
5
  import { useState } from "react";
6
- import { DisclosureRow, IconApiOutline14, IconArchiveOutline20, IconBrowseOutline16, IconDataOutline16, IconEditOutline16, IconFolderOpenOutline16, IconLinkOutline16, IconPlayOutline16, IconPlusOutline16, IconRefreshOutline16, IconSearchOutline16, IconSparkle16, IconStopFill16, IconTrashOutline16, StateDot, TerminalBlock, } from "@deepseek-ai/dsh-client-ui-primitives";
6
+ import { DisclosureRow, IconApiOutlineRegular, IconArchiveOutlineRegular, IconBrowseOutlineRegular, IconDataOutlineRegular, IconEditOutlineRegular, IconFolderOpenOutlineRegular, IconLinkOutlineRegular, IconPlayOutlineRegular, IconPlusOutlineRegular, IconRefreshOutlineRegular, IconSearchOutlineRegular, IconSparkleRegular, IconStopFillRegular, IconTrashOutlineRegular, StateDot, TerminalBlock, } from "@deepseek-ai/dsh-client-ui-primitives";
7
7
  import { TERMINAL_CLASS } from "./terminal-styles.js";
8
8
  const TOOL_PRESENTATION = {
9
- container_bash: { titleKey: "toolTitle_container_bash", icon: _jsx(IconApiOutline14, { size: 14 }), summaryKeys: ["description", "command"] },
10
- container_exec: { titleKey: "toolTitle_container_exec", icon: _jsx(IconApiOutline14, { size: 14 }), summaryKeys: ["description", "argv"] },
11
- container_read: { titleKey: "toolTitle_container_read", icon: _jsx(IconBrowseOutline16, { size: 14 }), summaryKeys: ["file_path"] },
12
- container_write: { titleKey: "toolTitle_container_write", icon: _jsx(IconEditOutline16, { size: 14 }), summaryKeys: ["file_path"] },
13
- container_edit: { titleKey: "toolTitle_container_edit", icon: _jsx(IconEditOutline16, { size: 14 }), summaryKeys: ["file_path"] },
14
- container_glob: { titleKey: "toolTitle_container_glob", icon: _jsx(IconSearchOutline16, { size: 14 }), summaryKeys: ["pattern"] },
15
- container_grep: { titleKey: "toolTitle_container_grep", icon: _jsx(IconSearchOutline16, { size: 14 }), summaryKeys: ["pattern"] },
16
- container_list: { titleKey: "toolTitle_container_list", icon: _jsx(IconDataOutline16, { size: 14 }), summaryKeys: [] },
17
- container_start: { titleKey: "toolTitle_container_start", icon: _jsx(IconPlayOutline16, { size: 14 }), summaryKeys: ["container", "image"] },
18
- container_recreate: { titleKey: "toolTitle_container_recreate", icon: _jsx(IconRefreshOutline16, { size: 14 }), summaryKeys: ["container", "image"] },
19
- container_remove: { titleKey: "toolTitle_container_remove", icon: _jsx(IconTrashOutline16, { size: 14 }), summaryKeys: ["container"] },
20
- container_mount_list: { titleKey: "toolTitle_container_mount_list", icon: _jsx(IconFolderOpenOutline16, { size: 14 }), summaryKeys: ["container"] },
21
- container_mount_add: { titleKey: "toolTitle_container_mount_add", icon: _jsx(IconFolderOpenOutline16, { size: 14 }), summaryKeys: ["container", "kind"] },
22
- container_mount_remove: { titleKey: "toolTitle_container_mount_remove", icon: _jsx(IconTrashOutline16, { size: 14 }), summaryKeys: ["container"] },
23
- container_mount_update: { titleKey: "toolTitle_container_mount_update", icon: _jsx(IconRefreshOutline16, { size: 14 }), summaryKeys: ["container", "mode"] },
24
- container_path_set: { titleKey: "toolTitle_container_path_set", icon: _jsx(IconEditOutline16, { size: 14 }), summaryKeys: ["container"] },
25
- container_path_add: { titleKey: "toolTitle_container_path_add", icon: _jsx(IconPlusOutline16, { size: 14 }), summaryKeys: ["container", "path"] },
26
- container_path_remove: { titleKey: "toolTitle_container_path_remove", icon: _jsx(IconTrashOutline16, { size: 14 }), summaryKeys: ["container", "path"] },
27
- container_secret_add: { titleKey: "toolTitle_container_secret_add", icon: _jsx(IconLinkOutline16, { size: 14 }), summaryKeys: ["container", "env"] },
28
- container_secret_remove: { titleKey: "toolTitle_container_secret_remove", icon: _jsx(IconLinkOutline16, { size: 14 }), summaryKeys: ["container", "env"] },
29
- image_list: { titleKey: "toolTitle_image_list", icon: _jsx(IconArchiveOutline20, { size: 14 }), summaryKeys: [] },
30
- image_get: { titleKey: "toolTitle_image_get", icon: _jsx(IconArchiveOutline20, { size: 14 }), summaryKeys: ["imageId"] },
31
- image_build: { titleKey: "toolTitle_image_build", icon: _jsx(IconPlusOutline16, { size: 14 }), summaryKeys: ["imageId", "parent"] },
32
- image_rebuild: { titleKey: "toolTitle_image_rebuild", icon: _jsx(IconRefreshOutline16, { size: 14 }), summaryKeys: ["imageId"] },
33
- image_rebuild_all: { titleKey: "toolTitle_image_rebuild_all", icon: _jsx(IconRefreshOutline16, { size: 14 }), summaryKeys: [] },
34
- image_remove: { titleKey: "toolTitle_image_remove", icon: _jsx(IconTrashOutline16, { size: 14 }), summaryKeys: ["imageId"] },
35
- volume_list: { titleKey: "toolTitle_volume_list", icon: _jsx(IconDataOutline16, { size: 14 }), summaryKeys: [] },
36
- volume_create: { titleKey: "toolTitle_volume_create", icon: _jsx(IconPlusOutline16, { size: 14 }), summaryKeys: ["name"] },
37
- volume_remove: { titleKey: "toolTitle_volume_remove", icon: _jsx(IconTrashOutline16, { size: 14 }), summaryKeys: ["name"] },
38
- secret_list: { titleKey: "toolTitle_secret_list", icon: _jsx(IconLinkOutline16, { size: 14 }), summaryKeys: [] },
39
- secret_create: { titleKey: "toolTitle_secret_create", icon: _jsx(IconPlusOutline16, { size: 14 }), summaryKeys: ["name"] },
40
- secret_remove: { titleKey: "toolTitle_secret_remove", icon: _jsx(IconTrashOutline16, { size: 14 }), summaryKeys: ["name"] },
41
- daemon_start: { titleKey: "toolTitle_daemon_start", icon: _jsx(IconPlayOutline16, { size: 14 }), summaryKeys: ["name"] },
42
- daemon_list: { titleKey: "toolTitle_daemon_list", icon: _jsx(IconPlayOutline16, { size: 14 }), summaryKeys: ["container"] },
43
- daemon_logs: { titleKey: "toolTitle_daemon_logs", icon: _jsx(IconBrowseOutline16, { size: 14 }), summaryKeys: ["name"] },
44
- daemon_restart: { titleKey: "toolTitle_daemon_restart", icon: _jsx(IconRefreshOutline16, { size: 14 }), summaryKeys: ["name"] },
45
- daemon_stop: { titleKey: "toolTitle_daemon_stop", icon: _jsx(IconStopFill16, { size: 14 }), summaryKeys: ["name"] },
9
+ container_bash: {
10
+ titleKey: "toolTitle_container_bash",
11
+ icon: _jsx(IconApiOutlineRegular, { size: 14 }),
12
+ summaryKeys: ["description", "command"],
13
+ },
14
+ container_exec: {
15
+ titleKey: "toolTitle_container_exec",
16
+ icon: _jsx(IconApiOutlineRegular, { size: 14 }),
17
+ summaryKeys: ["description", "argv"],
18
+ },
19
+ container_read: {
20
+ titleKey: "toolTitle_container_read",
21
+ icon: _jsx(IconBrowseOutlineRegular, { size: 14 }),
22
+ summaryKeys: ["file_path"],
23
+ },
24
+ container_write: {
25
+ titleKey: "toolTitle_container_write",
26
+ icon: _jsx(IconEditOutlineRegular, { size: 14 }),
27
+ summaryKeys: ["file_path"],
28
+ },
29
+ container_edit: {
30
+ titleKey: "toolTitle_container_edit",
31
+ icon: _jsx(IconEditOutlineRegular, { size: 14 }),
32
+ summaryKeys: ["file_path"],
33
+ },
34
+ container_glob: {
35
+ titleKey: "toolTitle_container_glob",
36
+ icon: _jsx(IconSearchOutlineRegular, { size: 14 }),
37
+ summaryKeys: ["pattern"],
38
+ },
39
+ container_grep: {
40
+ titleKey: "toolTitle_container_grep",
41
+ icon: _jsx(IconSearchOutlineRegular, { size: 14 }),
42
+ summaryKeys: ["pattern"],
43
+ },
44
+ container_list: {
45
+ titleKey: "toolTitle_container_list",
46
+ icon: _jsx(IconDataOutlineRegular, { size: 14 }),
47
+ summaryKeys: [],
48
+ },
49
+ container_start: {
50
+ titleKey: "toolTitle_container_start",
51
+ icon: _jsx(IconPlayOutlineRegular, { size: 14 }),
52
+ summaryKeys: ["container", "image"],
53
+ },
54
+ container_recreate: {
55
+ titleKey: "toolTitle_container_recreate",
56
+ icon: _jsx(IconRefreshOutlineRegular, { size: 14 }),
57
+ summaryKeys: ["container", "image"],
58
+ },
59
+ container_remove: {
60
+ titleKey: "toolTitle_container_remove",
61
+ icon: _jsx(IconTrashOutlineRegular, { size: 14 }),
62
+ summaryKeys: ["container"],
63
+ },
64
+ container_mount_list: {
65
+ titleKey: "toolTitle_container_mount_list",
66
+ icon: _jsx(IconFolderOpenOutlineRegular, { size: 14 }),
67
+ summaryKeys: ["container"],
68
+ },
69
+ container_mount_add: {
70
+ titleKey: "toolTitle_container_mount_add",
71
+ icon: _jsx(IconFolderOpenOutlineRegular, { size: 14 }),
72
+ summaryKeys: ["container", "kind"],
73
+ },
74
+ container_mount_remove: {
75
+ titleKey: "toolTitle_container_mount_remove",
76
+ icon: _jsx(IconTrashOutlineRegular, { size: 14 }),
77
+ summaryKeys: ["container"],
78
+ },
79
+ container_mount_update: {
80
+ titleKey: "toolTitle_container_mount_update",
81
+ icon: _jsx(IconRefreshOutlineRegular, { size: 14 }),
82
+ summaryKeys: ["container", "mode"],
83
+ },
84
+ container_path_set: {
85
+ titleKey: "toolTitle_container_path_set",
86
+ icon: _jsx(IconEditOutlineRegular, { size: 14 }),
87
+ summaryKeys: ["container"],
88
+ },
89
+ container_path_add: {
90
+ titleKey: "toolTitle_container_path_add",
91
+ icon: _jsx(IconPlusOutlineRegular, { size: 14 }),
92
+ summaryKeys: ["container", "path"],
93
+ },
94
+ container_path_remove: {
95
+ titleKey: "toolTitle_container_path_remove",
96
+ icon: _jsx(IconTrashOutlineRegular, { size: 14 }),
97
+ summaryKeys: ["container", "path"],
98
+ },
99
+ container_secret_add: {
100
+ titleKey: "toolTitle_container_secret_add",
101
+ icon: _jsx(IconLinkOutlineRegular, { size: 14 }),
102
+ summaryKeys: ["container", "env"],
103
+ },
104
+ container_secret_remove: {
105
+ titleKey: "toolTitle_container_secret_remove",
106
+ icon: _jsx(IconLinkOutlineRegular, { size: 14 }),
107
+ summaryKeys: ["container", "env"],
108
+ },
109
+ image_list: {
110
+ titleKey: "toolTitle_image_list",
111
+ icon: _jsx(IconArchiveOutlineRegular, { size: 14 }),
112
+ summaryKeys: [],
113
+ },
114
+ image_get: {
115
+ titleKey: "toolTitle_image_get",
116
+ icon: _jsx(IconArchiveOutlineRegular, { size: 14 }),
117
+ summaryKeys: ["imageId"],
118
+ },
119
+ image_build: {
120
+ titleKey: "toolTitle_image_build",
121
+ icon: _jsx(IconPlusOutlineRegular, { size: 14 }),
122
+ summaryKeys: ["imageId", "parent"],
123
+ },
124
+ image_rebuild: {
125
+ titleKey: "toolTitle_image_rebuild",
126
+ icon: _jsx(IconRefreshOutlineRegular, { size: 14 }),
127
+ summaryKeys: ["imageId"],
128
+ },
129
+ image_rebuild_all: {
130
+ titleKey: "toolTitle_image_rebuild_all",
131
+ icon: _jsx(IconRefreshOutlineRegular, { size: 14 }),
132
+ summaryKeys: [],
133
+ },
134
+ image_remove: {
135
+ titleKey: "toolTitle_image_remove",
136
+ icon: _jsx(IconTrashOutlineRegular, { size: 14 }),
137
+ summaryKeys: ["imageId"],
138
+ },
139
+ volume_list: {
140
+ titleKey: "toolTitle_volume_list",
141
+ icon: _jsx(IconDataOutlineRegular, { size: 14 }),
142
+ summaryKeys: [],
143
+ },
144
+ volume_create: {
145
+ titleKey: "toolTitle_volume_create",
146
+ icon: _jsx(IconPlusOutlineRegular, { size: 14 }),
147
+ summaryKeys: ["name"],
148
+ },
149
+ volume_remove: {
150
+ titleKey: "toolTitle_volume_remove",
151
+ icon: _jsx(IconTrashOutlineRegular, { size: 14 }),
152
+ summaryKeys: ["name"],
153
+ },
154
+ secret_list: {
155
+ titleKey: "toolTitle_secret_list",
156
+ icon: _jsx(IconLinkOutlineRegular, { size: 14 }),
157
+ summaryKeys: [],
158
+ },
159
+ secret_create: {
160
+ titleKey: "toolTitle_secret_create",
161
+ icon: _jsx(IconPlusOutlineRegular, { size: 14 }),
162
+ summaryKeys: ["name"],
163
+ },
164
+ secret_remove: {
165
+ titleKey: "toolTitle_secret_remove",
166
+ icon: _jsx(IconTrashOutlineRegular, { size: 14 }),
167
+ summaryKeys: ["name"],
168
+ },
169
+ daemon_start: {
170
+ titleKey: "toolTitle_daemon_start",
171
+ icon: _jsx(IconPlayOutlineRegular, { size: 14 }),
172
+ summaryKeys: ["name"],
173
+ },
174
+ daemon_list: {
175
+ titleKey: "toolTitle_daemon_list",
176
+ icon: _jsx(IconPlayOutlineRegular, { size: 14 }),
177
+ summaryKeys: ["container"],
178
+ },
179
+ daemon_logs: {
180
+ titleKey: "toolTitle_daemon_logs",
181
+ icon: _jsx(IconBrowseOutlineRegular, { size: 14 }),
182
+ summaryKeys: ["name"],
183
+ },
184
+ daemon_restart: {
185
+ titleKey: "toolTitle_daemon_restart",
186
+ icon: _jsx(IconRefreshOutlineRegular, { size: 14 }),
187
+ summaryKeys: ["name"],
188
+ },
189
+ daemon_stop: {
190
+ titleKey: "toolTitle_daemon_stop",
191
+ icon: _jsx(IconStopFillRegular, { size: 14 }),
192
+ summaryKeys: ["name"],
193
+ },
46
194
  };
47
195
  /** The wire tool names this package owns a row for. */
48
196
  export const TOOL_VIEW_KEYS = Object.keys(TOOL_PRESENTATION);
@@ -90,6 +238,14 @@ function firstLine(text) {
90
238
  const newline = text.indexOf("\n");
91
239
  return newline === -1 ? text : text.slice(0, newline);
92
240
  }
241
+ // A hard bound on the output lines a result may render, so a multi-megabyte
242
+ // result cannot create an unbounded number of DOM nodes. Small outputs pass
243
+ // through untouched.
244
+ const MAX_RENDERED_LINES = 2000;
245
+ function capLines(text, max) {
246
+ const lines = text.split("\n");
247
+ return lines.length <= max ? text : lines.slice(0, max).join("\n");
248
+ }
93
249
  function parseArgs(argsRaw) {
94
250
  try {
95
251
  const parsed = JSON.parse(argsRaw);
@@ -146,12 +302,11 @@ function prettyOutput(text) {
146
302
  return text;
147
303
  }
148
304
  function terminalCard(toolName, args, block, cwd) {
149
- if (toolName !== "container_bash" && toolName !== "container_exec")
305
+ if (toolName !== "container_bash" && toolName !== "container_exec") {
150
306
  return null;
307
+ }
151
308
  const command = toolName === "container_bash"
152
- ? typeof args.command === "string"
153
- ? args.command
154
- : ""
309
+ ? typeof args.command === "string" ? args.command : ""
155
310
  : Array.isArray(args.argv)
156
311
  ? args.argv.map((word) => String(word)).join(" ")
157
312
  : "";
@@ -185,9 +340,9 @@ function terminalCard(toolName, args, block, cwd) {
185
340
  return {
186
341
  ...base,
187
342
  output,
188
- ...(signal !== undefined
189
- ? { signal }
190
- : { exitCode: typeof record.exitCode === "number" ? record.exitCode : 0 }),
343
+ ...(signal !== undefined ? { signal } : {
344
+ exitCode: typeof record.exitCode === "number" ? record.exitCode : 0,
345
+ }),
191
346
  };
192
347
  }
193
348
  catch {
@@ -200,6 +355,7 @@ function terminalLabels(t) {
200
355
  return {
201
356
  signal: (signal) => t("terminalSignal", { signal }),
202
357
  exitCode: (code) => t("terminalExitCode", { code }),
358
+ noExitCode: t("terminalNoExitCode"),
203
359
  running: t("terminalRunning"),
204
360
  failed: t("terminalFailed"),
205
361
  done: t("terminalDone"),
@@ -215,9 +371,11 @@ function terminalLabels(t) {
215
371
  /** Render one podman tool call as an icon-titled, expandable row. */
216
372
  export function PodmanToolRow({ toolName, block, cwd, home, inspect, t, }) {
217
373
  const presentation = TOOL_PRESENTATION[toolName];
218
- const title = presentation === undefined ? toolName : t(presentation.titleKey);
374
+ const title = presentation === undefined
375
+ ? toolName
376
+ : t(presentation.titleKey);
219
377
  const settled = "kind" in block;
220
- const argsRaw = ((settled ? block.call?.argsRaw : block.argsRaw) ?? "");
378
+ const argsRaw = (settled ? block.call?.argsRaw : block.argsRaw) ?? "";
221
379
  const args = parseArgs(argsRaw);
222
380
  const terminal = terminalCard(toolName, args, block, cwd);
223
381
  const state = !settled
@@ -235,7 +393,15 @@ export function PodmanToolRow({ toolName, block, cwd, home, inspect, t, }) {
235
393
  argSummary(args, presentation?.summaryKeys ?? [], firstLine(argsRaw) || block.callId);
236
394
  const [expanded, setExpanded] = useState(false);
237
395
  const expandable = terminal !== null || (output !== null && output !== "");
238
- const leading = state === "error" ? (_jsx(StateDot, { state: "error" })) : state === "stopped" ? (_jsx(StateDot, { state: "warning" })) : (presentation?.icon ?? _jsx(IconSparkle16, { size: 14 }));
396
+ const leading = state === "error"
397
+ ? _jsx(StateDot, { state: "error" })
398
+ : state === "stopped"
399
+ ? _jsx(StateDot, { state: "warning" })
400
+ : (presentation?.icon ?? _jsx(IconSparkleRegular, { size: 14 }));
239
401
  void inspect;
240
- return (_jsx(DisclosureRow, { icon: leading, title: title, open: expanded && expandable, expandable: expandable, onToggle: () => setExpanded((value) => !value), expandOnRowClick: true, keepContentWhenOpen: true, collapsedContent: _jsxs(_Fragment, { children: [_jsx("span", { style: sepStyle, "aria-hidden": true }), _jsx("span", { style: failureLine !== null ? errorSummaryStyle : summaryStyle, children: summary })] }), children: terminal !== null ? (_jsx(TerminalBlock, { command: terminal.command, cwd: terminal.cwd, home: home, output: terminal.output, exitCode: terminal.exitCode, signal: terminal.signal, running: terminal.running, maxLines: Infinity, className: TERMINAL_CLASS, labels: terminalLabels(t) })) : expandable ? (_jsx("pre", { style: state === "error" ? errorBodyStyle : bodyStyle, children: output })) : null }));
402
+ return (_jsx(DisclosureRow, { icon: leading, title: title, open: expanded && expandable, expandable: expandable, onToggle: () => setExpanded((value) => !value), expandOnRowClick: true, keepContentWhenOpen: true, collapsedContent: _jsxs(_Fragment, { children: [_jsx("span", { style: sepStyle, "aria-hidden": true }), _jsx("span", { style: failureLine !== null ? errorSummaryStyle : summaryStyle, children: summary })] }), children: terminal !== null
403
+ ? (_jsx(TerminalBlock, { command: terminal.command, cwd: terminal.cwd, home: home, output: terminal.output, exitCode: terminal.exitCode, signal: terminal.signal, running: terminal.running, maxLines: MAX_RENDERED_LINES, className: TERMINAL_CLASS, labels: terminalLabels(t) }))
404
+ : expandable
405
+ ? (_jsx("pre", { style: state === "error" ? errorBodyStyle : bodyStyle, children: capLines(output ?? "", MAX_RENDERED_LINES) }))
406
+ : null }));
241
407
  }
@@ -0,0 +1,11 @@
1
+ export declare function mergeDefaultEnv(defaults: Record<string, string> | undefined, env: Record<string, string> | undefined): Record<string, string>;
2
+ export declare function missingDefaultEnv(defaults: Record<string, string> | undefined, env: Record<string, string> | undefined): Record<string, string>;
3
+ export declare function gitIdentityEnv(name: string, email: string): Record<string, string>;
4
+ export declare function readGitIdentity(env: Record<string, string> | undefined): {
5
+ name: string;
6
+ email: string;
7
+ };
8
+ export declare const GIT_IDENTITY_KEYS: readonly ["GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL"];
9
+ export declare function setGitIdentity(env: Record<string, string>, name: string, email: string): Record<string, string>;
10
+ export declare function gitOnlyDrift(server: Record<string, string>, before: Record<string, string>): boolean;
11
+ export declare function commitIdentityAction(server: Record<string, string>, draft: Record<string, string>, next: Record<string, string>): "discard" | "save" | "draft";
@@ -0,0 +1,96 @@
1
+ // SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
2
+ //
3
+ // SPDX-License-Identifier: MIT
4
+ // The container environment defaults: one global map, seeded into a container
5
+ // when it is created (see mergeDefaultEnv) and left alone by recreates, so a
6
+ // value can be removed again by recreating the container without it.
7
+ // The reserved prefix the orchestrator refuses on a client-supplied variable;
8
+ // a default in that namespace would make container creation fail, so it is
9
+ // dropped instead of sent.
10
+ const RESERVED_ENV_PREFIX = "DSH_PODMAN";
11
+ // mergeDefaultEnv layers the defaults under a container's own environment: the
12
+ // caller's entries win per key, and defaults that cannot be applied (an empty
13
+ // key, or one in the reserved namespace) are dropped.
14
+ export function mergeDefaultEnv(defaults, env) {
15
+ const merged = {};
16
+ for (const [key, value] of Object.entries(defaults ?? {})) {
17
+ if (key === "" || key.startsWith(RESERVED_ENV_PREFIX))
18
+ continue;
19
+ merged[key] = value;
20
+ }
21
+ return { ...merged, ...(env ?? {}) };
22
+ }
23
+ // missingDefaultEnv returns the defaults a container does not have yet, so a
24
+ // synchronisation can add exactly those and never overwrite an existing value.
25
+ export function missingDefaultEnv(defaults, env) {
26
+ const missing = {};
27
+ for (const [key, value] of Object.entries(mergeDefaultEnv(defaults, undefined))) {
28
+ if (!Object.prototype.hasOwnProperty.call(env ?? {}, key)) {
29
+ missing[key] = value;
30
+ }
31
+ }
32
+ return missing;
33
+ }
34
+ // gitIdentityEnv turns one name and one email into the four variables git
35
+ // needs: the author and the committer pair are the same identity.
36
+ export function gitIdentityEnv(name, email) {
37
+ return {
38
+ GIT_AUTHOR_NAME: name,
39
+ GIT_AUTHOR_EMAIL: email,
40
+ GIT_COMMITTER_NAME: name,
41
+ GIT_COMMITTER_EMAIL: email,
42
+ };
43
+ }
44
+ // readGitIdentity reads the name and email back out of an environment map (the
45
+ // author pair; the popup writes all four together).
46
+ export function readGitIdentity(env) {
47
+ return {
48
+ name: env?.GIT_AUTHOR_NAME ?? "",
49
+ email: env?.GIT_AUTHOR_EMAIL ?? "",
50
+ };
51
+ }
52
+ // The four keys gitIdentityEnv owns, in a stable order.
53
+ export const GIT_IDENTITY_KEYS = [
54
+ "GIT_AUTHOR_NAME",
55
+ "GIT_AUTHOR_EMAIL",
56
+ "GIT_COMMITTER_NAME",
57
+ "GIT_COMMITTER_EMAIL",
58
+ ];
59
+ // setGitIdentity returns env with the git identity added (name and email
60
+ // non-empty) or removed (either empty).
61
+ export function setGitIdentity(env, name, email) {
62
+ const next = { ...env };
63
+ for (const key of GIT_IDENTITY_KEYS)
64
+ delete next[key];
65
+ if (name === "" || email === "")
66
+ return next;
67
+ return { ...next, ...gitIdentityEnv(name, email) };
68
+ }
69
+ // gitOnlyDrift answers whether `before` differs from the persisted `server`
70
+ // only inside the git identity keys. When that holds, the identity popup can
71
+ // commit its change directly, because no other default is being edited.
72
+ export function gitOnlyDrift(server, before) {
73
+ const identity = new Set(GIT_IDENTITY_KEYS);
74
+ const keys = new Set([...Object.keys(server), ...Object.keys(before)]);
75
+ for (const key of keys) {
76
+ if (identity.has(key))
77
+ continue;
78
+ if (server[key] !== before[key])
79
+ return false;
80
+ }
81
+ return true;
82
+ }
83
+ // commitIdentityAction decides how the identity popup's result `next` is
84
+ // applied: "discard" when it restores the persisted environment exactly (the
85
+ // popup brought the identity back, so its pending change is dropped), "save"
86
+ // when the only drift from the server is inside the identity keys (committing
87
+ // straight cannot lose any other edit), "draft" otherwise (other defaults are
88
+ // being edited, so the result lands in the draft and the Save button stays in
89
+ // charge). Since setGitIdentity copies every non-identity key, `next` can
90
+ // equal the server only when nothing but the identity was pending, so
91
+ // "discard" never loses another edit.
92
+ export function commitIdentityAction(server, draft, next) {
93
+ if (JSON.stringify(next) === JSON.stringify(server))
94
+ return "discard";
95
+ return gitOnlyDrift(server, draft) ? "save" : "draft";
96
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,93 @@
1
+ // SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
2
+ //
3
+ // SPDX-License-Identifier: MIT
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import { commitIdentityAction, GIT_IDENTITY_KEYS, gitIdentityEnv, gitOnlyDrift, mergeDefaultEnv, missingDefaultEnv, readGitIdentity, setGitIdentity, } from "./container-env.js";
7
+ test("mergeDefaultEnv seeds defaults under the container's own env", () => {
8
+ assert.deepEqual(mergeDefaultEnv({ A: "1", B: "2" }, { B: "own", C: "3" }), {
9
+ A: "1",
10
+ B: "own",
11
+ C: "3",
12
+ });
13
+ assert.deepEqual(mergeDefaultEnv(undefined, { A: "1" }), { A: "1" });
14
+ assert.deepEqual(mergeDefaultEnv({ A: "1" }, undefined), { A: "1" });
15
+ });
16
+ test("mergeDefaultEnv drops empty and reserved defaults", () => {
17
+ assert.deepEqual(mergeDefaultEnv({ "": "x", DSH_PODMAN_GUEST_TOKEN: "x", KEEP: "1" }, {}), { KEEP: "1" });
18
+ // An explicit value in the reserved namespace is the caller's own doing and
19
+ // is left to the orchestrator's validation.
20
+ assert.deepEqual(mergeDefaultEnv({}, { DSH_PODMAN_GUEST_TOKEN: "x" }), {
21
+ DSH_PODMAN_GUEST_TOKEN: "x",
22
+ });
23
+ });
24
+ test("missingDefaultEnv reports only the absent keys", () => {
25
+ assert.deepEqual(missingDefaultEnv({ A: "1", B: "2" }, { B: "own" }), {
26
+ A: "1",
27
+ });
28
+ assert.deepEqual(missingDefaultEnv({ A: "1" }, { A: "own" }), {});
29
+ assert.deepEqual(missingDefaultEnv(undefined, {}), {});
30
+ });
31
+ test("git identity fills the author and committer pairs", () => {
32
+ assert.deepEqual(gitIdentityEnv("John Doe", "john.doe@git.example"), {
33
+ GIT_AUTHOR_NAME: "John Doe",
34
+ GIT_AUTHOR_EMAIL: "john.doe@git.example",
35
+ GIT_COMMITTER_NAME: "John Doe",
36
+ GIT_COMMITTER_EMAIL: "john.doe@git.example",
37
+ });
38
+ assert.deepEqual(readGitIdentity(gitIdentityEnv("John Doe", "john.doe@git.example")), {
39
+ name: "John Doe",
40
+ email: "john.doe@git.example",
41
+ });
42
+ assert.deepEqual(readGitIdentity(undefined), { name: "", email: "" });
43
+ });
44
+ test("setGitIdentity adds, replaces and clears the four keys", () => {
45
+ const base = { KEEP: "1", GIT_AUTHOR_NAME: "old" };
46
+ const set = setGitIdentity(base, "new", "new@example.com");
47
+ assert.deepEqual(set, {
48
+ KEEP: "1",
49
+ ...gitIdentityEnv("new", "new@example.com"),
50
+ });
51
+ for (const key of GIT_IDENTITY_KEYS)
52
+ assert.ok(key in set);
53
+ // An empty field clears the whole identity, leaving other defaults alone.
54
+ assert.deepEqual(setGitIdentity(set, "", "new@example.com"), { KEEP: "1" });
55
+ assert.deepEqual(setGitIdentity(set, "new", ""), { KEEP: "1" });
56
+ // The input map is not mutated.
57
+ assert.deepEqual(base, { KEEP: "1", GIT_AUTHOR_NAME: "old" });
58
+ });
59
+ test("gitOnlyDrift tells an identity-only change apart from other edits", () => {
60
+ const identity = gitIdentityEnv("John Doe", "john.doe@git.example");
61
+ // No drift at all qualifies (there is nothing else to lose by committing).
62
+ assert.equal(gitOnlyDrift(identity, identity), true);
63
+ // Changing or removing the identity alone is git-only.
64
+ assert.equal(gitOnlyDrift(identity, setGitIdentity(identity, "New", "new@example.com")), true);
65
+ assert.equal(gitOnlyDrift(identity, setGitIdentity(identity, "", "")), true);
66
+ // Any other default drift means the Save button must stay in charge.
67
+ assert.equal(gitOnlyDrift(identity, { ...identity, KEEP: "1" }), false);
68
+ assert.equal(gitOnlyDrift({ KEEP: "1" }, { KEEP: "2" }), false);
69
+ // Removing a non-identity key, or an identity key drifting in the server map
70
+ // while another edit exists, both count as a real diff.
71
+ assert.equal(gitOnlyDrift({ KEEP: "1", ...identity }, identity), false);
72
+ });
73
+ test("commitIdentityAction applies the popup result without losing edits", () => {
74
+ const saved = gitIdentityEnv("John Doe", "john.doe@git.example");
75
+ const changed = setGitIdentity(saved, "New", "new@example.com");
76
+ // The popup restores the saved identity while the draft still holds the
77
+ // changed one: the pending identity edit is discarded, not kept (regression:
78
+ // the old code returned early and left the changed identity in the draft).
79
+ assert.equal(commitIdentityAction(saved, changed, saved), "discard");
80
+ // No drift at all and an unchanged popup is a no-op discard too.
81
+ assert.equal(commitIdentityAction(saved, saved, saved), "discard");
82
+ // An identity-only change commits straight: nothing else can be lost.
83
+ assert.equal(commitIdentityAction(saved, changed, changed), "save");
84
+ assert.equal(commitIdentityAction(saved, saved, changed), "save");
85
+ // Clearing the identity alone is also git-only.
86
+ assert.equal(commitIdentityAction(saved, changed, {}), "save");
87
+ // Another pending default keeps the result in the draft, whatever the popup
88
+ // does to the identity: change, restore, or clear.
89
+ const withKeep = { ...saved, KEEP: "1" };
90
+ assert.equal(commitIdentityAction(saved, withKeep, { ...withKeep, ...changed }), "draft");
91
+ assert.equal(commitIdentityAction(saved, withKeep, withKeep), "draft");
92
+ assert.equal(commitIdentityAction(saved, withKeep, { KEEP: "1" }), "draft");
93
+ });