sidekick-docker 0.1.0 → 0.1.1
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/README.md +7 -1
- package/dist/sidekick-docker.mjs +175 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
A terminal dashboard for Docker. Manage containers, Compose projects, images, volumes, and networks — all from a single, keyboard-driven TUI.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="../assets/sidekick_docker_cli.gif" alt="Sidekick Docker CLI Demo" width="800">
|
|
11
|
+
</p>
|
|
10
12
|
|
|
11
13
|
## Install
|
|
12
14
|
|
|
@@ -127,6 +129,10 @@ sidekick-docker --socket tcp://192.168.1.100:2375
|
|
|
127
129
|
|
|
128
130
|
Full documentation is available at the [docs site](https://cesarandreslopez.github.io/sidekick-docker/).
|
|
129
131
|
|
|
132
|
+
## See Also
|
|
133
|
+
|
|
134
|
+
**[Sidekick Agent Hub](https://github.com/cesarandreslopez/sidekick-agent-hub)** — Multi-provider AI coding agent monitor. Real-time visibility into Claude Code, OpenCode, and Codex CLI sessions with token tracking, context management, and session intelligence. Available as a [TUI on npm](https://www.npmjs.com/package/sidekick-agent-hub) and a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=CesarAndresLopez.sidekick-for-max).
|
|
135
|
+
|
|
130
136
|
## Contributing
|
|
131
137
|
|
|
132
138
|
Contributions are welcome! See [CONTRIBUTING.md](../CONTRIBUTING.md) for setup instructions and guidelines.
|
package/dist/sidekick-docker.mjs
CHANGED
|
@@ -39925,6 +39925,89 @@ var require_ComposeClient = __commonJS({
|
|
|
39925
39925
|
async ps(project) {
|
|
39926
39926
|
return this.exec(["-p", project, "ps", "--format", "json"]);
|
|
39927
39927
|
}
|
|
39928
|
+
async *streamLogs(project, service, tail = 100) {
|
|
39929
|
+
const args = ["-p", project, "logs", "--follow", "--tail", String(tail), "--timestamps"];
|
|
39930
|
+
if (service)
|
|
39931
|
+
args.push(service);
|
|
39932
|
+
const proc = (0, child_process_1.spawn)("docker", ["compose", ...args], {
|
|
39933
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
39934
|
+
});
|
|
39935
|
+
const parseLine = (line, stream) => {
|
|
39936
|
+
if (!line.trim())
|
|
39937
|
+
return null;
|
|
39938
|
+
const pipeIdx = line.indexOf("|");
|
|
39939
|
+
let message = line;
|
|
39940
|
+
let timestamp = null;
|
|
39941
|
+
if (pipeIdx > 0) {
|
|
39942
|
+
const after = line.substring(pipeIdx + 1).trimStart();
|
|
39943
|
+
const tsMatch = after.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[\d.]*Z?)\s*(.*)/);
|
|
39944
|
+
if (tsMatch) {
|
|
39945
|
+
const parsed = new Date(tsMatch[1]);
|
|
39946
|
+
if (!isNaN(parsed.getTime())) {
|
|
39947
|
+
timestamp = parsed;
|
|
39948
|
+
message = `${line.substring(0, pipeIdx).trim()} | ${tsMatch[2]}`;
|
|
39949
|
+
} else {
|
|
39950
|
+
message = `${line.substring(0, pipeIdx).trim()} | ${after}`;
|
|
39951
|
+
}
|
|
39952
|
+
} else {
|
|
39953
|
+
message = `${line.substring(0, pipeIdx).trim()} | ${after}`;
|
|
39954
|
+
}
|
|
39955
|
+
}
|
|
39956
|
+
return { timestamp, stream, message };
|
|
39957
|
+
};
|
|
39958
|
+
let stdoutBuffer = "";
|
|
39959
|
+
let stderrBuffer = "";
|
|
39960
|
+
let done = false;
|
|
39961
|
+
const entries = [];
|
|
39962
|
+
let resolve = null;
|
|
39963
|
+
const push = (entry) => {
|
|
39964
|
+
entries.push(entry);
|
|
39965
|
+
resolve?.();
|
|
39966
|
+
};
|
|
39967
|
+
proc.stdout.on("data", (data) => {
|
|
39968
|
+
stdoutBuffer += data.toString();
|
|
39969
|
+
const lines = stdoutBuffer.split("\n");
|
|
39970
|
+
stdoutBuffer = lines.pop();
|
|
39971
|
+
for (const line of lines) {
|
|
39972
|
+
const entry = parseLine(line, "stdout");
|
|
39973
|
+
if (entry)
|
|
39974
|
+
push(entry);
|
|
39975
|
+
}
|
|
39976
|
+
});
|
|
39977
|
+
proc.stderr.on("data", (data) => {
|
|
39978
|
+
stderrBuffer += data.toString();
|
|
39979
|
+
const lines = stderrBuffer.split("\n");
|
|
39980
|
+
stderrBuffer = lines.pop();
|
|
39981
|
+
for (const line of lines) {
|
|
39982
|
+
const entry = parseLine(line, "stderr");
|
|
39983
|
+
if (entry)
|
|
39984
|
+
push(entry);
|
|
39985
|
+
}
|
|
39986
|
+
});
|
|
39987
|
+
proc.on("close", () => {
|
|
39988
|
+
done = true;
|
|
39989
|
+
resolve?.();
|
|
39990
|
+
});
|
|
39991
|
+
proc.on("error", () => {
|
|
39992
|
+
done = true;
|
|
39993
|
+
resolve?.();
|
|
39994
|
+
});
|
|
39995
|
+
try {
|
|
39996
|
+
while (!done) {
|
|
39997
|
+
if (entries.length === 0) {
|
|
39998
|
+
await new Promise((r) => {
|
|
39999
|
+
resolve = r;
|
|
40000
|
+
});
|
|
40001
|
+
resolve = null;
|
|
40002
|
+
}
|
|
40003
|
+
while (entries.length > 0) {
|
|
40004
|
+
yield entries.shift();
|
|
40005
|
+
}
|
|
40006
|
+
}
|
|
40007
|
+
} finally {
|
|
40008
|
+
proc.kill();
|
|
40009
|
+
}
|
|
40010
|
+
}
|
|
39928
40011
|
};
|
|
39929
40012
|
exports2.ComposeClient = ComposeClient2;
|
|
39930
40013
|
}
|
|
@@ -77304,6 +77387,7 @@ var DockerState = class {
|
|
|
77304
77387
|
networks = [];
|
|
77305
77388
|
composeProjects = [];
|
|
77306
77389
|
selectedLogs = [];
|
|
77390
|
+
selectedComposeLogs = [];
|
|
77307
77391
|
inspectedEnv = /* @__PURE__ */ new Map();
|
|
77308
77392
|
lastRefresh = null;
|
|
77309
77393
|
daemonConnected = false;
|
|
@@ -77407,6 +77491,15 @@ var DockerState = class {
|
|
|
77407
77491
|
clearLogs() {
|
|
77408
77492
|
this.selectedLogs = [];
|
|
77409
77493
|
}
|
|
77494
|
+
appendComposeLog(entry) {
|
|
77495
|
+
this.selectedComposeLogs.push(entry);
|
|
77496
|
+
if (this.selectedComposeLogs.length > 1e3) {
|
|
77497
|
+
this.selectedComposeLogs.shift();
|
|
77498
|
+
}
|
|
77499
|
+
}
|
|
77500
|
+
clearComposeLogs() {
|
|
77501
|
+
this.selectedComposeLogs = [];
|
|
77502
|
+
}
|
|
77410
77503
|
getStatsCollector() {
|
|
77411
77504
|
return this.statsCollector;
|
|
77412
77505
|
}
|
|
@@ -77426,6 +77519,7 @@ var DockerState = class {
|
|
|
77426
77519
|
statsCollector: this.statsCollector,
|
|
77427
77520
|
inspectedEnv: this.inspectedEnv,
|
|
77428
77521
|
selectedContainerLogs: [...this.selectedLogs],
|
|
77522
|
+
selectedComposeLogs: [...this.selectedComposeLogs],
|
|
77429
77523
|
lastRefresh: this.lastRefresh,
|
|
77430
77524
|
daemonConnected: this.daemonConnected
|
|
77431
77525
|
};
|
|
@@ -77767,9 +77861,12 @@ var ServicesPanel = class {
|
|
|
77767
77861
|
},
|
|
77768
77862
|
{
|
|
77769
77863
|
label: "Logs",
|
|
77770
|
-
render: () => {
|
|
77771
|
-
|
|
77772
|
-
|
|
77864
|
+
render: (_item, metrics) => {
|
|
77865
|
+
const logs = metrics.selectedComposeLogs;
|
|
77866
|
+
if (logs.length === 0) return "No compose logs. Logs will appear when a service produces output.";
|
|
77867
|
+
return logs.map((e) => colorizeLogEntry(e)).join("\n");
|
|
77868
|
+
},
|
|
77869
|
+
autoScrollBottom: true
|
|
77773
77870
|
}
|
|
77774
77871
|
];
|
|
77775
77872
|
getItems(metrics) {
|
|
@@ -78218,6 +78315,55 @@ var StatsStreamManager = class {
|
|
|
78218
78315
|
}
|
|
78219
78316
|
};
|
|
78220
78317
|
|
|
78318
|
+
// src/dashboard/ComposeLogStreamManager.ts
|
|
78319
|
+
var MAX_LOG_LINES2 = 1e3;
|
|
78320
|
+
var ComposeLogStreamManager = class {
|
|
78321
|
+
composeClient;
|
|
78322
|
+
currentProject = null;
|
|
78323
|
+
currentService = null;
|
|
78324
|
+
logs = [];
|
|
78325
|
+
aborted = false;
|
|
78326
|
+
onChange;
|
|
78327
|
+
constructor(composeClient, onChange) {
|
|
78328
|
+
this.composeClient = composeClient;
|
|
78329
|
+
this.onChange = onChange;
|
|
78330
|
+
}
|
|
78331
|
+
async select(project, service) {
|
|
78332
|
+
if (project === this.currentProject && service === this.currentService) return;
|
|
78333
|
+
this.stop();
|
|
78334
|
+
this.currentProject = project;
|
|
78335
|
+
this.currentService = service;
|
|
78336
|
+
this.logs = [];
|
|
78337
|
+
if (!project) return;
|
|
78338
|
+
this.aborted = false;
|
|
78339
|
+
this.streamLogs(project, service);
|
|
78340
|
+
}
|
|
78341
|
+
async streamLogs(project, service) {
|
|
78342
|
+
try {
|
|
78343
|
+
for await (const entry of this.composeClient.streamLogs(project, service ?? void 0)) {
|
|
78344
|
+
if (this.aborted || this.currentProject !== project) break;
|
|
78345
|
+
this.logs.push(entry);
|
|
78346
|
+
if (this.logs.length > MAX_LOG_LINES2) {
|
|
78347
|
+
this.logs.shift();
|
|
78348
|
+
}
|
|
78349
|
+
this.onChange();
|
|
78350
|
+
}
|
|
78351
|
+
} catch {
|
|
78352
|
+
}
|
|
78353
|
+
}
|
|
78354
|
+
stop() {
|
|
78355
|
+
this.aborted = true;
|
|
78356
|
+
this.currentProject = null;
|
|
78357
|
+
this.currentService = null;
|
|
78358
|
+
}
|
|
78359
|
+
getLogs() {
|
|
78360
|
+
return this.logs;
|
|
78361
|
+
}
|
|
78362
|
+
dispose() {
|
|
78363
|
+
this.stop();
|
|
78364
|
+
}
|
|
78365
|
+
};
|
|
78366
|
+
|
|
78221
78367
|
// src/dashboard/ink/Dashboard.tsx
|
|
78222
78368
|
var import_react35 = __toESM(require_react(), 1);
|
|
78223
78369
|
await init_build2();
|
|
@@ -79415,8 +79561,8 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
|
|
|
79415
79561
|
)
|
|
79416
79562
|
] })
|
|
79417
79563
|
] }),
|
|
79418
|
-
state.overlay === "help" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(HelpOverlay, { panels, activePanelIndex: state.activePanelIndex, version: "0.1.
|
|
79419
|
-
state.overlay === "version" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VersionOverlay, { version: "0.1.
|
|
79564
|
+
state.overlay === "help" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(HelpOverlay, { panels, activePanelIndex: state.activePanelIndex, version: "0.1.1" }),
|
|
79565
|
+
state.overlay === "version" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VersionOverlay, { version: "0.1.1" }),
|
|
79420
79566
|
state.overlay === "exec" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
79421
79567
|
ExecOverlay,
|
|
79422
79568
|
{
|
|
@@ -79434,7 +79580,7 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
|
|
|
79434
79580
|
filterString: state.filterString,
|
|
79435
79581
|
containerCount: metrics.containers.length,
|
|
79436
79582
|
runningCount,
|
|
79437
|
-
version: "0.1.
|
|
79583
|
+
version: "0.1.1",
|
|
79438
79584
|
matchCount: state.filterString ? currentItems.length : void 0,
|
|
79439
79585
|
totalCount: state.filterString ? totalItemCount : void 0,
|
|
79440
79586
|
lastRefresh: metrics.lastRefresh
|
|
@@ -79490,10 +79636,18 @@ async function dashboardAction(_opts, cmd) {
|
|
|
79490
79636
|
const statsManager = new StatsStreamManager(client, state.getStatsCollector(), () => {
|
|
79491
79637
|
scheduleRender();
|
|
79492
79638
|
});
|
|
79639
|
+
const composeLogManager = new ComposeLogStreamManager(composeClient, () => {
|
|
79640
|
+
state.clearComposeLogs();
|
|
79641
|
+
for (const entry of composeLogManager.getLogs()) {
|
|
79642
|
+
state.appendComposeLog(entry);
|
|
79643
|
+
}
|
|
79644
|
+
scheduleRender();
|
|
79645
|
+
});
|
|
79493
79646
|
const onSelectionChange = (panelId, itemId) => {
|
|
79494
79647
|
if (panelId === "containers") {
|
|
79495
79648
|
logManager.select(itemId);
|
|
79496
79649
|
statsManager.select(itemId);
|
|
79650
|
+
composeLogManager.select(null, null);
|
|
79497
79651
|
if (itemId && !state.getInspectedEnv(itemId)) {
|
|
79498
79652
|
client.inspectContainer(itemId).then((info) => {
|
|
79499
79653
|
state.setInspectedEnv(itemId, info.Config.Env || []);
|
|
@@ -79501,9 +79655,19 @@ async function dashboardAction(_opts, cmd) {
|
|
|
79501
79655
|
}).catch(() => {
|
|
79502
79656
|
});
|
|
79503
79657
|
}
|
|
79658
|
+
} else if (panelId === "services" && itemId) {
|
|
79659
|
+
logManager.select(null);
|
|
79660
|
+
statsManager.select(null);
|
|
79661
|
+
const parts = itemId.split(":");
|
|
79662
|
+
if (parts[0] === "project") {
|
|
79663
|
+
composeLogManager.select(parts.slice(1).join(":"), null);
|
|
79664
|
+
} else if (parts[0] === "service") {
|
|
79665
|
+
composeLogManager.select(parts[1], parts.slice(2).join(":"));
|
|
79666
|
+
}
|
|
79504
79667
|
} else {
|
|
79505
79668
|
logManager.select(null);
|
|
79506
79669
|
statsManager.select(null);
|
|
79670
|
+
composeLogManager.select(null, null);
|
|
79507
79671
|
}
|
|
79508
79672
|
};
|
|
79509
79673
|
const panels = [
|
|
@@ -79593,6 +79757,10 @@ async function dashboardAction(_opts, cmd) {
|
|
|
79593
79757
|
statsManager.dispose();
|
|
79594
79758
|
} catch {
|
|
79595
79759
|
}
|
|
79760
|
+
try {
|
|
79761
|
+
composeLogManager.dispose();
|
|
79762
|
+
} catch {
|
|
79763
|
+
}
|
|
79596
79764
|
try {
|
|
79597
79765
|
clearInterval(refreshInterval);
|
|
79598
79766
|
} catch {
|
|
@@ -79677,7 +79845,7 @@ async function logsAction(container, opts) {
|
|
|
79677
79845
|
|
|
79678
79846
|
// src/cli.ts
|
|
79679
79847
|
var program2 = new Command();
|
|
79680
|
-
program2.name("sidekick-docker").description("Docker management TUI dashboard").version("0.1.
|
|
79848
|
+
program2.name("sidekick-docker").description("Docker management TUI dashboard").version("0.1.1").option("--socket <path>", "Docker socket path").action(async (_opts, cmd) => {
|
|
79681
79849
|
await dashboardAction(_opts, cmd);
|
|
79682
79850
|
});
|
|
79683
79851
|
program2.command("ps").description("List containers").option("-a, --all", "Show all containers (default: running only)", false).action(async (opts) => {
|