@exagone313/dsh-podman 0.2.0-rc.2 → 0.2.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.pkg +17103 -0
- package/README.md +2 -2
- package/dist/card-route.js +52 -8
- package/dist/card-route.test.js +26 -1
- package/dist/client/ContainerCard.js +40 -4
- package/dist/client/card-protocol.d.ts +3 -0
- package/dist/client/container-card-controller.d.ts +3 -0
- package/dist/client/container-card-controller.js +6 -0
- package/dist/client/index.js +118 -5
- package/dist/client/locales.d.ts +1 -1
- package/dist/client/locales.js +18 -0
- package/dist/dsh-version.d.ts +1 -0
- package/dist/dsh-version.js +45 -0
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +2 -2
- package/dist/grpc/proto/dshctl/v1/control.proto +3 -0
- package/dist/settings-workspaces.test.js +3 -1
- package/dist/version-compat.d.ts +4 -0
- package/dist/version-compat.js +29 -0
- package/dist/version-compat.test.d.ts +1 -0
- package/dist/version-compat.test.js +27 -0
- package/dist/workspace-binding.d.ts +1 -1
- package/dist/workspace-binding.js +10 -3
- package/dist/workspace-binding.test.js +13 -2
- package/docs/architecture.md +10 -0
- package/docs/architecture.zh.md +6 -0
- package/docs/development.md +47 -12
- package/docs/development.zh.md +43 -12
- package/docs/install-dsh-and-dsh-podman.md +3 -13
- package/docs/install-dsh-and-dsh-podman.zh.md +2 -13
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -32,9 +32,9 @@ per-project Podman containers.
|
|
|
32
32
|
|
|
33
33
|
This repository was written in major parts using AI models:
|
|
34
34
|
|
|
35
|
+
- DeepSeek Flash
|
|
35
36
|
- early commits using OpenAI GPT-5.6 Luna
|
|
36
|
-
-
|
|
37
|
-
- source code weakness fixes by Anthropic Claude Opus 5
|
|
37
|
+
- a few fixes using Anthropic Claude Opus 5
|
|
38
38
|
|
|
39
39
|
## License
|
|
40
40
|
|
package/dist/card-route.js
CHANGED
|
@@ -11,6 +11,19 @@ import { defaultMountMode, mountKindToProto, mountModeToProto } from "./mount-en
|
|
|
11
11
|
import { unaryGuest } from "./guest-rpc.js";
|
|
12
12
|
import { workspaceSlug } from "./workspace-binding.js";
|
|
13
13
|
import { GIT_COMMIT, VERSION } from "./generated/version.js";
|
|
14
|
+
import { versionState as pluginVersionState } from "./version-compat.js";
|
|
15
|
+
import { dshVersion } from "./dsh-version.js";
|
|
16
|
+
// readOrchestratorVersion asks the orchestrator for its version, or returns ""
|
|
17
|
+
// when the call fails (an orchestrator predating the handshake).
|
|
18
|
+
async function readOrchestratorVersion(resolver) {
|
|
19
|
+
try {
|
|
20
|
+
const response = await resolver.control("getVersion", {});
|
|
21
|
+
return String(response?.version ?? "");
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
14
27
|
// The cache clean modes the settings card can request, mapped to the control
|
|
15
28
|
// plane's enum names.
|
|
16
29
|
const CACHE_CLEAN_MODES = {
|
|
@@ -99,17 +112,48 @@ function dshWorkspaceCwd(registry, slug) {
|
|
|
99
112
|
}
|
|
100
113
|
/** Build the live snapshot the card renders. */
|
|
101
114
|
export async function cardSnapshot(resolver, workspaceRegistry) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
// The handshake RPC is exempt from the orchestrator's version check, so it
|
|
116
|
+
// succeeds even when the rest is refused; an older orchestrator that has no
|
|
117
|
+
// GetVersion yet leaves the version unknown.
|
|
118
|
+
const orchestratorVersion = await readOrchestratorVersion(resolver);
|
|
119
|
+
let listings;
|
|
120
|
+
try {
|
|
121
|
+
listings = await Promise.all([
|
|
122
|
+
resolver.control("listContainers", {}),
|
|
123
|
+
resolver.control("listImages", {}),
|
|
124
|
+
resolver.control("listWorkspaces", {}),
|
|
125
|
+
resolver.control("listVolumes", {}),
|
|
126
|
+
resolver.control("listSecrets", {}),
|
|
127
|
+
resolver.control("listCaches", {}),
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
// An incompatible plugin is refused before any handler runs, so the card
|
|
132
|
+
// reports the mismatch instead of an unexplained empty snapshot.
|
|
133
|
+
if (error?.code !== "FailedPrecondition")
|
|
134
|
+
throw error;
|
|
135
|
+
return {
|
|
136
|
+
version: VERSION,
|
|
137
|
+
commit: GIT_COMMIT,
|
|
138
|
+
dshVersion: dshVersion(),
|
|
139
|
+
orchestratorVersion,
|
|
140
|
+
versionState: "major-mismatch",
|
|
141
|
+
projectsRoot: resolver.getConfig().projectsRoot,
|
|
142
|
+
workspaces: [],
|
|
143
|
+
containers: [],
|
|
144
|
+
images: [],
|
|
145
|
+
volumes: [],
|
|
146
|
+
secrets: [],
|
|
147
|
+
caches: [],
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const [containers, images, workspaces, volumes, secrets, caches] = listings;
|
|
110
151
|
return {
|
|
111
152
|
version: VERSION,
|
|
112
153
|
commit: GIT_COMMIT,
|
|
154
|
+
dshVersion: dshVersion(),
|
|
155
|
+
orchestratorVersion,
|
|
156
|
+
versionState: pluginVersionState(VERSION, orchestratorVersion),
|
|
113
157
|
projectsRoot: resolver.getConfig().projectsRoot,
|
|
114
158
|
workspaces: mergeWorkspaceViews(dshWorkspaceViews(workspaceRegistry, resolver.getConfig().projectsRoot), orchestratorWorkspaceViews(workspaces.workspaces)),
|
|
115
159
|
containers: (containers.containers ?? []).map((container) => ({
|
package/dist/card-route.test.js
CHANGED
|
@@ -17,6 +17,8 @@ function fakeResolver() {
|
|
|
17
17
|
setConfig: () => { },
|
|
18
18
|
async control(method, request) {
|
|
19
19
|
calls.push([method, request]);
|
|
20
|
+
if (method === "getVersion")
|
|
21
|
+
return { version: "9.9.9", commit: "abc" };
|
|
20
22
|
if (method === "listContainers")
|
|
21
23
|
return { containers: [{ containerName: "c1" }] };
|
|
22
24
|
if (method === "listImages")
|
|
@@ -92,7 +94,30 @@ test("the card route serves the live snapshot on GET", async () => {
|
|
|
92
94
|
assert.deepEqual(snapshot.volumes, [{ name: "data" }]);
|
|
93
95
|
assert.deepEqual(snapshot.secrets, [{ name: "s1" }]);
|
|
94
96
|
assert.equal(snapshot.caches[0].bytes, 1024);
|
|
95
|
-
assert.deepEqual(calls.map(([method]) => method), ["listContainers", "listImages", "listWorkspaces", "listVolumes", "listSecrets", "listCaches"]);
|
|
97
|
+
assert.deepEqual(calls.map(([method]) => method), ["getVersion", "listContainers", "listImages", "listWorkspaces", "listVolumes", "listSecrets", "listCaches"]);
|
|
98
|
+
assert.equal(typeof snapshot.dshVersion, "string");
|
|
99
|
+
assert.equal(snapshot.orchestratorVersion, "9.9.9");
|
|
100
|
+
assert.equal(snapshot.versionState, "major-mismatch", "a differing major is surfaced on the card");
|
|
101
|
+
});
|
|
102
|
+
test("a refused control call reports the version mismatch instead of an empty snapshot", async () => {
|
|
103
|
+
const { resolver } = fakeResolver();
|
|
104
|
+
const refusal = new Error("plugin is incompatible");
|
|
105
|
+
refusal.code = "FailedPrecondition";
|
|
106
|
+
resolver.control = async (method) => {
|
|
107
|
+
if (method === "getVersion")
|
|
108
|
+
return { version: "9.9.9", commit: "abc" };
|
|
109
|
+
throw refusal;
|
|
110
|
+
};
|
|
111
|
+
const { ctx, routes } = fakeRouteContext();
|
|
112
|
+
registerCardRoute(ctx, resolver);
|
|
113
|
+
const response = await routes[0].fetch(cardRequest());
|
|
114
|
+
assert.equal(response.status, 200);
|
|
115
|
+
const snapshot = (await response.json());
|
|
116
|
+
assert.equal(typeof snapshot.dshVersion, "string");
|
|
117
|
+
assert.equal(snapshot.orchestratorVersion, "9.9.9");
|
|
118
|
+
assert.equal(snapshot.versionState, "major-mismatch");
|
|
119
|
+
assert.deepEqual(snapshot.containers, []);
|
|
120
|
+
assert.deepEqual(snapshot.workspaces, []);
|
|
96
121
|
});
|
|
97
122
|
test("the card route runs one command per POST and returns its notice", async () => {
|
|
98
123
|
const { resolver, calls } = fakeResolver();
|
|
@@ -9,8 +9,10 @@ import { CardChevron, ConfigField, ConfirmButton } from "./container-card-shared
|
|
|
9
9
|
import { banner, cardBodyStyle, cardDescriptionStyle, cardHeadTextStyle, cardHeaderStyle, cardHoverStyle, cardNameStyle, cardOpenStyle, cardStyle, footerRow, hint, imageSelect, sectionTitle, } from "./container-card-styles.js";
|
|
10
10
|
import { VolumesSection } from "./container-card-volumes.js";
|
|
11
11
|
import { WorkspaceSection } from "./container-card-workspace.js";
|
|
12
|
-
import { Button, Input, Modal } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
13
|
-
import { useEffect, useId, useState } from "react";
|
|
12
|
+
import { Button, Input, Modal, writeClipboard, } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
13
|
+
import { useEffect, useId, useRef, useState, } from "react";
|
|
14
|
+
// The labels of the version lines, emphasised against their values.
|
|
15
|
+
const versionLabel = { fontWeight: 600 };
|
|
14
16
|
export function ContainerCard(props) {
|
|
15
17
|
const { t } = props;
|
|
16
18
|
const state = props.useContainerCard((snapshot) => snapshot);
|
|
@@ -22,7 +24,22 @@ export function ContainerCard(props) {
|
|
|
22
24
|
const [packages, setPackages] = useState([]);
|
|
23
25
|
const [defaultOpen, setDefaultOpen] = useState(false);
|
|
24
26
|
const [defaultImage, setDefaultImage] = useState(state.defaultImage);
|
|
27
|
+
const [copyState, setCopyState] = useState("idle");
|
|
28
|
+
const copyResetTimer = useRef();
|
|
25
29
|
const projectsRootId = useId();
|
|
30
|
+
// Copies the version lines as displayed, so a user can paste them into a bug
|
|
31
|
+
// report without transcribing anything.
|
|
32
|
+
const copyVersions = async () => {
|
|
33
|
+
const lines = [
|
|
34
|
+
`${t("dsh")} ${state.dshVersion || t("versionUnknown")}`,
|
|
35
|
+
`${t("cardTitle")} ${state.version}${state.commit ? ` · ${state.commit}` : ""}`,
|
|
36
|
+
`${t("orchestrator")} ${state.orchestratorVersion || t("versionUnknown")}`,
|
|
37
|
+
];
|
|
38
|
+
setCopyState((await writeClipboard(lines.join("\n"))) ? "copied" : "failed");
|
|
39
|
+
if (copyResetTimer.current !== undefined)
|
|
40
|
+
clearTimeout(copyResetTimer.current);
|
|
41
|
+
copyResetTimer.current = setTimeout(() => setCopyState("idle"), 1500);
|
|
42
|
+
};
|
|
26
43
|
// The card mounts with the settings panel, so this re-reads the live state
|
|
27
44
|
// every time the Plugins section is opened: a workspace created since the
|
|
28
45
|
// last read shows up without pressing Reload this view.
|
|
@@ -43,7 +60,11 @@ export function ContainerCard(props) {
|
|
|
43
60
|
...cardStyle,
|
|
44
61
|
...(open ? cardOpenStyle : {}),
|
|
45
62
|
...(hovered && !open ? cardHoverStyle : {}),
|
|
46
|
-
}, onPointerEnter: () => setHovered(true), onPointerLeave: () => setHovered(false), children: [_jsxs("button", { type: "button", style: cardHeaderStyle, "aria-expanded": open, onClick: () => setOpen(!open), children: [_jsxs("span", { style: cardHeadTextStyle, children: [
|
|
63
|
+
}, onPointerEnter: () => setHovered(true), onPointerLeave: () => setHovered(false), children: [_jsxs("button", { type: "button", style: cardHeaderStyle, "aria-expanded": open, onClick: () => setOpen(!open), children: [_jsxs("span", { style: cardHeadTextStyle, children: [_jsx("span", { style: cardNameStyle, children: t("cardTitle") }), _jsx("span", { style: cardDescriptionStyle, children: t("cardDescription") })] }), _jsx(CardChevron, { open: open })] }), open ? (_jsxs("div", { style: cardBodyStyle, children: [state.notice === "" ? null : (_jsxs("div", { style: banner, role: "status", children: [t("notice"), ": ", state.notice] })), state.versionState === "ok" ? null : (_jsx("div", { style: state.versionState === "major-mismatch"
|
|
64
|
+
? { ...banner, borderColor: "var(--dsw-alias-state-error-primary)" }
|
|
65
|
+
: banner, role: state.versionState === "major-mismatch" ? "alert" : "status", children: t(state.versionState === "major-mismatch"
|
|
66
|
+
? "versionMismatchMajor"
|
|
67
|
+
: "versionMismatchMinor") })), _jsx("div", { style: sectionTitle, children: t("workspacesTitle") }), state.workspaces.length === 0 ? (_jsx("p", { style: hint, children: t("none") })) : (state.workspaces.map((workspace) => (_jsx(WorkspaceSection, { t: t, workspace: workspace, containers: state.containers.filter((container) => container.workspaceSlug === workspace.workspaceSlug), images: state.images, volumes: state.volumes, secrets: state.secrets, busy: state.busy, defaultImage: state.defaultImage, projectsRoot: state.projectsRoot, directoryPicker: state.directoryPicker, onRemove: props.remove, onRemoveWorkspace: props.removeWorkspace, onRecreate: props.recreate, onCreate: props.createContainer, onStartContainer: props.startContainer, onAddContainerMount: props.addContainerMount, onRemoveContainerMount: props.removeContainerMount, onUpdateContainerMount: props.updateContainerMount, onSetContainerPaths: props.setContainerPaths, onAddContainerSecret: props.addContainerSecret, onRemoveContainerSecret: props.removeContainerSecret }, workspace.workspaceSlug)))), _jsxs("div", { style: {
|
|
47
68
|
display: "flex",
|
|
48
69
|
flexWrap: "wrap",
|
|
49
70
|
alignItems: "center",
|
|
@@ -75,5 +96,20 @@ export function ContainerCard(props) {
|
|
|
75
96
|
fontSize: "13px",
|
|
76
97
|
color: "var(--dsw-alias-label-secondary)",
|
|
77
98
|
minWidth: "110px",
|
|
78
|
-
}, children: t("projectsRoot") }), _jsx(Input, { id: projectsRootId, value: state.projectsRoot, disabled: true, style: { width: "200px" } })] }), _jsx(ConfigField, { t: t, label: t("socketsRoot"), value: state.socketsRootDraft, current: state.socketsRoot, writable: state.writable, onChange: props.editSocketsRoot, onSave: props.saveSocketsRoot, onDiscard: props.discardSocketsRoot }),
|
|
99
|
+
}, children: t("projectsRoot") }), _jsx(Input, { id: projectsRootId, value: state.projectsRoot, disabled: true, style: { width: "200px" } })] }), _jsx(ConfigField, { t: t, label: t("socketsRoot"), value: state.socketsRootDraft, current: state.socketsRoot, writable: state.writable, onChange: props.editSocketsRoot, onSave: props.saveSocketsRoot, onDiscard: props.discardSocketsRoot }), _jsxs("div", { style: {
|
|
100
|
+
display: "flex",
|
|
101
|
+
flexWrap: "wrap",
|
|
102
|
+
alignItems: "center",
|
|
103
|
+
gap: "8px",
|
|
104
|
+
}, children: [_jsx("div", { style: { ...sectionTitle, flex: 1 }, children: t("versionsTitle") }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => void copyVersions(), children: copyState === "copied"
|
|
105
|
+
? t("copied")
|
|
106
|
+
: copyState === "failed"
|
|
107
|
+
? t("copyFailed")
|
|
108
|
+
: t("copyVersions") })] }), _jsxs("div", { style: {
|
|
109
|
+
...hint,
|
|
110
|
+
display: "flex",
|
|
111
|
+
flexDirection: "column",
|
|
112
|
+
gap: "2px",
|
|
113
|
+
margin: 0,
|
|
114
|
+
}, children: [_jsxs("span", { children: [_jsx("span", { style: versionLabel, children: t("dsh") }), state.dshVersion ? (` ${state.dshVersion}`) : (_jsxs(_Fragment, { children: [" ", _jsx("span", { style: { color: "var(--dsw-alias-state-error-primary)" }, children: t("versionUnknown") })] }))] }), _jsxs("span", { children: [_jsx("span", { style: versionLabel, children: t("cardTitle") }), state.version ? ` ${state.version}` : "", state.commit ? ` · ${state.commit}` : ""] }), _jsxs("span", { children: [_jsx("span", { style: versionLabel, children: t("orchestrator") }), state.orchestratorVersion ? (` ${state.orchestratorVersion}`) : (_jsxs(_Fragment, { children: [" ", _jsx("span", { style: { color: "var(--dsw-alias-state-error-primary)" }, children: t("versionUnknown") })] }))] })] }), _jsx("div", { style: footerRow, children: _jsx(Button, { variant: "outline", size: "sm", disabled: state.busy, onClick: props.reload, children: state.busy ? t("busy") : t("reload") }) })] })) : null] }));
|
|
79
115
|
}
|
|
@@ -85,6 +85,9 @@ export interface WorkspaceView {
|
|
|
85
85
|
export interface CardSnapshot {
|
|
86
86
|
version: string;
|
|
87
87
|
commit: string;
|
|
88
|
+
dshVersion: string;
|
|
89
|
+
orchestratorVersion: string;
|
|
90
|
+
versionState: "ok" | "minor-mismatch" | "major-mismatch";
|
|
88
91
|
projectsRoot: string;
|
|
89
92
|
workspaces: readonly WorkspaceView[];
|
|
90
93
|
containers: readonly ContainerView[];
|
|
@@ -32,6 +32,9 @@ export interface CardState {
|
|
|
32
32
|
notice: string;
|
|
33
33
|
version: string;
|
|
34
34
|
commit: string;
|
|
35
|
+
dshVersion: string;
|
|
36
|
+
orchestratorVersion: string;
|
|
37
|
+
versionState: "ok" | "minor-mismatch" | "major-mismatch";
|
|
35
38
|
defaultImage: string;
|
|
36
39
|
defaultImageDraft: string;
|
|
37
40
|
socketsRoot: string;
|
|
@@ -7,6 +7,9 @@ export const CONTAINER_NS = "podman";
|
|
|
7
7
|
const EMPTY_SNAPSHOT = {
|
|
8
8
|
version: "",
|
|
9
9
|
commit: "",
|
|
10
|
+
dshVersion: "",
|
|
11
|
+
orchestratorVersion: "",
|
|
12
|
+
versionState: "ok",
|
|
10
13
|
projectsRoot: "",
|
|
11
14
|
workspaces: [],
|
|
12
15
|
containers: [],
|
|
@@ -49,6 +52,9 @@ export class ContainerCardController {
|
|
|
49
52
|
notice: this.notice,
|
|
50
53
|
version: this.snapshot.version,
|
|
51
54
|
commit: this.snapshot.commit,
|
|
55
|
+
dshVersion: this.snapshot.dshVersion,
|
|
56
|
+
orchestratorVersion: this.snapshot.orchestratorVersion,
|
|
57
|
+
versionState: this.snapshot.versionState,
|
|
52
58
|
defaultImage: value?.defaultImage ?? "",
|
|
53
59
|
defaultImageDraft: this.draft("defaultImage", value?.defaultImage ?? ""),
|
|
54
60
|
socketsRoot: value?.socketsRoot ?? "",
|
package/dist/client/index.js
CHANGED
|
@@ -3010,6 +3010,7 @@ function WorkspaceSection(props) {
|
|
|
3010
3010
|
var import_dsh_client_ui_primitives11 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
3011
3011
|
var import_react11 = require("react");
|
|
3012
3012
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
3013
|
+
var versionLabel = { fontWeight: 600 };
|
|
3013
3014
|
function ContainerCard(props) {
|
|
3014
3015
|
const { t } = props;
|
|
3015
3016
|
const state = props.useContainerCard((snapshot) => snapshot);
|
|
@@ -3021,7 +3022,21 @@ function ContainerCard(props) {
|
|
|
3021
3022
|
const [packages, setPackages] = (0, import_react11.useState)([]);
|
|
3022
3023
|
const [defaultOpen, setDefaultOpen] = (0, import_react11.useState)(false);
|
|
3023
3024
|
const [defaultImage, setDefaultImage] = (0, import_react11.useState)(state.defaultImage);
|
|
3025
|
+
const [copyState, setCopyState] = (0, import_react11.useState)(
|
|
3026
|
+
"idle"
|
|
3027
|
+
);
|
|
3028
|
+
const copyResetTimer = (0, import_react11.useRef)();
|
|
3024
3029
|
const projectsRootId = (0, import_react11.useId)();
|
|
3030
|
+
const copyVersions = async () => {
|
|
3031
|
+
const lines = [
|
|
3032
|
+
`${t("dsh")} ${state.dshVersion || t("versionUnknown")}`,
|
|
3033
|
+
`${t("cardTitle")} ${state.version}${state.commit ? ` \xB7 ${state.commit}` : ""}`,
|
|
3034
|
+
`${t("orchestrator")} ${state.orchestratorVersion || t("versionUnknown")}`
|
|
3035
|
+
];
|
|
3036
|
+
setCopyState(await (0, import_dsh_client_ui_primitives11.writeClipboard)(lines.join("\n")) ? "copied" : "failed");
|
|
3037
|
+
if (copyResetTimer.current !== void 0) clearTimeout(copyResetTimer.current);
|
|
3038
|
+
copyResetTimer.current = setTimeout(() => setCopyState("idle"), 1500);
|
|
3039
|
+
};
|
|
3025
3040
|
(0, import_react11.useEffect)(() => {
|
|
3026
3041
|
props.reload();
|
|
3027
3042
|
}, []);
|
|
@@ -3054,11 +3069,7 @@ function ContainerCard(props) {
|
|
|
3054
3069
|
onClick: () => setOpen(!open),
|
|
3055
3070
|
children: [
|
|
3056
3071
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { style: cardHeadTextStyle, children: [
|
|
3057
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.
|
|
3058
|
-
t("cardTitle"),
|
|
3059
|
-
state.version ? ` ${state.version}` : "",
|
|
3060
|
-
state.commit ? ` \xB7 ${state.commit}` : ""
|
|
3061
|
-
] }),
|
|
3072
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: cardNameStyle, children: t("cardTitle") }),
|
|
3062
3073
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: cardDescriptionStyle, children: t("cardDescription") })
|
|
3063
3074
|
] }),
|
|
3064
3075
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CardChevron, { open })
|
|
@@ -3071,6 +3082,16 @@ function ContainerCard(props) {
|
|
|
3071
3082
|
": ",
|
|
3072
3083
|
state.notice
|
|
3073
3084
|
] }),
|
|
3085
|
+
state.versionState === "ok" ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3086
|
+
"div",
|
|
3087
|
+
{
|
|
3088
|
+
style: state.versionState === "major-mismatch" ? { ...banner, borderColor: "var(--dsw-alias-state-error-primary)" } : banner,
|
|
3089
|
+
role: state.versionState === "major-mismatch" ? "alert" : "status",
|
|
3090
|
+
children: t(
|
|
3091
|
+
state.versionState === "major-mismatch" ? "versionMismatchMajor" : "versionMismatchMinor"
|
|
3092
|
+
)
|
|
3093
|
+
}
|
|
3094
|
+
),
|
|
3074
3095
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: sectionTitle, children: t("workspacesTitle") }),
|
|
3075
3096
|
state.workspaces.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: hint, children: t("none") }) : state.workspaces.map((workspace) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3076
3097
|
WorkspaceSection,
|
|
@@ -3330,6 +3351,74 @@ function ContainerCard(props) {
|
|
|
3330
3351
|
onDiscard: props.discardSocketsRoot
|
|
3331
3352
|
}
|
|
3332
3353
|
),
|
|
3354
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
3355
|
+
"div",
|
|
3356
|
+
{
|
|
3357
|
+
style: {
|
|
3358
|
+
display: "flex",
|
|
3359
|
+
flexWrap: "wrap",
|
|
3360
|
+
alignItems: "center",
|
|
3361
|
+
gap: "8px"
|
|
3362
|
+
},
|
|
3363
|
+
children: [
|
|
3364
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { ...sectionTitle, flex: 1 }, children: t("versionsTitle") }),
|
|
3365
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3366
|
+
import_dsh_client_ui_primitives11.Button,
|
|
3367
|
+
{
|
|
3368
|
+
variant: "outline",
|
|
3369
|
+
size: "sm",
|
|
3370
|
+
onClick: () => void copyVersions(),
|
|
3371
|
+
children: copyState === "copied" ? t("copied") : copyState === "failed" ? t("copyFailed") : t("copyVersions")
|
|
3372
|
+
}
|
|
3373
|
+
)
|
|
3374
|
+
]
|
|
3375
|
+
}
|
|
3376
|
+
),
|
|
3377
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
3378
|
+
"div",
|
|
3379
|
+
{
|
|
3380
|
+
style: {
|
|
3381
|
+
...hint,
|
|
3382
|
+
display: "flex",
|
|
3383
|
+
flexDirection: "column",
|
|
3384
|
+
gap: "2px",
|
|
3385
|
+
margin: 0
|
|
3386
|
+
},
|
|
3387
|
+
children: [
|
|
3388
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
|
|
3389
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: versionLabel, children: t("dsh") }),
|
|
3390
|
+
state.dshVersion ? ` ${state.dshVersion}` : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
3391
|
+
" ",
|
|
3392
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3393
|
+
"span",
|
|
3394
|
+
{
|
|
3395
|
+
style: { color: "var(--dsw-alias-state-error-primary)" },
|
|
3396
|
+
children: t("versionUnknown")
|
|
3397
|
+
}
|
|
3398
|
+
)
|
|
3399
|
+
] })
|
|
3400
|
+
] }),
|
|
3401
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
|
|
3402
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: versionLabel, children: t("cardTitle") }),
|
|
3403
|
+
state.version ? ` ${state.version}` : "",
|
|
3404
|
+
state.commit ? ` \xB7 ${state.commit}` : ""
|
|
3405
|
+
] }),
|
|
3406
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
|
|
3407
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: versionLabel, children: t("orchestrator") }),
|
|
3408
|
+
state.orchestratorVersion ? ` ${state.orchestratorVersion}` : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
3409
|
+
" ",
|
|
3410
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3411
|
+
"span",
|
|
3412
|
+
{
|
|
3413
|
+
style: { color: "var(--dsw-alias-state-error-primary)" },
|
|
3414
|
+
children: t("versionUnknown")
|
|
3415
|
+
}
|
|
3416
|
+
)
|
|
3417
|
+
] })
|
|
3418
|
+
] })
|
|
3419
|
+
]
|
|
3420
|
+
}
|
|
3421
|
+
),
|
|
3333
3422
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: footerRow, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3334
3423
|
import_dsh_client_ui_primitives11.Button,
|
|
3335
3424
|
{
|
|
@@ -3781,6 +3870,9 @@ var CONTAINER_NS = "podman";
|
|
|
3781
3870
|
var EMPTY_SNAPSHOT = {
|
|
3782
3871
|
version: "",
|
|
3783
3872
|
commit: "",
|
|
3873
|
+
dshVersion: "",
|
|
3874
|
+
orchestratorVersion: "",
|
|
3875
|
+
versionState: "ok",
|
|
3784
3876
|
projectsRoot: "",
|
|
3785
3877
|
workspaces: [],
|
|
3786
3878
|
containers: [],
|
|
@@ -3819,6 +3911,9 @@ var ContainerCardController = class {
|
|
|
3819
3911
|
notice: this.notice,
|
|
3820
3912
|
version: this.snapshot.version,
|
|
3821
3913
|
commit: this.snapshot.commit,
|
|
3914
|
+
dshVersion: this.snapshot.dshVersion,
|
|
3915
|
+
orchestratorVersion: this.snapshot.orchestratorVersion,
|
|
3916
|
+
versionState: this.snapshot.versionState,
|
|
3822
3917
|
defaultImage: value?.defaultImage ?? "",
|
|
3823
3918
|
defaultImageDraft: this.draft("defaultImage", value?.defaultImage ?? ""),
|
|
3824
3919
|
socketsRoot: value?.socketsRoot ?? "",
|
|
@@ -3983,6 +4078,15 @@ var NS = "podman";
|
|
|
3983
4078
|
var en = {
|
|
3984
4079
|
cardTitle: "dsh-podman",
|
|
3985
4080
|
cardDescription: "Manage the guest containers and their images.",
|
|
4081
|
+
versionMismatchMajor: "This plugin's major version differs from the orchestrator's; update the dsh image or the orchestrator image so both match.",
|
|
4082
|
+
versionMismatchMinor: "This plugin and the orchestrator have compatible but different versions; consider updating both images.",
|
|
4083
|
+
versionsTitle: "Versions",
|
|
4084
|
+
orchestrator: "dsh-podman-orchestrator",
|
|
4085
|
+
versionUnknown: "unknown",
|
|
4086
|
+
dsh: "dsh",
|
|
4087
|
+
copyVersions: "Copy versions",
|
|
4088
|
+
copied: "Copied",
|
|
4089
|
+
copyFailed: "Copy failed",
|
|
3986
4090
|
reload: "Reload this view",
|
|
3987
4091
|
reloading: "Reloading\u2026",
|
|
3988
4092
|
notice: "Notice",
|
|
@@ -4166,6 +4270,15 @@ var en = {
|
|
|
4166
4270
|
var zh = {
|
|
4167
4271
|
cardTitle: "dsh-podman",
|
|
4168
4272
|
cardDescription: "\u7BA1\u7406\u5DE5\u4F5C\u533A\u5BB9\u5668\u53CA\u5176\u955C\u50CF\u3002",
|
|
4273
|
+
versionMismatchMajor: "\u6B64\u63D2\u4EF6\u4E0E orchestrator \u7684\u4E3B\u7248\u672C\u4E0D\u540C\uFF1B\u8BF7\u66F4\u65B0 dsh \u955C\u50CF\u6216 orchestrator \u955C\u50CF\uFF0C\u4F7F\u4E24\u8005\u4E00\u81F4\u3002",
|
|
4274
|
+
versionMismatchMinor: "\u6B64\u63D2\u4EF6\u4E0E orchestrator \u7684\u7248\u672C\u4E0D\u540C\u4F46\u517C\u5BB9\uFF1B\u5EFA\u8BAE\u540C\u65F6\u66F4\u65B0\u4E24\u4E2A\u955C\u50CF\u3002",
|
|
4275
|
+
versionsTitle: "\u7248\u672C",
|
|
4276
|
+
orchestrator: "dsh-podman-orchestrator",
|
|
4277
|
+
versionUnknown: "\u672A\u77E5",
|
|
4278
|
+
dsh: "dsh",
|
|
4279
|
+
copyVersions: "\u590D\u5236\u7248\u672C",
|
|
4280
|
+
copied: "\u5DF2\u590D\u5236",
|
|
4281
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
4169
4282
|
reload: "\u5237\u65B0\u6B64\u89C6\u56FE",
|
|
4170
4283
|
reloading: "\u5237\u65B0\u4E2D\u2026",
|
|
4171
4284
|
notice: "\u63D0\u793A",
|
package/dist/client/locales.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const NS = "podman";
|
|
2
|
-
export type ContainerPluginKey = "cardTitle" | "cardDescription" | "reload" | "reloading" | "notice" | "configTitle" | "defaultImage" | "socketsRoot" | "projectsRoot" | "defaultImageHint" | "save" | "discard" | "workspacesTitle" | "noContainers" | "createContainer" | "containersTitle" | "containerName" | "workspace" | "image" | "status" | "created" | "projects" | "remove" | "removePod" | "confirmRemovePod" | "recreate" | "recreateWithImage" | "imagesTitle" | "imageId" | "baseImage" | "imageTag" | "builtAt" | "packages" | "volumesTitle" | "createVolume" | "removeVolume" | "volumeName" | "secretsTitle" | "createSecret" | "secretName" | "secretCharset" | "selectImage" | "setSecret" | "removeSecret" | "removeImage" | "rebuildImage" | "rebuildAllImages" | "buildImage" | "build" | "buildImageTitle" | "pullImage" | "setDefaultImage" | "setDefaultImageTitle" | "baseImagesTitle" | "customImagesTitle" | "primitive" | "packageManager" | "parent" | "cancel" | "removeTag" | "env" | "secretEnv" | "envTitle" | "envKey" | "envValue" | "addEnv" | "removeEnv" | "secretLength" | "containerSecretsTitle" | "attachSecret" | "secretEnvName" | "detachSecret" | "none" | "unavailable" | "busy" | "confirmTitle" | "confirm" | "confirmRemoveContainer" | "confirmRecreate" | "confirmRemoveImage" | "confirmRebuildImage" | "confirmRebuildAllImages" | "confirmRemoveVolume" | "confirmSetSecret" | "confirmRemoveSecret" | "confirmDetachSecret" | "mountsTitle" | "addMount" | "mountKind" | "mountProject" | "mountProjectPath" | "browse" | "browseTitle" | "browseSelect" | "browseLoading" | "browseError" | "browseUnavailable" | "browseHidden" | "browseTruncated" | "browseOutsideRoot" | "mountDestination" | "pathsTitle" | "noPaths" | "editPaths" | "addPath" | "add" | "moveUp" | "moveDown" | "apply" | "dragToReorder" | "remountWaiting" | "remountApprove" | "remountReject" | "mountVolume" | "mountSecret" | "mountMode" | "readOnly" | "readWrite" | "remountReadOnly" | "remountReadWrite" | "confirmRemountReadOnly" | "confirmRemountReadWrite" | "confirmRemoveMount" | "cachesTitle" | "noCaches" | "cacheUsage" | "cacheKeepLatest" | "confirmCacheKeepLatest" | "cacheRemoveAll" | "confirmCacheRemoveAll" | "addContainer" | "invalidContainerName" | "containerNameTaken" | "invalidName" | "invalidImageId" | "terminalSignal" | "terminalExitCode" | "terminalRunning" | "terminalFailed" | "terminalDone" | "terminalNoOutput" | "terminalCollapseAria" | "terminalExpandAria" | "terminalExpandRest" | "toolTitle_container_bash" | "toolTitle_container_exec" | "toolTitle_container_read" | "toolTitle_container_write" | "toolTitle_container_edit" | "toolTitle_container_glob" | "toolTitle_container_grep" | "toolTitle_container_list" | "toolTitle_container_start" | "toolTitle_container_recreate" | "toolTitle_container_remove" | "toolTitle_container_mount_list" | "toolTitle_container_mount_add" | "toolTitle_container_mount_remove" | "toolTitle_container_mount_update" | "toolTitle_container_path_set" | "toolTitle_container_path_add" | "toolTitle_container_path_remove" | "toolTitle_container_secret_add" | "toolTitle_container_secret_remove" | "toolTitle_image_list" | "toolTitle_image_get" | "toolTitle_image_build" | "toolTitle_image_rebuild" | "toolTitle_image_rebuild_all" | "toolTitle_image_remove" | "toolTitle_volume_list" | "toolTitle_volume_create" | "toolTitle_volume_remove" | "toolTitle_secret_list" | "toolTitle_secret_create" | "toolTitle_secret_remove" | "toolTitle_daemon_start" | "toolTitle_daemon_list" | "toolTitle_daemon_logs" | "toolTitle_daemon_restart" | "toolTitle_daemon_stop";
|
|
2
|
+
export type ContainerPluginKey = "cardTitle" | "cardDescription" | "versionMismatchMajor" | "versionMismatchMinor" | "versionsTitle" | "orchestrator" | "versionUnknown" | "dsh" | "copyVersions" | "copied" | "copyFailed" | "reload" | "reloading" | "notice" | "configTitle" | "defaultImage" | "socketsRoot" | "projectsRoot" | "defaultImageHint" | "save" | "discard" | "workspacesTitle" | "noContainers" | "createContainer" | "containersTitle" | "containerName" | "workspace" | "image" | "status" | "created" | "projects" | "remove" | "removePod" | "confirmRemovePod" | "recreate" | "recreateWithImage" | "imagesTitle" | "imageId" | "baseImage" | "imageTag" | "builtAt" | "packages" | "volumesTitle" | "createVolume" | "removeVolume" | "volumeName" | "secretsTitle" | "createSecret" | "secretName" | "secretCharset" | "selectImage" | "setSecret" | "removeSecret" | "removeImage" | "rebuildImage" | "rebuildAllImages" | "buildImage" | "build" | "buildImageTitle" | "pullImage" | "setDefaultImage" | "setDefaultImageTitle" | "baseImagesTitle" | "customImagesTitle" | "primitive" | "packageManager" | "parent" | "cancel" | "removeTag" | "env" | "secretEnv" | "envTitle" | "envKey" | "envValue" | "addEnv" | "removeEnv" | "secretLength" | "containerSecretsTitle" | "attachSecret" | "secretEnvName" | "detachSecret" | "none" | "unavailable" | "busy" | "confirmTitle" | "confirm" | "confirmRemoveContainer" | "confirmRecreate" | "confirmRemoveImage" | "confirmRebuildImage" | "confirmRebuildAllImages" | "confirmRemoveVolume" | "confirmSetSecret" | "confirmRemoveSecret" | "confirmDetachSecret" | "mountsTitle" | "addMount" | "mountKind" | "mountProject" | "mountProjectPath" | "browse" | "browseTitle" | "browseSelect" | "browseLoading" | "browseError" | "browseUnavailable" | "browseHidden" | "browseTruncated" | "browseOutsideRoot" | "mountDestination" | "pathsTitle" | "noPaths" | "editPaths" | "addPath" | "add" | "moveUp" | "moveDown" | "apply" | "dragToReorder" | "remountWaiting" | "remountApprove" | "remountReject" | "mountVolume" | "mountSecret" | "mountMode" | "readOnly" | "readWrite" | "remountReadOnly" | "remountReadWrite" | "confirmRemountReadOnly" | "confirmRemountReadWrite" | "confirmRemoveMount" | "cachesTitle" | "noCaches" | "cacheUsage" | "cacheKeepLatest" | "confirmCacheKeepLatest" | "cacheRemoveAll" | "confirmCacheRemoveAll" | "addContainer" | "invalidContainerName" | "containerNameTaken" | "invalidName" | "invalidImageId" | "terminalSignal" | "terminalExitCode" | "terminalRunning" | "terminalFailed" | "terminalDone" | "terminalNoOutput" | "terminalCollapseAria" | "terminalExpandAria" | "terminalExpandRest" | "toolTitle_container_bash" | "toolTitle_container_exec" | "toolTitle_container_read" | "toolTitle_container_write" | "toolTitle_container_edit" | "toolTitle_container_glob" | "toolTitle_container_grep" | "toolTitle_container_list" | "toolTitle_container_start" | "toolTitle_container_recreate" | "toolTitle_container_remove" | "toolTitle_container_mount_list" | "toolTitle_container_mount_add" | "toolTitle_container_mount_remove" | "toolTitle_container_mount_update" | "toolTitle_container_path_set" | "toolTitle_container_path_add" | "toolTitle_container_path_remove" | "toolTitle_container_secret_add" | "toolTitle_container_secret_remove" | "toolTitle_image_list" | "toolTitle_image_get" | "toolTitle_image_build" | "toolTitle_image_rebuild" | "toolTitle_image_rebuild_all" | "toolTitle_image_remove" | "toolTitle_volume_list" | "toolTitle_volume_create" | "toolTitle_volume_remove" | "toolTitle_secret_list" | "toolTitle_secret_create" | "toolTitle_secret_remove" | "toolTitle_daemon_start" | "toolTitle_daemon_list" | "toolTitle_daemon_logs" | "toolTitle_daemon_restart" | "toolTitle_daemon_stop";
|
|
3
3
|
export declare const en: Record<ContainerPluginKey, string>;
|
|
4
4
|
export declare const zh: Record<ContainerPluginKey, string>;
|
|
5
5
|
declare module "@deepseek-ai/dsh-client-ui-slots" {
|
package/dist/client/locales.js
CHANGED
|
@@ -5,6 +5,15 @@ export const NS = "podman";
|
|
|
5
5
|
export const en = {
|
|
6
6
|
cardTitle: "dsh-podman",
|
|
7
7
|
cardDescription: "Manage the guest containers and their images.",
|
|
8
|
+
versionMismatchMajor: "This plugin's major version differs from the orchestrator's; update the dsh image or the orchestrator image so both match.",
|
|
9
|
+
versionMismatchMinor: "This plugin and the orchestrator have compatible but different versions; consider updating both images.",
|
|
10
|
+
versionsTitle: "Versions",
|
|
11
|
+
orchestrator: "dsh-podman-orchestrator",
|
|
12
|
+
versionUnknown: "unknown",
|
|
13
|
+
dsh: "dsh",
|
|
14
|
+
copyVersions: "Copy versions",
|
|
15
|
+
copied: "Copied",
|
|
16
|
+
copyFailed: "Copy failed",
|
|
8
17
|
reload: "Reload this view",
|
|
9
18
|
reloading: "Reloading…",
|
|
10
19
|
notice: "Notice",
|
|
@@ -188,6 +197,15 @@ export const en = {
|
|
|
188
197
|
export const zh = {
|
|
189
198
|
cardTitle: "dsh-podman",
|
|
190
199
|
cardDescription: "管理工作区容器及其镜像。",
|
|
200
|
+
versionMismatchMajor: "此插件与 orchestrator 的主版本不同;请更新 dsh 镜像或 orchestrator 镜像,使两者一致。",
|
|
201
|
+
versionMismatchMinor: "此插件与 orchestrator 的版本不同但兼容;建议同时更新两个镜像。",
|
|
202
|
+
versionsTitle: "版本",
|
|
203
|
+
orchestrator: "dsh-podman-orchestrator",
|
|
204
|
+
versionUnknown: "未知",
|
|
205
|
+
dsh: "dsh",
|
|
206
|
+
copyVersions: "复制版本",
|
|
207
|
+
copied: "已复制",
|
|
208
|
+
copyFailed: "复制失败",
|
|
191
209
|
reload: "刷新此视图",
|
|
192
210
|
reloading: "刷新中…",
|
|
193
211
|
notice: "提示",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function dshVersion(): string;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
// The harness version this plugin runs inside. Every `@deepseek-ai/dsh-*`
|
|
8
|
+
// package carries the release version, and the dsh app package is linked into
|
|
9
|
+
// the profile's healed node_modules, so it is resolvable from the plugin even
|
|
10
|
+
// though it is not a dependency of it. "" when it cannot be resolved, which the
|
|
11
|
+
// card renders as unknown.
|
|
12
|
+
let cached;
|
|
13
|
+
export function dshVersion() {
|
|
14
|
+
cached ??= resolveDshVersion() ?? "";
|
|
15
|
+
return cached;
|
|
16
|
+
}
|
|
17
|
+
// resolveDshVersion walks the module search paths the way the harness resolves
|
|
18
|
+
// its own bundles, so a package that does not export ./package.json still
|
|
19
|
+
// resolves.
|
|
20
|
+
function resolveDshVersion() {
|
|
21
|
+
let searchPaths;
|
|
22
|
+
try {
|
|
23
|
+
searchPaths = createRequire(import.meta.url).resolve.paths("@deepseek-ai/dsh") ?? [];
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
for (const searchPath of searchPaths) {
|
|
29
|
+
if (searchPath === null)
|
|
30
|
+
continue;
|
|
31
|
+
const manifest = join(searchPath, "@deepseek-ai/dsh", "package.json");
|
|
32
|
+
if (!existsSync(manifest))
|
|
33
|
+
continue;
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(readFileSync(manifest, "utf8"));
|
|
36
|
+
if (typeof parsed.version === "string" && parsed.version !== "") {
|
|
37
|
+
return parsed.version;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// A malformed manifest is not a version source; keep looking.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.2.0-rc.
|
|
2
|
-
export declare const GIT_COMMIT = "
|
|
1
|
+
export declare const VERSION = "0.2.0-rc.4";
|
|
2
|
+
export declare const GIT_COMMIT = "fce200f";
|
|
@@ -7,6 +7,7 @@ package dshctl.v1;
|
|
|
7
7
|
option go_package = "github.com/Exagone313/dsh-podman/internal/genproto/dshctl/v1;dshctlv1";
|
|
8
8
|
|
|
9
9
|
service OrchestratorControl {
|
|
10
|
+
rpc GetVersion(GetVersionRequest) returns (GetVersionResponse);
|
|
10
11
|
rpc ListProjects(ListProjectsRequest) returns (ListProjectsResponse);
|
|
11
12
|
rpc CreateWorkspace(CreateWorkspaceRequest) returns (Workspace);
|
|
12
13
|
rpc DescribeWorkspace(DescribeWorkspaceRequest) returns (Workspace);
|
|
@@ -41,6 +42,8 @@ service OrchestratorControl {
|
|
|
41
42
|
rpc RemoveContainerSecret(RemoveContainerSecretRequest) returns (Container);
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
message GetVersionRequest {}
|
|
46
|
+
message GetVersionResponse { string version = 1; string commit = 2; }
|
|
44
47
|
message ListProjectsRequest {}
|
|
45
48
|
message ListProjectsResponse { repeated Project projects = 1; }
|
|
46
49
|
message Project { string name = 1; string host_path = 2; }
|
|
@@ -18,6 +18,8 @@ test("refresh on install publishes containers, images and workspaces", async ()
|
|
|
18
18
|
setConfig: () => { },
|
|
19
19
|
async control(method, request) {
|
|
20
20
|
calls.push([method, request]);
|
|
21
|
+
if (method === "getVersion")
|
|
22
|
+
return { version: "0.2.0", commit: "abc" };
|
|
21
23
|
if (method === "listContainers") {
|
|
22
24
|
return { containers: [{ containerName: "c1", workspaceSlug: "w1" }] };
|
|
23
25
|
}
|
|
@@ -38,7 +40,7 @@ test("refresh on install publishes containers, images and workspaces", async ()
|
|
|
38
40
|
};
|
|
39
41
|
await installCardCommandDriver(fakeContext(scope), resolver);
|
|
40
42
|
await scope.update({}); // settle the queued async refresh
|
|
41
|
-
assert.deepEqual(calls.map(([method]) => method), ["listContainers", "listImages", "listWorkspaces", "listVolumes", "listSecrets", "listCaches"]);
|
|
43
|
+
assert.deepEqual(calls.map(([method]) => method), ["getVersion", "listContainers", "listImages", "listWorkspaces", "listVolumes", "listSecrets", "listCaches"]);
|
|
42
44
|
assert.equal(scope.value.containers.length, 1);
|
|
43
45
|
assert.equal(scope.value.images.length, 1);
|
|
44
46
|
const workspaces = scope.value.workspaces;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
// core returns the leading x.y.z of a version string, or "" when there is
|
|
5
|
+
// none. A release build is exactly the tag; a build from a later commit carries
|
|
6
|
+
// a git-describe suffix (`1.2.3-4-gabc123`), and a local build may be `dev`.
|
|
7
|
+
export function core(value) {
|
|
8
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(String(value ?? ""));
|
|
9
|
+
return match === null ? "" : `${match[1]}.${match[2]}.${match[3]}`;
|
|
10
|
+
}
|
|
11
|
+
// major returns the major component of a version's core, or -1 when the value
|
|
12
|
+
// has no parseable core.
|
|
13
|
+
export function major(value) {
|
|
14
|
+
const parsed = core(value);
|
|
15
|
+
return parsed === "" ? -1 : Number(parsed.slice(0, parsed.indexOf(".")));
|
|
16
|
+
}
|
|
17
|
+
// versionState mirrors the orchestrator's own compatibility rule, so the card
|
|
18
|
+
// reports exactly what the control plane enforces.
|
|
19
|
+
export function versionState(plugin, orchestrator) {
|
|
20
|
+
const orchestratorCore = core(orchestrator);
|
|
21
|
+
if (orchestratorCore === "")
|
|
22
|
+
return "ok";
|
|
23
|
+
const pluginCore = core(plugin);
|
|
24
|
+
if (pluginCore === "")
|
|
25
|
+
return "major-mismatch";
|
|
26
|
+
if (major(plugin) !== major(orchestrator))
|
|
27
|
+
return "major-mismatch";
|
|
28
|
+
return pluginCore === orchestratorCore ? "ok" : "minor-mismatch";
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|