@termix-ssh/plugin-sdk 1.0.19 → 1.0.21

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 (44) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/PLUGIN-GUIDE.md +8 -2
  3. package/dist/frontend.d.ts +7 -3
  4. package/dist/frontend.js +19 -3
  5. package/dist/host/{PluginsTab-YAGYRBIE.js → PluginsTab-HQGSDSHS.js} +31 -47
  6. package/dist/host/{chunk-MYBONEWZ.js → chunk-A5CULDWV.js} +18 -11
  7. package/dist/host/{chunk-2MU6WNSX.js → chunk-FGX27MAA.js} +1 -1
  8. package/dist/host/{chunk-LQKK2JJA.js → chunk-GTHINW3L.js} +1551 -1508
  9. package/dist/host/{chunk-7LEJ3HYF.js → chunk-JFQGI4OD.js} +5 -3
  10. package/dist/host/{chunk-446UEDSK.js → chunk-WWFRBU2E.js} +19 -3
  11. package/dist/host/core-classes.json +1 -1
  12. package/dist/host/{linked-server-OUF3POT7.js → linked-server-UDSFY3N7.js} +1 -1
  13. package/dist/host/{rbac-api-LZVENREG.js → rbac-api-AAAPMF52.js} +1 -1
  14. package/dist/host/testing-host.js +4 -4
  15. package/dist/host/types/src/ui/api/look-api.d.ts +2 -0
  16. package/dist/host/types/src/ui/api/plugins-api.d.ts +1 -1
  17. package/dist/host/types/src/ui/components/data-view.d.ts +7 -2
  18. package/dist/host/types/src/ui/components/panel-layout.d.ts +9 -2
  19. package/dist/host/types/src/ui/lib/app-return.d.ts +13 -0
  20. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +1 -1
  21. package/dist/host/types/src/ui/plugins/plugin-model.d.ts +1 -1
  22. package/dist/host/ui.js +67 -24
  23. package/dist/host/{use-permissions-BYCDS5PD.js → use-permissions-VAND5GEG.js} +1 -1
  24. package/dist/host-commands.d.ts +2 -6
  25. package/dist/host-commands.js +3 -6
  26. package/dist/host-scripts/docker.d.ts +137 -0
  27. package/dist/host-scripts/docker.js +345 -0
  28. package/dist/host-scripts/index.d.ts +15 -0
  29. package/dist/host-scripts/index.js +15 -0
  30. package/dist/host-scripts/journal.d.ts +45 -0
  31. package/dist/host-scripts/journal.js +194 -0
  32. package/dist/host-scripts/metrics.d.ts +134 -0
  33. package/dist/host-scripts/metrics.js +320 -0
  34. package/dist/host-scripts/openrc.d.ts +6 -0
  35. package/dist/host-scripts/openrc.js +90 -0
  36. package/dist/host-scripts/shell.d.ts +43 -0
  37. package/dist/host-scripts/shell.js +95 -0
  38. package/dist/host-scripts/systemd.d.ts +73 -0
  39. package/dist/host-scripts/systemd.js +320 -0
  40. package/dist/host-scripts/text.d.ts +23 -0
  41. package/dist/host-scripts/text.js +111 -0
  42. package/dist/manifest.d.ts +1 -1
  43. package/dist/manifest.js +1 -1
  44. package/package.json +5 -1
@@ -0,0 +1,137 @@
1
+ export type ContainerRuntime = "docker" | "podman";
2
+ export declare function normalizeContainerRuntime(value: unknown): ContainerRuntime;
3
+ export declare function getRuntimeLabel(runtime: ContainerRuntime): string;
4
+ /**
5
+ * Non-interactive SSH shells don't source ~/.zprofile or ~/.bash_profile,
6
+ * so PATH additions from installers like Homebrew or OrbStack are missing.
7
+ */
8
+ export declare const EXTRA_PATH_DIRS = "/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:\"$HOME/.orbstack/bin\"";
9
+ export declare function containerCommand(runtime: ContainerRuntime | undefined, args: string, isWindows?: boolean): string;
10
+ export declare const CONTAINER_ID_RE: RegExp;
11
+ export declare const DOCKER_TIMESTAMP_RE: RegExp;
12
+ export declare function isContainerId(value: unknown): value is string;
13
+ export declare const CONTAINER_ACTIONS: readonly ["start", "stop", "restart", "pause", "unpause"];
14
+ export type ContainerAction = (typeof CONTAINER_ACTIONS)[number];
15
+ export declare function listFormat(isWindows: boolean): string;
16
+ export declare function statsFormat(isWindows: boolean): string;
17
+ /** One row of `ps --format listFormat()`. */
18
+ export interface ContainerRow {
19
+ id: string;
20
+ name: string;
21
+ image: string;
22
+ status: string;
23
+ state: string;
24
+ ports: string;
25
+ created: string;
26
+ }
27
+ /** CLI arguments, without the runtime, for each container command. */
28
+ export declare const dockerArgs: {
29
+ version: () => string;
30
+ ping: () => string;
31
+ list: (all: boolean, isWindows?: boolean) => string;
32
+ inspect: (id: string) => string;
33
+ stats: (id: string, isWindows?: boolean) => string;
34
+ action: (action: ContainerAction, id: string) => string;
35
+ remove: (id: string, force: boolean) => string;
36
+ logs: (id: string, options?: {
37
+ tail?: number;
38
+ timestamps?: boolean;
39
+ since?: string;
40
+ until?: string;
41
+ }) => string;
42
+ states: () => string;
43
+ composeProjects: () => string;
44
+ };
45
+ /** The version from `docker --version` or `podman --version`. */
46
+ export declare function parseRuntimeVersion(output: string): string;
47
+ export type RuntimeProblem = "NOT_INSTALLED" | "DAEMON_NOT_RUNNING" | "PERMISSION_DENIED" | "DOCKER_ERROR";
48
+ /** Sorts a failed `docker ps` into a reason the UI can explain. */
49
+ export declare function classifyRuntimeError(message: string): RuntimeProblem;
50
+ /** Parses `ps --format listFormat()` output, skipping lines that are not JSON. */
51
+ export declare function parseContainerList(output: string): Record<string, unknown>[];
52
+ export interface ContainerStats {
53
+ cpu: string;
54
+ memoryUsed: string;
55
+ memoryLimit: string;
56
+ memoryPercent: string;
57
+ netInput: string;
58
+ netOutput: string;
59
+ blockRead: string;
60
+ blockWrite: string;
61
+ pids: string;
62
+ }
63
+ /** Parses one `stats --no-stream --format statsFormat()` line. Throws on bad JSON. */
64
+ export declare function parseContainerStats(output: string): ContainerStats;
65
+ export interface ContainerState {
66
+ /** Docker's own state word: running, exited, restarting, ... */
67
+ state: string;
68
+ /** Health from the status text, when the image declares a healthcheck. */
69
+ unhealthy: boolean;
70
+ }
71
+ export type DockerEventName = "exited" | "started" | "unhealthy" | "restarting";
72
+ export declare const PS_STATE_FORMAT = "'{\"name\":\"{{.Names}}\",\"state\":\"{{.State}}\",\"status\":\"{{.Status}}\"}'";
73
+ /** Parses `ps -a --format PS_STATE_FORMAT` output, one JSON object per line. */
74
+ export declare function parseContainerStates(output: string): Map<string, ContainerState>;
75
+ /**
76
+ * The events a pair of snapshots implies. A container missing from the
77
+ * previous snapshot counts as newly seen rather than started, so the first
78
+ * poll after a restart does not replay every running container.
79
+ */
80
+ export declare function diffContainerStates(previous: Map<string, ContainerState>, current: Map<string, ContainerState>): Array<{
81
+ container: string;
82
+ event: DockerEventName;
83
+ }>;
84
+ export interface ContainerCounts {
85
+ running: number;
86
+ stopped: number;
87
+ unhealthy: number;
88
+ total: number;
89
+ }
90
+ /** Counts from `ps -a --format PS_STATE_FORMAT` output. */
91
+ export declare function countContainers(output: string): ContainerCounts;
92
+ export declare const STACK_NAME_RE: RegExp;
93
+ export declare const STACK_SERVICE_RE: RegExp;
94
+ export declare const COMPOSE_FILES: string[];
95
+ export declare const STACK_ACTIONS: readonly ["deploy", "redeploy", "pull", "start", "stop", "restart", "pause", "unpause", "down", "destroy"];
96
+ export type StackAction = (typeof STACK_ACTIONS)[number];
97
+ /** Actions that can target one service of a stack. */
98
+ export declare const STACK_SERVICE_ACTIONS: StackAction[];
99
+ export type StackState = "running" | "partial" | "stopped" | "paused" | "not-deployed";
100
+ export interface StackSummary {
101
+ name: string;
102
+ managed: boolean;
103
+ state: StackState;
104
+ running: number;
105
+ total: number;
106
+ composeFile: string | null;
107
+ }
108
+ export interface StackService {
109
+ service: string;
110
+ name: string;
111
+ id: string;
112
+ image: string;
113
+ state: string;
114
+ status: string;
115
+ health: string;
116
+ ports: string;
117
+ }
118
+ /** `compose ls` and `compose ps --format json` print one object per line or one array. */
119
+ export declare function parseJsonLines(output: string): Record<string, unknown>[];
120
+ export declare function parseServices(output: string): StackService[];
121
+ /** Turns the `compose ls` status text ("running(2), exited(1)") into a state. */
122
+ export declare function parseStackStatus(status: string): {
123
+ state: StackState;
124
+ running: number;
125
+ total: number;
126
+ };
127
+ export declare function stackStateFromServices(services: StackService[]): {
128
+ state: StackState;
129
+ running: number;
130
+ total: number;
131
+ };
132
+ /** Stacks from `compose ls -a --format json`, for hosts with no stacks folder. */
133
+ export declare function parseComposeProjects(output: string): StackSummary[];
134
+ /** The compose commands one action runs, in order. */
135
+ export declare function composeArgsFor(action: StackAction, service: string | undefined, options: {
136
+ removeVolumes?: boolean;
137
+ }): string[];
@@ -0,0 +1,345 @@
1
+ /**
2
+ * Docker and Podman command builders and output parsers.
3
+ *
4
+ * Container ids and names are checked with CONTAINER_ID_RE before they go on
5
+ * a command line, so they need no quoting. Anything free form is quoted.
6
+ */
7
+ import { HostScriptError } from "./text.js";
8
+ export function normalizeContainerRuntime(value) {
9
+ return value === "podman" ? "podman" : "docker";
10
+ }
11
+ export function getRuntimeLabel(runtime) {
12
+ return runtime === "podman" ? "Podman" : "Docker";
13
+ }
14
+ /**
15
+ * Non-interactive SSH shells don't source ~/.zprofile or ~/.bash_profile,
16
+ * so PATH additions from installers like Homebrew or OrbStack are missing.
17
+ */
18
+ export const EXTRA_PATH_DIRS = '/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:"$HOME/.orbstack/bin"';
19
+ export function containerCommand(runtime, args, isWindows = false) {
20
+ const cli = `${normalizeContainerRuntime(runtime)} ${args}`;
21
+ // cmd and PowerShell don't understand the POSIX env prefix.
22
+ return isWindows ? cli : `PATH="${EXTRA_PATH_DIRS}:$PATH" ${cli}`;
23
+ }
24
+ export const CONTAINER_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/;
25
+ export const DOCKER_TIMESTAMP_RE = /^[0-9T:.Z+-]+$/;
26
+ export function isContainerId(value) {
27
+ return (typeof value === "string" &&
28
+ value.length <= 256 &&
29
+ CONTAINER_ID_RE.test(value));
30
+ }
31
+ function requireContainer(id) {
32
+ if (!isContainerId(id))
33
+ throw new HostScriptError("That is not a valid container id");
34
+ return id;
35
+ }
36
+ export const CONTAINER_ACTIONS = [
37
+ "start",
38
+ "stop",
39
+ "restart",
40
+ "pause",
41
+ "unpause",
42
+ ];
43
+ const LIST_FORMAT_UNIX = `'{"id":"{{.ID}}","name":"{{.Names}}","image":"{{.Image}}","status":"{{.Status}}","state":"{{.State}}","ports":"{{.Ports}}","created":"{{.CreatedAt}}"}' `;
44
+ const LIST_FORMAT_WINDOWS = `"{\\"id\\":\\"{{.ID}}\\",\\"name\\":\\"{{.Names}}\\",\\"image\\":\\"{{.Image}}\\",\\"status\\":\\"{{.Status}}\\",\\"state\\":\\"{{.State}}\\",\\"ports\\":\\"{{.Ports}}\\",\\"created\\":\\"{{.CreatedAt}}\\"}"`;
45
+ const STATS_FORMAT_UNIX = `'{"cpu":"{{.CPUPerc}}","memory":"{{.MemUsage}}","memoryPercent":"{{.MemPerc}}","netIO":"{{.NetIO}}","blockIO":"{{.BlockIO}}","pids":"{{.PIDs}}"}' `;
46
+ const STATS_FORMAT_WINDOWS = `"{\\"cpu\\":\\"{{.CPUPerc}}\\",\\"memory\\":\\"{{.MemUsage}}\\",\\"memoryPercent\\":\\"{{.MemPerc}}\\",\\"netIO\\":\\"{{.NetIO}}\\",\\"blockIO\\":\\"{{.BlockIO}}\\",\\"pids\\":\\"{{.PIDs}}\\"}"`;
47
+ export function listFormat(isWindows) {
48
+ return isWindows ? LIST_FORMAT_WINDOWS : LIST_FORMAT_UNIX;
49
+ }
50
+ export function statsFormat(isWindows) {
51
+ return isWindows ? STATS_FORMAT_WINDOWS : STATS_FORMAT_UNIX;
52
+ }
53
+ /** CLI arguments, without the runtime, for each container command. */
54
+ export const dockerArgs = {
55
+ version: () => "--version",
56
+ ping: () => "ps",
57
+ list: (all, isWindows = false) => `ps ${all ? "-a " : ""}--format ${listFormat(isWindows)}`,
58
+ inspect: (id) => `inspect ${requireContainer(id)}`,
59
+ stats: (id, isWindows = false) => `stats ${requireContainer(id)} --no-stream --format ${statsFormat(isWindows)}`,
60
+ action: (action, id) => {
61
+ if (!CONTAINER_ACTIONS.includes(action))
62
+ throw new HostScriptError("Unknown action");
63
+ return `${action} ${requireContainer(id)}`;
64
+ },
65
+ remove: (id, force) => `rm ${force ? "-f " : ""}${requireContainer(id)}`,
66
+ logs: (id, options = {}) => {
67
+ let args = `logs ${requireContainer(id)}`;
68
+ if (options.tail && options.tail > 0)
69
+ args += ` --tail ${Math.floor(options.tail)}`;
70
+ if (options.timestamps)
71
+ args += " --timestamps";
72
+ if (options.since && DOCKER_TIMESTAMP_RE.test(options.since)) {
73
+ args += ` --since ${options.since}`;
74
+ }
75
+ if (options.until && DOCKER_TIMESTAMP_RE.test(options.until)) {
76
+ args += ` --until ${options.until}`;
77
+ }
78
+ return `${args} 2>&1`;
79
+ },
80
+ states: () => `ps -a --format ${PS_STATE_FORMAT}`,
81
+ composeProjects: () => "compose ls -a --format json",
82
+ };
83
+ /** The version from `docker --version` or `podman --version`. */
84
+ export function parseRuntimeVersion(output) {
85
+ return output.match(/(?:Docker|podman) version ([^\s,]+)/i)?.[1] ?? "unknown";
86
+ }
87
+ /** Sorts a failed `docker ps` into a reason the UI can explain. */
88
+ export function classifyRuntimeError(message) {
89
+ if (/command not found|not found: (docker|podman)|No such file or directory/i.test(message)) {
90
+ return "NOT_INSTALLED";
91
+ }
92
+ if (message.includes("Cannot connect to the Docker daemon"))
93
+ return "DAEMON_NOT_RUNNING";
94
+ if (/permission denied/i.test(message))
95
+ return "PERMISSION_DENIED";
96
+ return "DOCKER_ERROR";
97
+ }
98
+ /** Parses `ps --format listFormat()` output, skipping lines that are not JSON. */
99
+ export function parseContainerList(output) {
100
+ return output
101
+ .split("\n")
102
+ .filter((line) => line.trim())
103
+ .flatMap((line) => {
104
+ try {
105
+ return [JSON.parse(line)];
106
+ }
107
+ catch {
108
+ return [];
109
+ }
110
+ });
111
+ }
112
+ /** Parses one `stats --no-stream --format statsFormat()` line. Throws on bad JSON. */
113
+ export function parseContainerStats(output) {
114
+ const raw = JSON.parse(output.trim());
115
+ const split = (value) => {
116
+ const [first, second] = (value ?? "").split(" / ");
117
+ return [first?.trim() || "0B", second?.trim() || "0B"];
118
+ };
119
+ const [memoryUsed, memoryLimit] = split(raw.memory);
120
+ const [netInput, netOutput] = split(raw.netIO);
121
+ const [blockRead, blockWrite] = split(raw.blockIO);
122
+ return {
123
+ cpu: raw.cpu,
124
+ memoryUsed,
125
+ memoryLimit,
126
+ memoryPercent: raw.memoryPercent,
127
+ netInput,
128
+ netOutput,
129
+ blockRead,
130
+ blockWrite,
131
+ pids: raw.pids,
132
+ };
133
+ }
134
+ export const PS_STATE_FORMAT = `'{"name":"{{.Names}}","state":"{{.State}}","status":"{{.Status}}"}'`;
135
+ /** Parses `ps -a --format PS_STATE_FORMAT` output, one JSON object per line. */
136
+ export function parseContainerStates(output) {
137
+ const states = new Map();
138
+ for (const line of output.split("\n")) {
139
+ const trimmed = line.trim();
140
+ if (!trimmed)
141
+ continue;
142
+ try {
143
+ const parsed = JSON.parse(trimmed);
144
+ if (!parsed.name)
145
+ continue;
146
+ states.set(parsed.name, {
147
+ state: (parsed.state ?? "").toLowerCase(),
148
+ unhealthy: /\(unhealthy\)/i.test(parsed.status ?? ""),
149
+ });
150
+ }
151
+ catch {
152
+ // A partial line is not worth failing the whole poll over.
153
+ }
154
+ }
155
+ return states;
156
+ }
157
+ /**
158
+ * The events a pair of snapshots implies. A container missing from the
159
+ * previous snapshot counts as newly seen rather than started, so the first
160
+ * poll after a restart does not replay every running container.
161
+ */
162
+ export function diffContainerStates(previous, current) {
163
+ const events = [];
164
+ for (const [name, now] of current) {
165
+ const before = previous.get(name);
166
+ if (!before)
167
+ continue;
168
+ if (before.state !== now.state) {
169
+ if (now.state === "exited")
170
+ events.push({ container: name, event: "exited" });
171
+ else if (now.state === "running")
172
+ events.push({ container: name, event: "started" });
173
+ else if (now.state === "restarting") {
174
+ events.push({ container: name, event: "restarting" });
175
+ }
176
+ }
177
+ // A container can go unhealthy while it stays up.
178
+ if (!before.unhealthy && now.unhealthy) {
179
+ events.push({ container: name, event: "unhealthy" });
180
+ }
181
+ }
182
+ return events;
183
+ }
184
+ /** Counts from `ps -a --format PS_STATE_FORMAT` output. */
185
+ export function countContainers(output) {
186
+ const states = [...parseContainerStates(output).values()];
187
+ const running = states.filter((s) => s.state === "running").length;
188
+ return {
189
+ running,
190
+ stopped: states.length - running,
191
+ unhealthy: states.filter((s) => s.unhealthy).length,
192
+ total: states.length,
193
+ };
194
+ }
195
+ // Compose stacks
196
+ export const STACK_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;
197
+ export const STACK_SERVICE_RE = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,126}$/;
198
+ export const COMPOSE_FILES = [
199
+ "compose.yaml",
200
+ "compose.yml",
201
+ "docker-compose.yaml",
202
+ "docker-compose.yml",
203
+ ];
204
+ export const STACK_ACTIONS = [
205
+ "deploy",
206
+ "redeploy",
207
+ "pull",
208
+ "start",
209
+ "stop",
210
+ "restart",
211
+ "pause",
212
+ "unpause",
213
+ "down",
214
+ "destroy",
215
+ ];
216
+ /** Actions that can target one service of a stack. */
217
+ export const STACK_SERVICE_ACTIONS = [
218
+ "deploy",
219
+ "pull",
220
+ "start",
221
+ "stop",
222
+ "restart",
223
+ "pause",
224
+ "unpause",
225
+ ];
226
+ /** `compose ls` and `compose ps --format json` print one object per line or one array. */
227
+ export function parseJsonLines(output) {
228
+ const text = output.trim();
229
+ if (!text)
230
+ return [];
231
+ if (text.startsWith("[")) {
232
+ try {
233
+ return JSON.parse(text);
234
+ }
235
+ catch {
236
+ return [];
237
+ }
238
+ }
239
+ return text.split("\n").flatMap((line) => {
240
+ try {
241
+ return [JSON.parse(line)];
242
+ }
243
+ catch {
244
+ return [];
245
+ }
246
+ });
247
+ }
248
+ const str = (value) => typeof value === "string" ? value : "";
249
+ export function parseServices(output) {
250
+ return parseJsonLines(output).map((row) => {
251
+ const publishers = Array.isArray(row.Publishers)
252
+ ? row.Publishers
253
+ : [];
254
+ const ports = publishers
255
+ .filter((p) => Number(p.PublishedPort) > 0)
256
+ .map((p) => `${p.PublishedPort}:${p.TargetPort}`)
257
+ .filter((value, index, all) => all.indexOf(value) === index)
258
+ .join(", ");
259
+ return {
260
+ service: str(row.Service),
261
+ name: str(row.Name),
262
+ id: str(row.ID),
263
+ image: str(row.Image),
264
+ state: str(row.State),
265
+ status: str(row.Status),
266
+ health: str(row.Health),
267
+ ports,
268
+ };
269
+ });
270
+ }
271
+ function stateOf(running, paused, total) {
272
+ if (total === 0)
273
+ return "not-deployed";
274
+ if (paused === total)
275
+ return "paused";
276
+ if (running === total)
277
+ return "running";
278
+ if (running > 0)
279
+ return "partial";
280
+ return "stopped";
281
+ }
282
+ /** Turns the `compose ls` status text ("running(2), exited(1)") into a state. */
283
+ export function parseStackStatus(status) {
284
+ let running = 0;
285
+ let paused = 0;
286
+ let total = 0;
287
+ for (const part of status.split(",")) {
288
+ const match = part.trim().match(/^([a-z]+)\((\d+)\)$/i);
289
+ if (!match)
290
+ continue;
291
+ const count = Number(match[2]);
292
+ total += count;
293
+ if (match[1] === "running")
294
+ running += count;
295
+ if (match[1] === "paused")
296
+ paused += count;
297
+ }
298
+ return { state: stateOf(running, paused, total), running, total };
299
+ }
300
+ export function stackStateFromServices(services) {
301
+ const total = services.length;
302
+ const running = services.filter((s) => s.state === "running").length;
303
+ const paused = services.filter((s) => s.state === "paused").length;
304
+ return { state: stateOf(running, paused, total), running, total };
305
+ }
306
+ /** Stacks from `compose ls -a --format json`, for hosts with no stacks folder. */
307
+ export function parseComposeProjects(output) {
308
+ return parseJsonLines(output)
309
+ .flatMap((project) => {
310
+ const name = str(project.Name);
311
+ if (!STACK_NAME_RE.test(name))
312
+ return [];
313
+ return [
314
+ {
315
+ name,
316
+ managed: false,
317
+ ...parseStackStatus(str(project.Status)),
318
+ composeFile: str(project.ConfigFiles).split(",")[0] || null,
319
+ },
320
+ ];
321
+ })
322
+ .sort((a, b) => a.name.localeCompare(b.name));
323
+ }
324
+ /** The compose commands one action runs, in order. */
325
+ export function composeArgsFor(action, service, options) {
326
+ const target = service ? ` ${service}` : "";
327
+ switch (action) {
328
+ case "deploy":
329
+ return [`up -d --remove-orphans${target}`];
330
+ case "redeploy":
331
+ return [`pull${target}`, `up -d --remove-orphans${target}`];
332
+ case "pull":
333
+ return [`pull${target}`];
334
+ case "start":
335
+ case "stop":
336
+ case "restart":
337
+ case "pause":
338
+ case "unpause":
339
+ return [`${action}${target}`];
340
+ case "down":
341
+ return ["down --remove-orphans"];
342
+ case "destroy":
343
+ return [`down --remove-orphans${options.removeVolumes ? " -v" : ""}`];
344
+ }
345
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Host scripts: command builders and output parsers for Docker, Linux
3
+ * metrics, systemd, the journal and OpenRC.
4
+ *
5
+ * Pure TypeScript with no Node or ssh2 imports, so the same code runs in a
6
+ * plugin backend and in the mobile app. Run a script by sending it on stdin
7
+ * to the command from `scriptShell()`.
8
+ */
9
+ export * from "./text.js";
10
+ export * from "./shell.js";
11
+ export * from "./docker.js";
12
+ export * from "./metrics.js";
13
+ export * from "./systemd.js";
14
+ export * from "./journal.js";
15
+ export * from "./openrc.js";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Host scripts: command builders and output parsers for Docker, Linux
3
+ * metrics, systemd, the journal and OpenRC.
4
+ *
5
+ * Pure TypeScript with no Node or ssh2 imports, so the same code runs in a
6
+ * plugin backend and in the mobile app. Run a script by sending it on stdin
7
+ * to the command from `scriptShell()`.
8
+ */
9
+ export * from "./text.js";
10
+ export * from "./shell.js";
11
+ export * from "./docker.js";
12
+ export * from "./metrics.js";
13
+ export * from "./systemd.js";
14
+ export * from "./journal.js";
15
+ export * from "./openrc.js";
@@ -0,0 +1,45 @@
1
+ export declare const MAX_JOURNAL_LINES = 5000;
2
+ export interface JournalQuery {
3
+ units: string[];
4
+ priority?: number;
5
+ since?: string;
6
+ until?: string;
7
+ boot?: string;
8
+ grep?: string;
9
+ lines: number;
10
+ kernel: boolean;
11
+ }
12
+ export interface JournalEntry {
13
+ /** Unix ms. */
14
+ time: number;
15
+ priority: number;
16
+ unit: string;
17
+ ident: string;
18
+ pid: string;
19
+ message: string;
20
+ }
21
+ export interface BootView {
22
+ offset: number;
23
+ id: string;
24
+ first: string;
25
+ last: string;
26
+ }
27
+ export declare function isJournalTime(value: string): boolean;
28
+ /** Reads journal filters from a query string. Anything odd throws a HostScriptError. */
29
+ export declare function parseJournalQuery(input: Record<string, unknown>): JournalQuery;
30
+ /** journalctl arguments for a query, each one quoted. */
31
+ export declare function journalArgs(query: Omit<JournalQuery, "lines"> & {
32
+ lines?: number;
33
+ }, options?: {
34
+ follow?: boolean;
35
+ useGrep?: boolean;
36
+ }): string;
37
+ export declare function journalScript(query: JournalQuery): string;
38
+ /** One `journalctl -o json` line, or null when it is not an entry. */
39
+ export declare function parseEntry(line: string): JournalEntry | null;
40
+ export declare function parseJournal(output: string, query: JournalQuery): JournalEntry[];
41
+ export declare const BOOTS_SCRIPT = "journalctl --list-boots --no-pager 2>/dev/null | tail -n 50; exit 0\n";
42
+ export declare function parseBoots(output: string): BootView[];
43
+ export declare const USAGE_SCRIPT = "journalctl --disk-usage 2>&1; exit 0\n";
44
+ export declare function parseUsage(output: string): string;
45
+ export declare function vacuumScript(body: Record<string, unknown>): string;