iranti-control-plane 0.5.3 → 0.5.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 +10 -8
- package/bin/iranti-cp.js +102 -1
- package/dist/server/bundle.cjs +163 -417
- package/package.json +1 -1
- package/public/control-plane/assets/{index-s2MmFd0w.css → index-CPcXaBi0.css} +1 -1
- package/public/control-plane/assets/{index-Ue3NshqF.js → index-VQDPvyqf.js} +14 -14
- package/public/control-plane/index.html +2 -2
package/README.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# Iranti Control Plane
|
|
2
2
|
|
|
3
|
-
Local-first operator dashboard for [Iranti](https://github.com/nfemmanuel/iranti)
|
|
3
|
+
Local-first operator dashboard for [Iranti](https://github.com/nfemmanuel/iranti) — inspect memory, watch Staff activity, manage instances, and diagnose your setup without raw SQL.
|
|
4
4
|
|
|
5
5
|
## Status
|
|
6
6
|
|
|
7
|
-
Current package version: `0.
|
|
8
|
-
The operator surface is live and under active
|
|
7
|
+
Current package version: `0.5.5`.
|
|
8
|
+
The operator surface is live and under active development.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Available on npm and mirrored by jsDelivr:
|
|
11
|
+
- npm: `npm install -g iranti-control-plane`
|
|
12
|
+
- jsDelivr CDN: `https://cdn.jsdelivr.net/npm/iranti-control-plane/`
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
## Install
|
|
13
15
|
|
|
14
16
|
```bash
|
|
15
17
|
npm install -g iranti-control-plane
|
|
@@ -57,8 +59,8 @@ npm run migrate
|
|
|
57
59
|
npm run dev
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
Open http://localhost:5173 for the frontend dev server.
|
|
61
|
-
|
|
62
|
+
Open http://localhost:5173 for the frontend dev server.
|
|
63
|
+
|
|
62
64
|
### Port model
|
|
63
65
|
|
|
64
66
|
The control plane has two common local startup modes:
|
|
@@ -145,4 +147,4 @@ See `docs/specs/control-plane-api.md` for the full API spec and `docs/prd/contro
|
|
|
145
147
|
For release and manual publish checks, see [`docs/guides/releasing.md`](docs/guides/releasing.md).
|
|
146
148
|
|
|
147
149
|
If npm publish is run from GitHub Actions, the repo `NPM_TOKEN` secret must be an npm **Automation token**. A standard token that still requires OTP will fail with `EOTP`.
|
|
148
|
-
|
|
150
|
+
|
package/bin/iranti-cp.js
CHANGED
|
@@ -31,6 +31,9 @@ Usage:
|
|
|
31
31
|
iranti-cp doctor [iranti doctor args...]
|
|
32
32
|
iranti-cp upgrade [self]
|
|
33
33
|
iranti-cp upgrade iranti [iranti upgrade args...]
|
|
34
|
+
iranti-cp config [get]
|
|
35
|
+
iranti-cp config set port <n>
|
|
36
|
+
iranti-cp config unset port
|
|
34
37
|
|
|
35
38
|
Commands:
|
|
36
39
|
open Open an existing Control Plane if one is running, otherwise start it in the background.
|
|
@@ -41,9 +44,13 @@ Commands:
|
|
|
41
44
|
version Print the installed iranti-control-plane version.
|
|
42
45
|
doctor Proxy to "iranti doctor".
|
|
43
46
|
upgrade Upgrade iranti-control-plane itself, or proxy to "iranti upgrade" for core Iranti.
|
|
47
|
+
config Read or write persistent Control Plane configuration.
|
|
48
|
+
get Show current config.
|
|
49
|
+
set port <n> Set the default startup port (1024–65535).
|
|
50
|
+
unset port Clear the default port, reverting to the 3000–3010 auto-range.
|
|
44
51
|
|
|
45
52
|
Options:
|
|
46
|
-
--port <n> Prefer a specific Control Plane port for open/start/status.
|
|
53
|
+
--port <n> Prefer a specific Control Plane port for open/start/status (one-time override).
|
|
47
54
|
--json Emit machine-readable output for status.
|
|
48
55
|
-h, --help Show this help.
|
|
49
56
|
`);
|
|
@@ -86,6 +93,26 @@ function parseArgs(argv) {
|
|
|
86
93
|
return { help, port, json, positionals };
|
|
87
94
|
}
|
|
88
95
|
|
|
96
|
+
function getUserConfigPath() {
|
|
97
|
+
return path.join(require('os').homedir(), '.iranti-runtime', 'iranti-cp-config.json');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function readUserConfig() {
|
|
101
|
+
try {
|
|
102
|
+
const raw = fs.readFileSync(getUserConfigPath(), 'utf8');
|
|
103
|
+
const parsed = JSON.parse(raw);
|
|
104
|
+
return typeof parsed === 'object' && parsed !== null ? parsed : {};
|
|
105
|
+
} catch {
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function writeUserConfig(config) {
|
|
111
|
+
const configPath = getUserConfigPath();
|
|
112
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
113
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
114
|
+
}
|
|
115
|
+
|
|
89
116
|
function preferredPorts(explicitPort) {
|
|
90
117
|
const seen = new Set();
|
|
91
118
|
const ordered = [];
|
|
@@ -100,11 +127,64 @@ function preferredPorts(explicitPort) {
|
|
|
100
127
|
|
|
101
128
|
add(explicitPort);
|
|
102
129
|
add(process.env.CONTROL_PLANE_PORT);
|
|
130
|
+
add(readUserConfig().defaultPort);
|
|
103
131
|
for (let port = 3000; port <= 3010; port += 1) add(port);
|
|
104
132
|
add(3002);
|
|
105
133
|
return ordered;
|
|
106
134
|
}
|
|
107
135
|
|
|
136
|
+
async function handleConfig(subcommands) {
|
|
137
|
+
const sub = subcommands[0];
|
|
138
|
+
|
|
139
|
+
if (!sub || sub === 'get') {
|
|
140
|
+
const config = readUserConfig();
|
|
141
|
+
const portDisplay = config.defaultPort != null
|
|
142
|
+
? String(config.defaultPort)
|
|
143
|
+
: '(not set — uses 3000–3010 auto-range)';
|
|
144
|
+
console.log(`defaultPort: ${portDisplay}`);
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (sub === 'set') {
|
|
149
|
+
const key = subcommands[1];
|
|
150
|
+
const value = subcommands[2];
|
|
151
|
+
if (key === 'port') {
|
|
152
|
+
if (!value) {
|
|
153
|
+
console.error('iranti-cp config set port: missing port number');
|
|
154
|
+
return 1;
|
|
155
|
+
}
|
|
156
|
+
const parsed = Number.parseInt(value, 10);
|
|
157
|
+
if (!Number.isFinite(parsed) || parsed < 1024 || parsed > 65535) {
|
|
158
|
+
console.error(`iranti-cp config set port: "${value}" is not a valid port (1024–65535)`);
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
const config = readUserConfig();
|
|
162
|
+
config.defaultPort = parsed;
|
|
163
|
+
writeUserConfig(config);
|
|
164
|
+
console.log(`Default port set to ${parsed}. Takes effect on next iranti-cp start.`);
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
console.error(`iranti-cp config set: unknown key "${key !== undefined ? key : ''}". Supported: port`);
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (sub === 'unset') {
|
|
172
|
+
const key = subcommands[1];
|
|
173
|
+
if (key === 'port') {
|
|
174
|
+
const config = readUserConfig();
|
|
175
|
+
delete config.defaultPort;
|
|
176
|
+
writeUserConfig(config);
|
|
177
|
+
console.log('Default port cleared. Will use 3000–3010 auto-range on next start.');
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
console.error(`iranti-cp config unset: unknown key "${key !== undefined ? key : ''}". Supported: port`);
|
|
181
|
+
return 1;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
console.error(`iranti-cp config: unknown subcommand "${sub}". Use get, set, or unset.`);
|
|
185
|
+
return 1;
|
|
186
|
+
}
|
|
187
|
+
|
|
108
188
|
async function fetchJson(url, timeoutMs = 1500) {
|
|
109
189
|
const controller = new AbortController();
|
|
110
190
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -229,6 +309,25 @@ function spawnControlPlane({ port, openBrowser, detached }) {
|
|
|
229
309
|
if (port) env.CONTROL_PLANE_PORT = String(port);
|
|
230
310
|
if (!openBrowser) env.IRANTI_CP_NO_OPEN = '1';
|
|
231
311
|
|
|
312
|
+
// On Windows, spawning node.exe directly causes Windows Terminal (wt.exe)
|
|
313
|
+
// to intercept the new console process and flash a tab, even with
|
|
314
|
+
// windowsHide:true. windowsHide suppresses a standalone console window but
|
|
315
|
+
// does not prevent Windows Terminal from attaching.
|
|
316
|
+
//
|
|
317
|
+
// Routing through `cmd /c start "" /b node bundle.cjs` creates the child
|
|
318
|
+
// with the DETACHED_PROCESS flag implicitly, so no console is allocated and
|
|
319
|
+
// Windows Terminal never attaches. This is only needed for the detached
|
|
320
|
+
// (background) case; foreground start keeps stdio:inherit as usual.
|
|
321
|
+
if (process.platform === 'win32' && detached) {
|
|
322
|
+
const child = spawn(
|
|
323
|
+
'cmd',
|
|
324
|
+
['/d', '/s', '/c', 'start', '', '/b', process.execPath, BUNDLE],
|
|
325
|
+
{ env, detached: true, stdio: 'ignore', windowsHide: true },
|
|
326
|
+
);
|
|
327
|
+
child.unref();
|
|
328
|
+
return child;
|
|
329
|
+
}
|
|
330
|
+
|
|
232
331
|
const child = spawn(process.execPath, [BUNDLE], {
|
|
233
332
|
env,
|
|
234
333
|
stdio: detached ? 'ignore' : 'inherit',
|
|
@@ -527,6 +626,8 @@ async function main() {
|
|
|
527
626
|
exitCode = await runIrantiProxy(['doctor', ...rest]);
|
|
528
627
|
} else if (command === 'upgrade') {
|
|
529
628
|
exitCode = await handleUpgrade(rest[0] || 'self', rest.slice(1));
|
|
629
|
+
} else if (command === 'config') {
|
|
630
|
+
exitCode = await handleConfig(rest);
|
|
530
631
|
} else {
|
|
531
632
|
console.error(`iranti-cp: unknown command "${command}".`);
|
|
532
633
|
printHelp();
|