zigrix 0.1.0-alpha.3 → 0.1.0-alpha.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 +30 -0
- package/dist/dashboard.d.ts +35 -0
- package/dist/dashboard.js +113 -0
- package/dist/index.js +17 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,36 @@ zigrix onboard
|
|
|
36
36
|
zigrix doctor
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### Prerequisites: Node.js version
|
|
40
|
+
|
|
41
|
+
Zigrix requires Node.js **22 or later**. Verify before installing:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
node --version # must be v22.x or higher
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If you use **nvm**, make sure the correct version is active:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
nvm use 22 # or: nvm use --lts
|
|
51
|
+
node --version # confirm v22+
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> **Important (nvm users):** `npm link` binds `zigrix` to whichever Node version is active at install time.
|
|
55
|
+
> If you switch node versions later, re-run the install to rebind:
|
|
56
|
+
> ```bash
|
|
57
|
+
> npm run build && npm link
|
|
58
|
+
> ```
|
|
59
|
+
|
|
60
|
+
### Version mismatch troubleshooting
|
|
61
|
+
|
|
62
|
+
If `zigrix --version` shows an unexpected version after updating, rebuild and relink:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run build && npm link
|
|
66
|
+
zigrix --version # should now match package.json
|
|
67
|
+
```
|
|
68
|
+
|
|
39
69
|
`zigrix onboard` will:
|
|
40
70
|
1. Create `~/.zigrix/` with default config
|
|
41
71
|
2. Detect OpenClaw and import agents from `openclaw.json`
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Default port for zigrix dashboard */
|
|
2
|
+
export declare const DASHBOARD_DEFAULT_PORT = 3838;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the dashboard directory from a given dist index.js path.
|
|
5
|
+
* Convention: dist/index.js → ../../dashboard (i.e. <package-root>/dashboard)
|
|
6
|
+
*
|
|
7
|
+
* The path "../../dashboard" is interpreted relative to the FILE dist/index.js:
|
|
8
|
+
* - First ".." removes the filename component → <pkg>/dist/
|
|
9
|
+
* - Second ".." removes the dist directory → <pkg>/ (package root)
|
|
10
|
+
* - "dashboard" → <pkg>/dashboard
|
|
11
|
+
*
|
|
12
|
+
* Using path.resolve(distIndexPath, '..', '..', 'dashboard') achieves exactly
|
|
13
|
+
* this because path.resolve treats each ".." as stripping the last segment of
|
|
14
|
+
* the current accumulated path (including the filename on the first step).
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveDashboardDir(distIndexPath: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Check whether a TCP port is already in use.
|
|
19
|
+
* Returns true if the port is busy (another process is listening).
|
|
20
|
+
*/
|
|
21
|
+
export declare function isPortBusy(port: number): Promise<boolean>;
|
|
22
|
+
export interface RunDashboardOptions {
|
|
23
|
+
port?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Main entry point for `zigrix dashboard`.
|
|
27
|
+
*
|
|
28
|
+
* Steps:
|
|
29
|
+
* 1. Resolve dashboard directory relative to this module's dist path.
|
|
30
|
+
* 2. Check port availability; fail clearly on conflict.
|
|
31
|
+
* 3. Auto-install if node_modules is missing.
|
|
32
|
+
* 4. Auto-build if .next directory is missing.
|
|
33
|
+
* 5. Spawn `next start -p <port>` in foreground and wait.
|
|
34
|
+
*/
|
|
35
|
+
export declare function runDashboard(options?: RunDashboardOptions): Promise<void>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { execSync, spawn } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as net from 'node:net';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
/** Default port for zigrix dashboard */
|
|
7
|
+
export const DASHBOARD_DEFAULT_PORT = 3838;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the dashboard directory from a given dist index.js path.
|
|
10
|
+
* Convention: dist/index.js → ../../dashboard (i.e. <package-root>/dashboard)
|
|
11
|
+
*
|
|
12
|
+
* The path "../../dashboard" is interpreted relative to the FILE dist/index.js:
|
|
13
|
+
* - First ".." removes the filename component → <pkg>/dist/
|
|
14
|
+
* - Second ".." removes the dist directory → <pkg>/ (package root)
|
|
15
|
+
* - "dashboard" → <pkg>/dashboard
|
|
16
|
+
*
|
|
17
|
+
* Using path.resolve(distIndexPath, '..', '..', 'dashboard') achieves exactly
|
|
18
|
+
* this because path.resolve treats each ".." as stripping the last segment of
|
|
19
|
+
* the current accumulated path (including the filename on the first step).
|
|
20
|
+
*/
|
|
21
|
+
export function resolveDashboardDir(distIndexPath) {
|
|
22
|
+
return path.resolve(distIndexPath, '..', '..', 'dashboard');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check whether a TCP port is already in use.
|
|
26
|
+
* Returns true if the port is busy (another process is listening).
|
|
27
|
+
*/
|
|
28
|
+
export function isPortBusy(port) {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const server = net.createServer();
|
|
31
|
+
server.once('error', (err) => {
|
|
32
|
+
if (err.code === 'EADDRINUSE') {
|
|
33
|
+
resolve(true);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Unexpected error — treat as busy to be safe
|
|
37
|
+
resolve(true);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
server.once('listening', () => {
|
|
41
|
+
server.close(() => resolve(false));
|
|
42
|
+
});
|
|
43
|
+
server.listen(port, '127.0.0.1');
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Main entry point for `zigrix dashboard`.
|
|
48
|
+
*
|
|
49
|
+
* Steps:
|
|
50
|
+
* 1. Resolve dashboard directory relative to this module's dist path.
|
|
51
|
+
* 2. Check port availability; fail clearly on conflict.
|
|
52
|
+
* 3. Auto-install if node_modules is missing.
|
|
53
|
+
* 4. Auto-build if .next directory is missing.
|
|
54
|
+
* 5. Spawn `next start -p <port>` in foreground and wait.
|
|
55
|
+
*/
|
|
56
|
+
export async function runDashboard(options = {}) {
|
|
57
|
+
const port = options.port ?? DASHBOARD_DEFAULT_PORT;
|
|
58
|
+
// Resolve dashboard directory.
|
|
59
|
+
// When running from compiled dist/index.js: import.meta.url resolves to
|
|
60
|
+
// file:///…/dist/index.js, so dirname is …/dist.
|
|
61
|
+
// ../../dashboard therefore points to <package-root>/dashboard.
|
|
62
|
+
const selfPath = fileURLToPath(import.meta.url);
|
|
63
|
+
const dashboardDir = resolveDashboardDir(selfPath);
|
|
64
|
+
if (!fs.existsSync(dashboardDir)) {
|
|
65
|
+
throw new Error(`Dashboard directory not found: ${dashboardDir}`);
|
|
66
|
+
}
|
|
67
|
+
// --- Port conflict check ---
|
|
68
|
+
const busy = await isPortBusy(port);
|
|
69
|
+
if (busy) {
|
|
70
|
+
throw new Error(`Port ${port} is already in use. ` +
|
|
71
|
+
`Stop the conflicting process or specify a different port with --port.`);
|
|
72
|
+
}
|
|
73
|
+
// --- Auto-install dependencies ---
|
|
74
|
+
const nodeModulesDir = path.join(dashboardDir, 'node_modules');
|
|
75
|
+
if (!fs.existsSync(nodeModulesDir)) {
|
|
76
|
+
console.log('📦 dashboard/node_modules not found — running npm install…');
|
|
77
|
+
execSync('npm install', { cwd: dashboardDir, stdio: 'inherit' });
|
|
78
|
+
}
|
|
79
|
+
// --- Auto-build ---
|
|
80
|
+
const nextBuildDir = path.join(dashboardDir, '.next');
|
|
81
|
+
if (!fs.existsSync(nextBuildDir)) {
|
|
82
|
+
console.log('🔨 dashboard/.next not found — running npm run build…');
|
|
83
|
+
execSync('npm run build', { cwd: dashboardDir, stdio: 'inherit' });
|
|
84
|
+
}
|
|
85
|
+
// --- Launch Next.js in foreground ---
|
|
86
|
+
console.log(`🚀 Starting zigrix dashboard on http://localhost:${port}`);
|
|
87
|
+
const nextBin = path.join(dashboardDir, 'node_modules', '.bin', 'next');
|
|
88
|
+
const child = spawn(nextBin, ['start', '-p', String(port)], {
|
|
89
|
+
cwd: dashboardDir,
|
|
90
|
+
stdio: 'inherit',
|
|
91
|
+
env: { ...process.env },
|
|
92
|
+
});
|
|
93
|
+
await new Promise((resolve, reject) => {
|
|
94
|
+
child.on('exit', (code, signal) => {
|
|
95
|
+
if (code === 0 || signal === 'SIGINT' || signal === 'SIGTERM') {
|
|
96
|
+
resolve();
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
reject(new Error(`next start exited with code ${code ?? signal}`));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
child.on('error', (err) => {
|
|
103
|
+
reject(new Error(`Failed to start next: ${err.message}`));
|
|
104
|
+
});
|
|
105
|
+
// Forward SIGINT/SIGTERM to child for clean shutdown
|
|
106
|
+
const forward = (sig) => {
|
|
107
|
+
if (!child.killed)
|
|
108
|
+
child.kill(sig);
|
|
109
|
+
};
|
|
110
|
+
process.once('SIGINT', () => forward('SIGINT'));
|
|
111
|
+
process.once('SIGTERM', () => forward('SIGTERM'));
|
|
112
|
+
});
|
|
113
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { loadRunRecord } from './runner/store.js';
|
|
|
21
21
|
import { ensureBaseState, resolvePaths } from './state/paths.js';
|
|
22
22
|
import { applyStalePolicy, createTask, findStaleTasks, listTaskEvents, listTasks, loadTask, rebuildIndex, recordTaskProgress, updateTaskStatus, } from './state/tasks.js';
|
|
23
23
|
import { verifyState } from './state/verify.js';
|
|
24
|
+
import { runDashboard, DASHBOARD_DEFAULT_PORT } from './dashboard.js';
|
|
24
25
|
const STATUS_MAP = {
|
|
25
26
|
start: 'IN_PROGRESS',
|
|
26
27
|
report: 'REPORTED',
|
|
@@ -57,11 +58,12 @@ function loadRuntime(options) {
|
|
|
57
58
|
const loaded = loadConfig({ baseDir: options.baseDir, configPath: options.config });
|
|
58
59
|
return { ...loaded, paths: resolvePaths(loaded.config) };
|
|
59
60
|
}
|
|
61
|
+
const { version: pkgVersion } = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
|
|
60
62
|
const program = new Command();
|
|
61
63
|
program
|
|
62
64
|
.name('zigrix')
|
|
63
65
|
.description('Zigrix — multi-project parallel task orchestration CLI')
|
|
64
|
-
.version(
|
|
66
|
+
.version(pkgVersion);
|
|
65
67
|
const config = program.command('config').description('Inspect Zigrix config');
|
|
66
68
|
const agent = program.command('agent').description('Manage Zigrix agent registry and orchestration membership');
|
|
67
69
|
const rule = program.command('rule').description('Inspect and validate rule assets');
|
|
@@ -785,6 +787,20 @@ program
|
|
|
785
787
|
const loaded = loadConfig({ baseDir: options.baseDir, configPath: options.config });
|
|
786
788
|
printValue(loadRunRecord(loaded.config, runIdOrPath), true);
|
|
787
789
|
});
|
|
790
|
+
// ─── dashboard ──────────────────────────────────────────────────────────────
|
|
791
|
+
program
|
|
792
|
+
.command('dashboard')
|
|
793
|
+
.description('Start the zigrix web dashboard (Next.js) in the foreground')
|
|
794
|
+
.option('--port <n>', `TCP port to listen on (default: ${DASHBOARD_DEFAULT_PORT})`, String(DASHBOARD_DEFAULT_PORT))
|
|
795
|
+
.action(async (options) => {
|
|
796
|
+
const port = parseInt(options.port, 10);
|
|
797
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
798
|
+
console.error(`Invalid port: ${options.port}`);
|
|
799
|
+
process.exitCode = 1;
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
await runDashboard({ port });
|
|
803
|
+
});
|
|
788
804
|
program.parseAsync(process.argv).catch((error) => {
|
|
789
805
|
console.error(error instanceof Error ? error.message : String(error));
|
|
790
806
|
process.exitCode = 1;
|