@tikoci/quickchr 0.5.1 → 0.5.3
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/.bun-version +1 -0
- package/DESIGN.md +39 -1
- package/MANUAL.md +97 -21
- package/README.md +7 -10
- package/package.json +2 -1
- package/src/cli/flags.ts +4 -1
- package/src/cli/index.ts +148 -14
- package/src/cli/wizard.ts +54 -58
- package/src/index.ts +1 -0
- package/src/lib/completions.ts +9 -6
- package/src/lib/lock.ts +1 -0
- package/src/lib/platform.ts +13 -1
- package/src/lib/qemu.ts +31 -4
- package/src/lib/quickchr.ts +179 -15
- package/src/lib/state.ts +1 -0
- package/src/lib/types.ts +48 -1
package/.bun-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.4.4
|
package/DESIGN.md
CHANGED
|
@@ -245,7 +245,9 @@ CI, integration and release all install, enforced by `scripts/check-bun-pin.ts`
|
|
|
245
245
|
runtime could change between two identical commits — and did: a six-week gap with no `ci.yml`
|
|
246
246
|
runs spanned the Bun 1.4.0 release, and the next PR inherited a 1.3.14 → 1.4.2 jump as if it
|
|
247
247
|
were its own regression. Anything that can turn the build red is a dependency and belongs in
|
|
248
|
-
a reviewable diff; "whatever is newest at run time" is not a version.
|
|
248
|
+
a reviewable diff; "whatever is newest at run time" is not a version. The same
|
|
249
|
+
`.bun-version` file ships in the npm package so `doctor` can name the tested
|
|
250
|
+
runtime and warn when the running Bun is older without duplicating the pin.
|
|
249
251
|
|
|
250
252
|
**The 2026-07 refactor (#29)** replaced the organically-grown scheme (integration on every
|
|
251
253
|
PR push, three parallel copies of the runner logic, `continue-on-error` green-washing)
|
|
@@ -1174,3 +1176,39 @@ before acting, and uses shared stopped-state cleanup without recursively taking
|
|
|
1174
1176
|
the non-reentrant stop lock. It confirms QEMU exit before clearing the PID or
|
|
1175
1177
|
releasing named-network membership. Lock wait, hard-stop escalation and final
|
|
1176
1178
|
exit verification are separate bounded phases outside the guest grace budget.
|
|
1179
|
+
|
|
1180
|
+
## Frozen boot + continue (#214)
|
|
1181
|
+
|
|
1182
|
+
Some lab workflows must touch the guest before RouterOS init runs. The driver:
|
|
1183
|
+
CHR's `:beep` speaker driver is skipped when CPUID reports a hypervisor, and
|
|
1184
|
+
patching the 2-byte gate in guest RAM via gdbstub before sys2 init unlocks
|
|
1185
|
+
PC-speaker capture for acoustic testing (verified on CHR 7.25, QEMU 11.1.2).
|
|
1186
|
+
`start --frozen` boots held at reset (`-S`) with a gdbstub; `quickchr continue`
|
|
1187
|
+
resumes. The RAM patch itself stays in the harness, not quickchr.
|
|
1188
|
+
|
|
1189
|
+
The gdbstub is a unix socket (`-gdb unix:<machineDir>/gdb.sock,server=on,
|
|
1190
|
+
wait=off`), not `-s` (TCP :1234): no port bookkeeping, no parallel-VM
|
|
1191
|
+
collisions. `wait=off` is required so the spawn never blocks waiting for a
|
|
1192
|
+
client. Measured: `info status` reads `paused (prelaunch)`; gdb attaches with
|
|
1193
|
+
the CPU at the reset vector `0xfff0`. POSIX-first by scope — a Windows TCP
|
|
1194
|
+
fallback (e.g. `portBase+9`) is parked as a follow-up, and the integration test
|
|
1195
|
+
is gated off win32 until it lands.
|
|
1196
|
+
|
|
1197
|
+
`frozen` is per-boot intent, like `--no-wait`, not persisted config: stopping
|
|
1198
|
+
clears the marker and a later plain `start` boots normally. `status` stays
|
|
1199
|
+
`"running"` while frozen — without a third status value — so `isMachineRunning()`,
|
|
1200
|
+
`stop`/`remove` (host-side SIGTERM works on frozen QEMU unchanged), and the
|
|
1201
|
+
`--all` filters keep working untouched. A never-continued frozen machine can
|
|
1202
|
+
never produce `BOOT_TIMEOUT`, by construction: the boot wait is skipped
|
|
1203
|
+
entirely, and `start` returns after the spawn early-exit check.
|
|
1204
|
+
|
|
1205
|
+
`continue` is one existing primitive — `monitorCommand("cont")` — plus the
|
|
1206
|
+
promise `start` makes: REST-ready by default, `--no-wait` to return
|
|
1207
|
+
immediately, `--timeout-extra` extending the resume wait like `start`. It
|
|
1208
|
+
checks `info status` first, so a guest gdb already resumed just clears the
|
|
1209
|
+
marker (idempotent); a QEMU-level `cont` error throws `PROCESS_FAILED` before
|
|
1210
|
+
the marker is touched (same `/^error/i` contract as savevm/loadvm); a dead pid
|
|
1211
|
+
is `MACHINE_STOPPED`; a wait that never reaches REST is a genuine
|
|
1212
|
+
`BOOT_TIMEOUT` with `preserve-existing` cleanup. Deliberately out: no frozen-specific hint on `exec`/`get` failures,
|
|
1213
|
+
and no `savevm` refusal on prelaunch VMs (snapshot is already tricky business;
|
|
1214
|
+
QEMU accepts or errors on its own).
|
package/MANUAL.md
CHANGED
|
@@ -64,7 +64,7 @@ by the user (or their AI agent) using `quickchr` as a building block.
|
|
|
64
64
|
| Bun ≥ 1.1 | runtime | macOS/Linux: `curl -fsSL https://bun.sh/install \| bash` · Windows: `powershell -c "irm bun.sh/install.ps1 \| iex"` (or `winget install Oven-sh.Bun`) |
|
|
65
65
|
| `qemu-system-x86_64` | x86 CHR | brew/apt/dnf/pacman · Windows: `winget install SoftwareFreedomConservancy.QEMU` |
|
|
66
66
|
| `qemu-system-aarch64` | arm64 CHR | brew/apt/dnf/pacman · Windows: bundled in the SFC QEMU package above |
|
|
67
|
-
| `qemu-img` | `--boot-size`, `--add-disk`, snapshots | included in all standard QEMU packages (brew, apt, dnf, SFC installer) |
|
|
67
|
+
| `qemu-img` | Default qcow2 creation, setup wizard, `--boot-size`, `--add-disk`, snapshots | included in all standard QEMU packages (brew, apt, dnf, SFC installer) |
|
|
68
68
|
| UEFI firmware (edk2) | arm64 CHR | `qemu-efi-aarch64` (apt), `edk2-aarch64` (dnf), bundled with `qemu` (brew) |
|
|
69
69
|
| `socket_vmnet` (optional) | rootless `shared`/`bridged` networking on macOS | `brew install socket_vmnet` |
|
|
70
70
|
| `socat` (optional) | piping serial console externally | brew/apt |
|
|
@@ -116,7 +116,9 @@ The `--json` form emits `{ ok, checks, staleImages }`, where `checks` is an
|
|
|
116
116
|
array of `{ label, status, detail }` and `staleImages` lists cached images older
|
|
117
117
|
than the current long-term release. The exit code still reflects `ok`.
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
`doctor` reports the tested Bun pin and warns when the running runtime is older.
|
|
120
|
+
|
|
121
|
+

|
|
120
122
|
|
|
121
123
|
Use this whenever a `start` fails — the diagnostic almost always points
|
|
122
124
|
at a missing dependency, missing firmware, or insufficient disk.
|
|
@@ -125,7 +127,7 @@ at a missing dependency, missing firmware, or insufficient disk.
|
|
|
125
127
|
|
|
126
128
|
## 3. CLI command reference
|
|
127
129
|
|
|
128
|
-
The dispatcher lives in `src/cli/index.ts
|
|
130
|
+
The dispatcher lives in `src/cli/index.ts`. Help text is
|
|
129
131
|
available for every command via `quickchr help <command>`.
|
|
130
132
|
|
|
131
133
|
### Lifecycle
|
|
@@ -138,14 +140,14 @@ directory, downloads the RouterOS image (cached), and writes
|
|
|
138
140
|
|
|
139
141
|
| Flag | Default | Notes |
|
|
140
142
|
|---|---|---|
|
|
141
|
-
| `--name <s>` | auto | `
|
|
143
|
+
| `--name <s>` | auto | `version-arch-N` if omitted |
|
|
142
144
|
| `--version <ver>` | latest in channel | e.g., `7.22.1` |
|
|
143
145
|
| `--channel <ch>` | `stable` | `stable`, `long-term`, `testing`, `development` |
|
|
144
146
|
| `--arch <a>` | host native | `arm64`, `x86`, or `auto` |
|
|
145
147
|
| `--cpu <n>` | `1` | vCPU count |
|
|
146
148
|
| `--mem <mb>` | `512` | RAM (cross-arch TCG bumps to `1024`) |
|
|
147
149
|
| `--boot-disk-format <f>` | `qcow2` | `qcow2` or `raw` |
|
|
148
|
-
| `--boot-size <size>` | — | Resize boot disk; needs `qemu-img`;
|
|
150
|
+
| `--boot-size <size>` | — | Resize qcow2 boot disk; needs `qemu-img`; explicit raw is refused |
|
|
149
151
|
| `--add-disk <size>` | — | Extra blank qcow2 disk; repeatable |
|
|
150
152
|
| `--forward <spec>` | — | Extra hostfwd port or built-in service host-port pin; repeatable. See §Custom port forwards. |
|
|
151
153
|
| `--add-package <pkg>` | — | Provisioning, repeatable, ≥ 7.20.8 |
|
|
@@ -165,6 +167,7 @@ directory, downloads the RouterOS image (cached), and writes
|
|
|
165
167
|
| `--no-winbox` | false | Exclude WinBox port |
|
|
166
168
|
| `--no-api-ssl` | false | Exclude API-SSL port |
|
|
167
169
|
| `--port-base <n>` | auto from 9100 | First port of the 10-port block |
|
|
170
|
+
| `--qemu-arg <arg>` | — | Extra QEMU argv element, repeatable (space form only). Recorded and reused every boot. See Extra QEMU arguments below. |
|
|
168
171
|
|
|
169
172
|
`--vmnet-shared` and `--vmnet-bridge <iface>` have been removed. Use
|
|
170
173
|
`--add-network shared` and `--add-network bridged:<iface>`; either old spelling is
|
|
@@ -192,7 +195,9 @@ Additional flags:
|
|
|
192
195
|
| `--fg` / `--foreground` (alias `--no-bg`, `--no-background`) | Serial console on stdio |
|
|
193
196
|
| `--bg` / `--background` | **Deprecated, does nothing.** Background is the default, and the flag never made `start` return early — it only ever chose where the serial console went. Prints a warning; use `--no-wait` |
|
|
194
197
|
| `--timeout-extra <s>` / `-T <s>` | Add extra seconds to computed boot timeout |
|
|
195
|
-
| `--dry-run` | Print resolved options and QEMU
|
|
198
|
+
| `--dry-run` | Print the resolved start options as JSON (including `qemuArgs`/`frozen`) and exit — not the final QEMU argv |
|
|
199
|
+
| `--qemu-arg <arg>` | Extra QEMU argv element, repeatable (space form only). Recorded when creating; on an existing machine it replaces recorded args for this boot only. Refused with `--all`. |
|
|
200
|
+
| `--frozen` | Boot frozen at reset (`-S`) with a gdbstub; resume with `continue`. Refused with `--fg`, provisioning, and `--all` |
|
|
196
201
|
|
|
197
202
|
After `--no-wait`, readiness is yours. centrs (0.1.8+) waits for it in one command:
|
|
198
203
|
|
|
@@ -216,6 +221,62 @@ When `--fg` is combined with provisioning options, quickchr boots
|
|
|
216
221
|
foreground with stdio attached. This is observable as a brief stop in
|
|
217
222
|
machine state.
|
|
218
223
|
|
|
224
|
+
**Extra QEMU arguments (`--qemu-arg`).** One argv element per occurrence,
|
|
225
|
+
appended verbatim (no splitting, no validation) after quickchr's own args.
|
|
226
|
+
Use the space form: `--qemu-arg=x` overwrites instead of accumulating, so
|
|
227
|
+
mixing space and `=` forms keeps only the `=` values. Values beginning with
|
|
228
|
+
`--` need the `--qemu-arg=<arg>` form (one element per flag).
|
|
229
|
+
|
|
230
|
+
Passthrough runs with your privileges and is replayed every boot: it can
|
|
231
|
+
touch any path you can (`-drive`, `-audiodev`) or open QEMU-side listeners,
|
|
232
|
+
so treat recorded args like config you own. A bad recorded arg fails every
|
|
233
|
+
start; recover with a session override (`start <name> --qemu-arg …`) and then
|
|
234
|
+
persist the fix with `set <name> --qemu-arg …` (or `--clear-qemu-args`) while
|
|
235
|
+
stopped.
|
|
236
|
+
Ordering is load-bearing — passthrough goes last, so a conflicting option
|
|
237
|
+
resolves last-wins (a duplicate `-M pc,...` merges over quickchr's `-M pc`;
|
|
238
|
+
measured on QEMU 11.1.2). A bad element surfaces as `SPAWN_FAILED` with the
|
|
239
|
+
QEMU log tail. Lab escape hatch for QEMU surface quickchr does not model —
|
|
240
|
+
e.g. capturing the CHR PC speaker (x86 only) to a WAV file:
|
|
241
|
+
|
|
242
|
+
```sh
|
|
243
|
+
quickchr start beep-lab --version 7.25 --arch x86 \
|
|
244
|
+
--qemu-arg -M --qemu-arg pc,pcspk-audiodev=cap \
|
|
245
|
+
--qemu-arg -audiodev --qemu-arg wav,id=cap,path=$PWD/cap.wav
|
|
246
|
+
# cap.wav is created at spawn (0 bytes while running); QEMU flushes the
|
|
247
|
+
# 44-byte RIFF header on exit. Capturing actual beeps also needs the
|
|
248
|
+
# --frozen RAM-patch workflow below: stock CHR skips :beep under a
|
|
249
|
+
# hypervisor, so an unpatched guest never emits audio.
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### `continue <name> [--no-wait] [--timeout-extra <s>]`
|
|
253
|
+
|
|
254
|
+
Resume a machine started with `start --frozen` (held at reset with a
|
|
255
|
+
gdbstub). Sends one monitor `cont`, then waits for RouterOS to become
|
|
256
|
+
ready — the same promise `start` makes. `--no-wait` returns right after
|
|
257
|
+
resuming; `--timeout-extra`/`-T` extends the resume timeout like `start`. Idempotent: when the guest is already running (e.g. gdb
|
|
258
|
+
continued it), this just clears the frozen marker. A machine that never
|
|
259
|
+
reaches readiness is a `BOOT_TIMEOUT`; it is stopped and preserved.
|
|
260
|
+
|
|
261
|
+
`--frozen` exists for harnesses that must touch the guest before RouterOS
|
|
262
|
+
init runs — e.g. patching guest RAM via gdb, then resuming:
|
|
263
|
+
|
|
264
|
+
```sh
|
|
265
|
+
quickchr start beep-lab --version 7.25 --arch x86 --frozen
|
|
266
|
+
# frozen at reset; gdb at <machineDir>/gdb.sock (see 'quickchr list beep-lab')
|
|
267
|
+
gdb -ex "target remote /path/to/machines/beep-lab/gdb.sock" \
|
|
268
|
+
-ex "set *(char *)0xADDR = 0x.." -ex detach -ex quit
|
|
269
|
+
quickchr continue beep-lab
|
|
270
|
+
# resumed, REST-ready
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Frozen is per-boot intent, not config: `stop` clears it and a later plain
|
|
274
|
+
`start` boots normally. `list` marks frozen machines (`(frozen)` in the
|
|
275
|
+
table, a `Frozen:` line plus the gdb socket path in the detail view).
|
|
276
|
+
Note the vocabulary overlap: this `frozen` (QEMU held at reset) is a
|
|
277
|
+
different domain from QGA `fsfreeze-status` (guest filesystems frozen for
|
|
278
|
+
snapshots).
|
|
279
|
+
|
|
219
280
|
#### `stop [name | --all]`
|
|
220
281
|
|
|
221
282
|
Power cut: sends SIGTERM to QEMU, waits up to 5 seconds, then SIGKILL if
|
|
@@ -320,7 +381,10 @@ repeat the original command rather than to run a bare `start`.
|
|
|
320
381
|
|
|
321
382
|
Without a name: tabular summary of every machine. With a name: detailed
|
|
322
383
|
view including credentials and connection tips. `--json` for
|
|
323
|
-
machine-readable output.
|
|
384
|
+
machine-readable output. A machine held by `start --frozen` shows
|
|
385
|
+
`(frozen)` in the table and a `Frozen:` line with its gdb socket path in
|
|
386
|
+
the detail view; single-machine `--json` carries `frozen` (and the recorded
|
|
387
|
+
`qemuArgs`) alongside the other fields.
|
|
324
388
|
|
|
325
389
|
A machine whose `machine.json` cannot be read — unreadable on disk, not valid JSON, or
|
|
326
390
|
valid JSON that is not usable machine state — still appears, as a row marked
|
|
@@ -447,6 +511,8 @@ flags that were asked for so it can be run as given.
|
|
|
447
511
|
| `--device-mode <m>` | `rose`, `advanced`, `basic`, `home`, `auto`. |
|
|
448
512
|
| `--device-mode-enable <f,…>` | Set one or more device-mode flags to yes. Repeatable, comma-separated. |
|
|
449
513
|
| `--device-mode-disable <f,…>` | Set one or more to no. |
|
|
514
|
+
| `--qemu-arg <arg>` | Replace the recorded QEMU passthrough args (repeatable, space-form only). Stopped only; takes effect on the next start. |
|
|
515
|
+
| `--clear-qemu-args` | Drop the recorded QEMU passthrough args. Stopped only. |
|
|
450
516
|
|
|
451
517
|
**Device-mode restarts the machine.** RouterOS only applies a device-mode change
|
|
452
518
|
across a power cycle, and quickchr owns the power button — which is why this lives
|
|
@@ -486,7 +552,7 @@ Interactive wizard (`@clack/prompts`) that walks through all machine creation
|
|
|
486
552
|
options and starts the CHR. Triggered by `quickchr setup` or automatically
|
|
487
553
|
when `quickchr` is run with no subcommand on a TTY.
|
|
488
554
|
|
|
489
|
-

|
|
555
|
+

|
|
490
556
|
|
|
491
557
|
**First-run detection**: if no machines exist yet, the wizard opens with a
|
|
492
558
|
welcome message before the first prompt.
|
|
@@ -513,7 +579,10 @@ another machine, view status, or quit.
|
|
|
513
579
|
| 12 | Run mode: background / foreground | background | (default) / `--fg` |
|
|
514
580
|
| 13 | Final confirmation | — | — |
|
|
515
581
|
|
|
516
|
-
|
|
582
|
+
The wizard requires `qemu-img` and refuses before any prompts or image downloads
|
|
583
|
+
when it is absent, showing the install remedy. For a raw x86 boot without `qemu-img`,
|
|
584
|
+
use `quickchr add --arch x86 --boot-disk-format raw` to create a stopped machine,
|
|
585
|
+
then `quickchr start <name>` with the returned name to boot it. Step 10
|
|
517
586
|
only appears when the platform supports networks beyond user-mode (macOS or
|
|
518
587
|
Linux). The provisioning gate (step 11) only appears for RouterOS ≥ 7.20.8;
|
|
519
588
|
older versions show an upgrade prompt.
|
|
@@ -597,7 +666,7 @@ default output form when writing step outputs directly.
|
|
|
597
666
|
process that wrote it is alive (an extraction: for 30 minutes after its last
|
|
598
667
|
write). `prune` removes stale ones before anything else.
|
|
599
668
|
|
|
600
|
-
`cache list --json` prints
|
|
669
|
+
`cache list --json` prints an array of entry objects, including `kind` (`image`, `image-zip`,
|
|
601
670
|
`packages` or `partial`) and `paths` (every file deleting the entry removes).
|
|
602
671
|
Files in the cache that quickchr did not write are reported as "Other files"
|
|
603
672
|
and never deleted. `prune` and `clear` never delete an in-use entry.
|
|
@@ -690,7 +759,7 @@ mutate any machine's `machine.json`.
|
|
|
690
759
|
|
|
691
760
|
| Env var | Effect |
|
|
692
761
|
|---|---|
|
|
693
|
-
| `QUICKCHR_DATA_DIR` | Override the data root (defaults to
|
|
762
|
+
| `QUICKCHR_DATA_DIR` | Override the data root (defaults to `~/.local/share/quickchr` on POSIX or the Windows path — see §8) |
|
|
694
763
|
| `QUICKCHR_NO_PROMPT` | Force non-interactive mode (skips all wizards) |
|
|
695
764
|
| `QUICKCHR_DEBUG` | Emit `[debug]` lines from the progress logger |
|
|
696
765
|
| `QUICKCHR_INTEGRATION` | Required to run `test/integration/` |
|
|
@@ -814,7 +883,7 @@ throws `INVALID_NAME` for a name that is not a usable path segment.
|
|
|
814
883
|
|---|---|---|
|
|
815
884
|
| Identity | `name`, `state`, `ports`, `restUrl`, `sshPort`, `portBase` | Static metadata persisted in `machine.json` |
|
|
816
885
|
| Capture | `captureInterface`, `hostGatewayIp` | Platform-correct values for TZSP capture (`tshark -i …`) and the guest→host address any UDP emitter targets |
|
|
817
|
-
| Lifecycle | `waitForBoot()`, `waitFor(fn, ms?)`, `stop()`, `shutdown(opts?)`, `remove()`, `clean()`, `destroy()` | Boot readiness, custom polling, power-cut or guest-shutdown teardown |
|
|
886
|
+
| Lifecycle | `waitForBoot()`, `waitFor(fn, ms?)`, `stop()`, `shutdown(opts?)`, `continue(opts?)`, `remove()`, `clean()`, `destroy()` | Boot readiness, custom polling, power-cut or guest-shutdown teardown, frozen resume |
|
|
818
887
|
| Comms | `rest()`, `exec()`, `monitor()`, `serial()`, `qga()` | REST, RouterOS CLI, QEMU monitor, serial console, QGA (x86 only) |
|
|
819
888
|
| Provisioning | `license()`, `setDeviceMode()`, `availablePackages()`, `installPackage()` | Post-boot configuration |
|
|
820
889
|
| Files | `upload()`, `download()` | SCP transfer to/from the CHR |
|
|
@@ -836,6 +905,7 @@ interface ChrInstance {
|
|
|
836
905
|
waitFor(condition: () => Promise<boolean>, timeoutMs?: number): Promise<boolean>;
|
|
837
906
|
stop(): Promise<void>; // power cut: SIGTERM, then SIGKILL
|
|
838
907
|
shutdown(opts?: ShutdownOptions): Promise<ShutdownResult>; // /system/shutdown, bounded fallback
|
|
908
|
+
continue(opts?: ContinueOptions): Promise<void>; // resume a frozen start, REST-ready by default
|
|
839
909
|
remove(): Promise<void>;
|
|
840
910
|
clean(): Promise<void>;
|
|
841
911
|
destroy(): Promise<void>; // stop + remove
|
|
@@ -922,6 +992,8 @@ redact before logging or attaching to bug reports.
|
|
|
922
992
|
| `installDeps` | boolean | — | **Deprecated, ignored.** Never installed anything; run `quickchr doctor` for install hints |
|
|
923
993
|
| `dryRun` | boolean | `false` | Print QEMU command and exit |
|
|
924
994
|
| `timeoutExtra` | number | `0` | Extra **milliseconds** added to boot timeout |
|
|
995
|
+
| `qemuArgs` | string[] | — | Extra QEMU argv elements, one entry each, appended verbatim last. Recorded by `add()`; a `start()` value replaces stored args for that boot only |
|
|
996
|
+
| `frozen` | boolean | `false` | Boot frozen at reset with a gdbstub; resume with `continue()`. Refused with provisioning or `background: false` |
|
|
925
997
|
| `onProgress` | `(msg) => void` | `console.log` | `[debug]` lines only when `QUICKCHR_DEBUG=1` |
|
|
926
998
|
|
|
927
999
|
Provisioning fields trigger a version check at the top of `start()` /
|
|
@@ -1257,10 +1329,10 @@ restrictions, half-open SLiRP under TCG), see `docs/networking.md`.
|
|
|
1257
1329
|
|
|
1258
1330
|
## 8. Storage layout
|
|
1259
1331
|
|
|
1260
|
-
### macOS / Linux
|
|
1332
|
+
### macOS / Linux
|
|
1261
1333
|
|
|
1262
1334
|
```text
|
|
1263
|
-
|
|
1335
|
+
~/.local/share/quickchr/
|
|
1264
1336
|
├── .allocate.lock/ held while a new machine picks its ports/MACs
|
|
1265
1337
|
├── cache/ Downloaded RouterOS images
|
|
1266
1338
|
│ ├── chr-7.22.1.img.zip
|
|
@@ -1283,8 +1355,8 @@ $XDG_DATA_HOME/quickchr/ (defaults to ~/.local/share/quickchr/)
|
|
|
1283
1355
|
└── <name>.json
|
|
1284
1356
|
```
|
|
1285
1357
|
|
|
1286
|
-
|
|
1287
|
-
whole tree with `QUICKCHR_DATA_DIR=/some/path`.
|
|
1358
|
+
The data root is `~/.local/share/quickchr/`; `XDG_DATA_HOME` does not
|
|
1359
|
+
change it. Override the whole tree with `QUICKCHR_DATA_DIR=/some/path`.
|
|
1288
1360
|
|
|
1289
1361
|
**Global settings** live separately, under the XDG **config** tier, not this
|
|
1290
1362
|
data tree: `~/.config/quickchr/quickchr.env` (dotenv-style). See §3's
|
|
@@ -1581,12 +1653,16 @@ ceiling.
|
|
|
1581
1653
|
|
|
1582
1654
|
### Boot disk
|
|
1583
1655
|
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1656
|
+
The CLI and wizard default to **qcow2**, converted from the raw MikroTik
|
|
1657
|
+
image before first boot. Creating qcow2 requires `qemu-img` even without resize.
|
|
1658
|
+
`--boot-size` resizes qcow2 to the requested size; explicitly selecting raw
|
|
1659
|
+
with a boot size is refused. Without `--boot-size`, qcow2 keeps the original
|
|
1660
|
+
image size and enables snapshots.
|
|
1587
1661
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1662
|
+
Library callers default to **raw** unless `bootSize` implies qcow2. CLI users
|
|
1663
|
+
can select raw explicitly with `--boot-disk-format raw`. On x86, this permits
|
|
1664
|
+
creation without `qemu-img` when no resize or extra disks are requested. ARM64
|
|
1665
|
+
also needs `qemu-img` for its writable UEFI firmware disk.
|
|
1590
1666
|
|
|
1591
1667
|
### Extra disks
|
|
1592
1668
|
|
package/README.md
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
# quickchr
|
|
2
2
|
|
|
3
|
-
> **Experimental** — quickchr is under active development. Most testing has been on
|
|
4
|
-
> macOS (Apple Silicon + Intel), with CI on Linux (x86_64 + aarch64). Windows support
|
|
5
|
-
> is early-stage: unit tests pass on `windows-latest` CI, and a weekly sweep boots a real
|
|
6
|
-
> CHR on Windows (TCG, anchor smoke subset only). Expect rough edges on Windows — bug
|
|
7
|
-
> reports welcome.
|
|
8
|
-
|
|
9
3
|
CLI and library to download, launch, and manage MikroTik CHR virtual machines via QEMU.
|
|
10
4
|
|
|
11
5
|
## Quick Start
|
|
@@ -14,7 +8,7 @@ CLI and library to download, launch, and manage MikroTik CHR virtual machines vi
|
|
|
14
8
|
|
|
15
9
|
- [Bun](https://bun.sh) runtime
|
|
16
10
|
- [QEMU](https://www.qemu.org) with `qemu-system-x86_64` and/or `qemu-system-aarch64`
|
|
17
|
-
- `qemu-img` for
|
|
11
|
+
- `qemu-img` for default qcow2 creation, the setup wizard, disk resize, extra disks, and snapshots
|
|
18
12
|
- UEFI firmware (edk2) for arm64 CHR
|
|
19
13
|
|
|
20
14
|
Install QEMU:
|
|
@@ -60,7 +54,7 @@ bun add @tikoci/quickchr
|
|
|
60
54
|
### Prerelease QA (0.5.1)
|
|
61
55
|
|
|
62
56
|
0.5.1 is published to npm `next` first; the unqualified install above uses `latest`,
|
|
63
|
-
and 0.5.1 is promoted there once QA passes.
|
|
57
|
+
and 0.5.1 is promoted there once QA passes. Pin this published candidate for
|
|
64
58
|
reproducible CLI or library QA:
|
|
65
59
|
|
|
66
60
|
```bash
|
|
@@ -77,7 +71,8 @@ The [0.5.0 notes](./CHANGELOG.md#050--2026-10-22) cover migration from 0.4.x.
|
|
|
77
71
|
### CLI Usage
|
|
78
72
|
|
|
79
73
|
The easiest way to start is the interactive wizard — it walks through
|
|
80
|
-
every option and starts the CHR for you
|
|
74
|
+
every option and starts the CHR for you. It requires `qemu-img` and reports
|
|
75
|
+
a missing tool before prompting or downloading:
|
|
81
76
|
|
|
82
77
|
```bash
|
|
83
78
|
quickchr setup
|
|
@@ -201,6 +196,7 @@ reports a forced power cut if the guest does not stop within the grace budget.
|
|
|
201
196
|
| `--install-all-packages` | Install all packages from `all_packages.zip` | |
|
|
202
197
|
| `--add-user <user:pass>` | Create a user after boot | |
|
|
203
198
|
| `--disable-admin` | Disable the default admin account | |
|
|
199
|
+
| `--secure-login` | Create a managed login; use `--disable-admin` to disable admin | |
|
|
204
200
|
| `--no-secure-login` | Keep admin with no password (skip managed account creation) | |
|
|
205
201
|
| `--add-network <spec>` | Add a network NIC (repeatable). Specs: `user`, `shared`, `bridged:<iface>`, `socket::<name>`, `tap:<iface>`. Default: single user NIC. | |
|
|
206
202
|
| `--no-network` | Start with no NICs (headless) | |
|
|
@@ -260,7 +256,8 @@ In **foreground** mode, your terminal becomes the CHR serial console. Use these
|
|
|
260
256
|
|
|
261
257
|
### Disk Support
|
|
262
258
|
|
|
263
|
-
-
|
|
259
|
+
- CLI and wizard boot disks default to qcow2, converted from the raw MikroTik image using `qemu-img`. Library callers default to raw unless `bootSize` implies qcow2.
|
|
260
|
+
- `--boot-size` resizes qcow2 before first boot; explicitly combining raw with a boot size is refused.
|
|
264
261
|
- Extra disks from `--add-disk` are always created as blank qcow2 images.
|
|
265
262
|
- `quickchr clean <name>` removes any resized boot disk and extra disks, then recreates them from the saved machine config.
|
|
266
263
|
- `quickchr disk <name>` shows the stored disk layout. If `qemu-img` is installed, it also shows virtual and actual sizes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tikoci/quickchr",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "CLI and library to download, launch, and manage MikroTik CHR virtual machines via QEMU",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
|
+
".bun-version",
|
|
18
19
|
"src",
|
|
19
20
|
"README.md",
|
|
20
21
|
"DESIGN.md",
|
package/src/cli/flags.ts
CHANGED
|
@@ -20,12 +20,13 @@ const CREATE_FLAGS = [
|
|
|
20
20
|
"boot-disk-format", "boot-size", "add-disk",
|
|
21
21
|
"device-mode", "device-mode-enable", "device-mode-disable",
|
|
22
22
|
"add-user", "disable-admin", "secure-login",
|
|
23
|
+
"qemu-arg",
|
|
23
24
|
] as const;
|
|
24
25
|
|
|
25
26
|
/** `start` also boots, so it owns the run-time flags on top of the create set. */
|
|
26
27
|
const START_ONLY_FLAGS = [
|
|
27
28
|
"all", "dry-run", "wait", "background", "bg", "fg", "foreground",
|
|
28
|
-
"timeout-extra",
|
|
29
|
+
"timeout-extra", "frozen",
|
|
29
30
|
"license-level", "license-account", "license-password",
|
|
30
31
|
] as const;
|
|
31
32
|
|
|
@@ -40,6 +41,7 @@ const START_ONLY_FLAGS = [
|
|
|
40
41
|
const SET_ONLY_FLAGS = [
|
|
41
42
|
"name", "license", "level", "account", "password",
|
|
42
43
|
"device-mode", "device-mode-enable", "device-mode-disable",
|
|
44
|
+
"qemu-arg", "clear-qemu-args",
|
|
43
45
|
] as const;
|
|
44
46
|
|
|
45
47
|
export const ADD_FLAGS: readonly string[] = CREATE_FLAGS;
|
|
@@ -78,6 +80,7 @@ export const VALUE_FLAGS: ReadonlySet<string> = new Set([
|
|
|
78
80
|
"device-mode", "device-mode-enable", "device-mode-disable",
|
|
79
81
|
"add-user", "timeout-extra",
|
|
80
82
|
"license-level", "license-account", "license-password",
|
|
83
|
+
"qemu-arg",
|
|
81
84
|
// exec / qga / console
|
|
82
85
|
"via", "user", "password", "timeout", "path", "data", "script",
|
|
83
86
|
// set --license
|