dockscope 0.2.3 → 0.2.5

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 CHANGED
@@ -42,11 +42,12 @@ Requires Docker to be running. Opens a browser at `http://localhost:4681`.
42
42
  ```
43
43
  dockscope up [options]
44
44
 
45
- -p, --port <port> Server port (default: 4681)
46
- -f, --file <path> Docker Compose file path (default: auto-detect)
45
+ -p, --port <port> Server port (default: 4681, auto-increments if in use)
47
46
  --no-open Don't open browser automatically
48
47
  ```
49
48
 
49
+ Docker Compose files are auto-detected (`compose.yml`, `docker-compose.yml`, etc.) and dependencies are read from container labels at runtime — no configuration needed.
50
+
50
51
  ### Scan Mode
51
52
 
52
53
  Output the container graph as JSON (no UI):
@@ -64,7 +65,7 @@ npm install
64
65
  npm run dev
65
66
  ```
66
67
 
67
- This starts the backend on port `4681` and the Vite dev server on port `4680` with hot reload.
68
+ This starts the server on port `4681` with Vite HMR embedded single port, same as production.
68
69
 
69
70
  ### Scripts
70
71
 
package/dist/cli.js CHANGED
@@ -1,29 +1,67 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import path from 'path';
6
+ import { createConnection } from 'net';
3
7
  import { startServer } from './server/index.js';
4
8
  import { buildGraph, checkConnection } from './docker/client.js';
9
+ function isPortInUse(port) {
10
+ return new Promise((resolve) => {
11
+ const conn = createConnection({ port, host: '127.0.0.1' });
12
+ const timeout = setTimeout(() => {
13
+ conn.destroy();
14
+ resolve(false);
15
+ }, 500);
16
+ conn.on('connect', () => {
17
+ clearTimeout(timeout);
18
+ conn.destroy();
19
+ resolve(true);
20
+ });
21
+ conn.on('error', () => {
22
+ clearTimeout(timeout);
23
+ resolve(false);
24
+ });
25
+ });
26
+ }
27
+ async function findAvailablePort(start) {
28
+ let port = start;
29
+ while (await isPortInUse(port)) {
30
+ console.log(` Port ${port} is in use, trying ${port + 1}...`);
31
+ port++;
32
+ if (port > start + 20) {
33
+ console.error(` No available port found in range ${start}-${port}`);
34
+ process.exit(1);
35
+ }
36
+ }
37
+ return port;
38
+ }
39
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
40
+ const pkg = JSON.parse(readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8'));
41
+ const VERSION = pkg.version;
5
42
  const program = new Command();
6
43
  program
7
44
  .name('dockscope')
8
45
  .description('Visual, interactive Docker infrastructure debugger')
9
- .version('0.1.0');
46
+ .version(VERSION);
10
47
  program
11
- .command('up')
48
+ .command('up', { isDefault: true })
12
49
  .description('Start the DockScope dashboard')
13
50
  .option('-p, --port <port>', 'Server port', '4681')
14
- .option('-f, --file <path>', 'Docker Compose file path', 'docker-compose.yml')
15
51
  .option('--no-open', "Don't open browser automatically")
52
+ .option('--no-port-check', 'Skip port conflict detection')
16
53
  .action(async (opts) => {
17
- const port = parseInt(opts.port, 10);
54
+ const requestedPort = parseInt(opts.port, 10);
55
+ const port = opts.portCheck === false ? requestedPort : await findAvailablePort(requestedPort);
18
56
  console.log(`
19
57
  ____ _ ____
20
58
  | _ \\ ___ ___| | _/ ___| ___ ___ _ __ ___
21
59
  | | | |/ _ \\ / __| |/ \\___ \\ / __/ _ \\| '_ \\ / _ \\
22
60
  | |_| | (_) | (__| < ___) | (_| (_) | |_) | __/
23
61
  |____/ \\___/ \\___|_|\\_\\____/ \\___\\___/| .__/ \\___|
24
- |_| v0.1.0
62
+ |_| v${VERSION}
25
63
  `);
26
- await startServer({ port, composeFile: opts.file, open: opts.open !== false });
64
+ await startServer({ port, open: opts.open !== false });
27
65
  const url = `http://localhost:${port}`;
28
66
  console.log(` Dashboard: ${url}`);
29
67
  console.log(` API: ${url}/api/graph`);
@@ -38,14 +76,13 @@ program
38
76
  program
39
77
  .command('scan')
40
78
  .description('Scan Docker environment and output graph data as JSON')
41
- .option('-f, --file <path>', 'Docker Compose file path', 'docker-compose.yml')
42
- .action(async (opts) => {
79
+ .action(async () => {
43
80
  const connected = await checkConnection();
44
81
  if (!connected) {
45
82
  console.error('Cannot connect to Docker daemon. Is Docker running?');
46
83
  process.exit(1);
47
84
  }
48
- const graph = await buildGraph(opts.file);
85
+ const graph = await buildGraph();
49
86
  console.log(JSON.stringify(graph, null, 2));
50
87
  });
51
88
  program.parse();
@@ -22,12 +22,28 @@ export async function startServer(opts) {
22
22
  const metricHistory = new Map();
23
23
  // REST routes
24
24
  setupRoutes(app, opts, metricHistory);
25
- // Serve built frontend in production
26
- const webDir = path.resolve(__dirname, '../web');
27
- app.use(express.static(webDir));
28
- app.get('*', (_req, res) => {
29
- res.sendFile(path.join(webDir, 'index.html'));
30
- });
25
+ // Frontend: Vite dev server (HMR) or static files (production)
26
+ if (process.env.DOCKSCOPE_DEV === '1') {
27
+ try {
28
+ const { createServer: createVite } = await import('vite');
29
+ const vite = await createVite({
30
+ server: { middlewareMode: true, hmr: { server } },
31
+ appType: 'spa',
32
+ });
33
+ app.use(vite.middlewares);
34
+ }
35
+ catch {
36
+ console.error('Vite not found — install devDependencies for dev mode');
37
+ process.exit(1);
38
+ }
39
+ }
40
+ else {
41
+ const webDir = path.resolve(__dirname, '../web');
42
+ app.use(express.static(webDir));
43
+ app.get('*', (_req, res) => {
44
+ res.sendFile(path.join(webDir, 'index.html'));
45
+ });
46
+ }
31
47
  // --- WebSocket ---
32
48
  const broadcast = (msg) => {
33
49
  const data = JSON.stringify(msg);
@@ -39,7 +55,7 @@ export async function startServer(opts) {
39
55
  let cachedGraph = { nodes: [], links: [] };
40
56
  const refreshGraph = async () => {
41
57
  try {
42
- cachedGraph = await buildGraph(opts.composeFile);
58
+ cachedGraph = await buildGraph();
43
59
  broadcast({ type: 'graph', data: cachedGraph });
44
60
  }
45
61
  catch {
@@ -2,7 +2,7 @@ import { buildGraph, checkConnection, composeAction, containerAction, getContain
2
2
  export function setupRoutes(app, opts, metricHistory) {
3
3
  app.get('/api/graph', async (_req, res) => {
4
4
  try {
5
- const graph = await buildGraph(opts.composeFile);
5
+ const graph = await buildGraph();
6
6
  res.json(graph);
7
7
  }
8
8
  catch (err) {
package/dist/types.d.ts CHANGED
@@ -88,6 +88,5 @@ export interface MetricPoint {
88
88
  }
89
89
  export interface ServerOptions {
90
90
  port: number;
91
- composeFile: string;
92
91
  open: boolean;
93
92
  }