@ghalex/kvbox 0.0.0-stage → 0.0.2
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/dist/kvbox.js +382 -0
- package/dist/kvbox.js.map +1 -0
- package/package.json +43 -4
- package/README.md +0 -3
package/dist/kvbox.js
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/output.ts
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
var color = pc.createColors(process.stdout.isTTY === true && !process.env["NO_COLOR"]);
|
|
6
|
+
function json(value) {
|
|
7
|
+
process.stdout.write(JSON.stringify(value, null, 2) + "\n");
|
|
8
|
+
}
|
|
9
|
+
function info(msg) {
|
|
10
|
+
process.stderr.write(msg + "\n");
|
|
11
|
+
}
|
|
12
|
+
function notImplemented(command) {
|
|
13
|
+
process.stderr.write(color.yellow(`kvbox ${command}: not implemented yet
|
|
14
|
+
`));
|
|
15
|
+
process.exit(2);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/program.ts
|
|
19
|
+
import { Command as Command14 } from "commander";
|
|
20
|
+
|
|
21
|
+
// ../shared/src/version.ts
|
|
22
|
+
import { createRequire } from "module";
|
|
23
|
+
function versionOf(srcFileUrl, injected) {
|
|
24
|
+
if (typeof injected === "string") return injected;
|
|
25
|
+
try {
|
|
26
|
+
const require2 = createRequire(srcFileUrl);
|
|
27
|
+
return require2("../package.json").version;
|
|
28
|
+
} catch {
|
|
29
|
+
return "0.0.0-dev";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/version.ts
|
|
34
|
+
var VERSION = versionOf(import.meta.url, true ? "0.0.2" : void 0);
|
|
35
|
+
|
|
36
|
+
// src/commands/machine/index.ts
|
|
37
|
+
import { Command as Command5 } from "commander";
|
|
38
|
+
|
|
39
|
+
// src/commands/machine/connect.ts
|
|
40
|
+
import { Command } from "commander";
|
|
41
|
+
var connectCommand = new Command("connect").description("open an interactive shell in a box over SSH").argument("<name>", "box name").action(() => notImplemented("machine connect"));
|
|
42
|
+
|
|
43
|
+
// src/commands/machine/ls.ts
|
|
44
|
+
import { Command as Command2 } from "commander";
|
|
45
|
+
var lsCommand = new Command2("ls").description("list boxes with status and IP").option("--json", "print as JSON").action(() => notImplemented("machine ls"));
|
|
46
|
+
|
|
47
|
+
// src/commands/machine/new.ts
|
|
48
|
+
import { Command as Command3, Option } from "commander";
|
|
49
|
+
|
|
50
|
+
// ../shared/src/sizes.ts
|
|
51
|
+
var SIZES = {
|
|
52
|
+
s: { vcpu: 2, memMib: 4096 },
|
|
53
|
+
m: { vcpu: 4, memMib: 8192 },
|
|
54
|
+
l: { vcpu: 8, memMib: 16384 }
|
|
55
|
+
};
|
|
56
|
+
var SIZE_NAMES = Object.keys(SIZES);
|
|
57
|
+
var DEFAULT_SIZE = "s";
|
|
58
|
+
|
|
59
|
+
// src/commands/machine/new.ts
|
|
60
|
+
var newCommand = new Command3("new").description("boot a new box from the base image or a snapshot").argument("[name]", "box name (generated when omitted)").addOption(new Option("-s, --size <size>", "box size").choices(SIZE_NAMES).default(DEFAULT_SIZE)).option("-f, --from <snapshot>", "start from a snapshot instead of the base image").option("--force", "skip the capacity guard").option("--json", "print the box as JSON").action(() => notImplemented("machine new"));
|
|
61
|
+
|
|
62
|
+
// src/commands/machine/rm.ts
|
|
63
|
+
import { Command as Command4 } from "commander";
|
|
64
|
+
var rmCommand = new Command4("rm").description("destroy a box").argument("<name>", "box name").option("-y, --yes", "do not ask for confirmation").action(() => notImplemented("machine rm"));
|
|
65
|
+
|
|
66
|
+
// src/commands/machine/index.ts
|
|
67
|
+
var machineCommand = new Command5("machine").alias("m").description("manage boxes").addCommand(newCommand).addCommand(lsCommand).addCommand(connectCommand).addCommand(rmCommand);
|
|
68
|
+
var machineAliases = [newCommand, lsCommand, connectCommand, rmCommand];
|
|
69
|
+
|
|
70
|
+
// src/commands/server/index.ts
|
|
71
|
+
import { Command as Command9 } from "commander";
|
|
72
|
+
|
|
73
|
+
// src/commands/server/start.ts
|
|
74
|
+
import { Command as Command6 } from "commander";
|
|
75
|
+
|
|
76
|
+
// ../shared/src/paths.ts
|
|
77
|
+
import { homedir } from "os";
|
|
78
|
+
import { join } from "path";
|
|
79
|
+
function kvboxHome() {
|
|
80
|
+
return process.env["KVBOX_HOME"] ?? join(homedir(), ".kvbox");
|
|
81
|
+
}
|
|
82
|
+
function paths(home = kvboxHome()) {
|
|
83
|
+
return {
|
|
84
|
+
home,
|
|
85
|
+
config: join(home, "config.json"),
|
|
86
|
+
serverSock: join(home, "server.sock"),
|
|
87
|
+
serverPid: join(home, "server.pid"),
|
|
88
|
+
serverLog: join(home, "server.log"),
|
|
89
|
+
hosts: join(home, "hosts.json"),
|
|
90
|
+
ssh: join(home, "ssh"),
|
|
91
|
+
sshKey: join(home, "ssh", "id_ed25519"),
|
|
92
|
+
bin: join(home, "bin"),
|
|
93
|
+
firecracker: join(home, "bin", "firecracker"),
|
|
94
|
+
assets: join(home, "assets"),
|
|
95
|
+
kernel: join(home, "assets", "vmlinux"),
|
|
96
|
+
rootfs: join(home, "assets", "rootfs.ext4"),
|
|
97
|
+
machines: join(home, "machines"),
|
|
98
|
+
snapshots: join(home, "snapshots"),
|
|
99
|
+
machine: (name) => ({
|
|
100
|
+
dir: join(home, "machines", name),
|
|
101
|
+
meta: join(home, "machines", name, "meta.json"),
|
|
102
|
+
disk: join(home, "machines", name, "disk.ext4"),
|
|
103
|
+
sock: join(home, "machines", name, "fc.sock"),
|
|
104
|
+
log: join(home, "machines", name, "fc.log")
|
|
105
|
+
}),
|
|
106
|
+
snapshot: (name) => ({
|
|
107
|
+
dir: join(home, "snapshots", name),
|
|
108
|
+
meta: join(home, "snapshots", name, "meta.json"),
|
|
109
|
+
vmstate: join(home, "snapshots", name, "vmstate"),
|
|
110
|
+
mem: join(home, "snapshots", name, "mem"),
|
|
111
|
+
disk: join(home, "snapshots", name, "disk.ext4")
|
|
112
|
+
})
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/client.ts
|
|
117
|
+
import { spawn } from "child_process";
|
|
118
|
+
import { openSync } from "fs";
|
|
119
|
+
import { mkdir, stat } from "fs/promises";
|
|
120
|
+
import { setTimeout as sleep } from "timers/promises";
|
|
121
|
+
import { Agent, request } from "undici";
|
|
122
|
+
import { z as z2 } from "zod";
|
|
123
|
+
|
|
124
|
+
// ../shared/src/api.ts
|
|
125
|
+
import { z } from "zod";
|
|
126
|
+
var SizeSchema = z.enum(SIZE_NAMES);
|
|
127
|
+
var MachineSchema = z.object({
|
|
128
|
+
name: z.string(),
|
|
129
|
+
ip: z.string(),
|
|
130
|
+
mac: z.string(),
|
|
131
|
+
pid: z.number().int().nullable(),
|
|
132
|
+
vcpu: z.number().int(),
|
|
133
|
+
memMib: z.number().int(),
|
|
134
|
+
createdAt: z.string(),
|
|
135
|
+
source: z.string(),
|
|
136
|
+
// "base" or "snapshot:<name>"
|
|
137
|
+
status: z.enum(["running", "stopped"])
|
|
138
|
+
});
|
|
139
|
+
var CreateMachineRequest = z.object({
|
|
140
|
+
name: z.string().optional(),
|
|
141
|
+
size: SizeSchema.optional(),
|
|
142
|
+
from: z.string().optional(),
|
|
143
|
+
force: z.boolean().optional()
|
|
144
|
+
});
|
|
145
|
+
var SnapshotSchema = z.object({
|
|
146
|
+
name: z.string(),
|
|
147
|
+
source: z.string(),
|
|
148
|
+
vcpu: z.number().int(),
|
|
149
|
+
memMib: z.number().int(),
|
|
150
|
+
createdAt: z.string()
|
|
151
|
+
});
|
|
152
|
+
var CreateSnapshotRequest = z.object({
|
|
153
|
+
box: z.string(),
|
|
154
|
+
name: z.string()
|
|
155
|
+
});
|
|
156
|
+
var CapacitySchema = z.object({
|
|
157
|
+
hostMemMib: z.number().int(),
|
|
158
|
+
hostCores: z.number().int(),
|
|
159
|
+
reservedMemMib: z.number().int(),
|
|
160
|
+
reservedVcpu: z.number().int(),
|
|
161
|
+
running: z.number().int()
|
|
162
|
+
});
|
|
163
|
+
var HealthSchema = z.object({
|
|
164
|
+
ok: z.literal(true),
|
|
165
|
+
version: z.string(),
|
|
166
|
+
pid: z.number().int()
|
|
167
|
+
});
|
|
168
|
+
var ApiErrorSchema = z.object({
|
|
169
|
+
error: z.string()
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// ../shared/src/errors.ts
|
|
173
|
+
var KvboxError = class extends Error {
|
|
174
|
+
constructor(message, exitCode = 1) {
|
|
175
|
+
super(message);
|
|
176
|
+
this.exitCode = exitCode;
|
|
177
|
+
this.name = "KvboxError";
|
|
178
|
+
}
|
|
179
|
+
exitCode;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/client.ts
|
|
183
|
+
function resolveTarget(hostFlag) {
|
|
184
|
+
if (hostFlag) {
|
|
185
|
+
throw new KvboxError(`--host ${hostFlag}: remote hosts arrive in M2`);
|
|
186
|
+
}
|
|
187
|
+
return { kind: "local", socketPath: paths().serverSock };
|
|
188
|
+
}
|
|
189
|
+
var ServerClient = class {
|
|
190
|
+
constructor(target) {
|
|
191
|
+
this.target = target;
|
|
192
|
+
if (target.kind === "local") {
|
|
193
|
+
this.dispatcher = new Agent({ connect: { socketPath: target.socketPath } });
|
|
194
|
+
this.base = "http://kvbox";
|
|
195
|
+
} else {
|
|
196
|
+
this.dispatcher = new Agent();
|
|
197
|
+
this.base = target.url;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
target;
|
|
201
|
+
dispatcher;
|
|
202
|
+
base;
|
|
203
|
+
async call(method, path, schema, body) {
|
|
204
|
+
let res;
|
|
205
|
+
try {
|
|
206
|
+
res = await request(this.base + path, {
|
|
207
|
+
dispatcher: this.dispatcher,
|
|
208
|
+
method,
|
|
209
|
+
headers: body === void 0 ? {} : { "content-type": "application/json" },
|
|
210
|
+
body: body === void 0 ? null : JSON.stringify(body)
|
|
211
|
+
});
|
|
212
|
+
} catch (err) {
|
|
213
|
+
const code = err.code ?? err.cause;
|
|
214
|
+
if (this.target.kind === "local") {
|
|
215
|
+
throw new KvboxError(`kvbox-server is not running (${String(code)}); start it with: kvbox server start`);
|
|
216
|
+
}
|
|
217
|
+
throw new KvboxError(`cannot reach ${this.target.name}: ${String(code)}`);
|
|
218
|
+
}
|
|
219
|
+
const text = await res.body.text();
|
|
220
|
+
if (res.statusCode >= 300) {
|
|
221
|
+
const parsed = ApiErrorSchema.safeParse(text ? JSON.parse(text) : {});
|
|
222
|
+
const msg = parsed.success ? parsed.data.error : `${res.statusCode} ${text}`;
|
|
223
|
+
throw new KvboxError(msg, res.statusCode === 501 ? 2 : 1);
|
|
224
|
+
}
|
|
225
|
+
return schema.parse(text ? JSON.parse(text) : void 0);
|
|
226
|
+
}
|
|
227
|
+
health() {
|
|
228
|
+
return this.call("GET", "/health", HealthSchema);
|
|
229
|
+
}
|
|
230
|
+
capacity() {
|
|
231
|
+
return this.call("GET", "/capacity", CapacitySchema);
|
|
232
|
+
}
|
|
233
|
+
listMachines() {
|
|
234
|
+
return this.call("GET", "/machines", z2.array(MachineSchema));
|
|
235
|
+
}
|
|
236
|
+
createMachine(req) {
|
|
237
|
+
return this.call("POST", "/machines", MachineSchema, req);
|
|
238
|
+
}
|
|
239
|
+
deleteMachine(name) {
|
|
240
|
+
return this.call("DELETE", `/machines/${encodeURIComponent(name)}`, z2.undefined());
|
|
241
|
+
}
|
|
242
|
+
listSnapshots() {
|
|
243
|
+
return this.call("GET", "/snapshots", z2.array(SnapshotSchema));
|
|
244
|
+
}
|
|
245
|
+
createSnapshot(req) {
|
|
246
|
+
return this.call("POST", "/snapshots", SnapshotSchema, req);
|
|
247
|
+
}
|
|
248
|
+
deleteSnapshot(name) {
|
|
249
|
+
return this.call("DELETE", `/snapshots/${encodeURIComponent(name)}`, z2.undefined());
|
|
250
|
+
}
|
|
251
|
+
async close() {
|
|
252
|
+
await this.dispatcher.close();
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
function clientFor(opts) {
|
|
256
|
+
return new ServerClient(resolveTarget(opts.host));
|
|
257
|
+
}
|
|
258
|
+
var NOT_A_HOST_HINT = "kvbox-server is not installed, so this machine is not a kvbox host. Set it up with install-server.sh, or point the CLI at a remote host (M2: kvbox host add).";
|
|
259
|
+
async function startLocalServer(p = paths(), timeoutMs = 5e3) {
|
|
260
|
+
await mkdir(p.home, { recursive: true });
|
|
261
|
+
const log = openSync(p.serverLog, "a");
|
|
262
|
+
const child = spawn("kvbox-server", ["start"], { detached: true, stdio: ["ignore", log, log] });
|
|
263
|
+
const failed = new Promise((_, reject) => {
|
|
264
|
+
child.once("error", (err) => {
|
|
265
|
+
reject(new KvboxError(err.code === "ENOENT" ? NOT_A_HOST_HINT : `could not start kvbox-server: ${err.message}`));
|
|
266
|
+
});
|
|
267
|
+
child.once("exit", (code) => {
|
|
268
|
+
reject(new KvboxError(`kvbox-server exited with code ${String(code)} before listening; see ${p.serverLog}`));
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
const ready = (async () => {
|
|
272
|
+
const deadline = Date.now() + timeoutMs;
|
|
273
|
+
while (Date.now() < deadline) {
|
|
274
|
+
if (await stat(p.serverSock).then(() => true, () => false)) {
|
|
275
|
+
const probe = new ServerClient({ kind: "local", socketPath: p.serverSock });
|
|
276
|
+
try {
|
|
277
|
+
await probe.health();
|
|
278
|
+
return;
|
|
279
|
+
} catch {
|
|
280
|
+
} finally {
|
|
281
|
+
await probe.close();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
await sleep(100);
|
|
285
|
+
}
|
|
286
|
+
throw new KvboxError(`kvbox-server did not start within ${timeoutMs / 1e3}s; see ${p.serverLog}`);
|
|
287
|
+
})();
|
|
288
|
+
await Promise.race([ready, failed]);
|
|
289
|
+
child.removeAllListeners("exit");
|
|
290
|
+
child.unref();
|
|
291
|
+
return { pid: child.pid ?? -1, socketPath: p.serverSock };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/commands/server/start.ts
|
|
295
|
+
var startCommand = new Command6("start").description("start the local kvbox-server in the background").action(async (_opts, cmd) => {
|
|
296
|
+
const client = clientFor(cmd.optsWithGlobals());
|
|
297
|
+
try {
|
|
298
|
+
const health = await client.health();
|
|
299
|
+
info(`kvbox-server ${health.version} is already running (pid ${health.pid})`);
|
|
300
|
+
return;
|
|
301
|
+
} catch {
|
|
302
|
+
} finally {
|
|
303
|
+
await client.close();
|
|
304
|
+
}
|
|
305
|
+
const { pid, socketPath } = await startLocalServer(paths());
|
|
306
|
+
info(`kvbox-server started (pid ${pid}), listening on ${socketPath}`);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// src/commands/server/status.ts
|
|
310
|
+
import { Command as Command7 } from "commander";
|
|
311
|
+
var statusCommand = new Command7("status").description("show whether the server is running").option("--json", "print as JSON").action(async (opts, cmd) => {
|
|
312
|
+
const client = clientFor(cmd.optsWithGlobals());
|
|
313
|
+
try {
|
|
314
|
+
const health = await client.health();
|
|
315
|
+
if (opts.json) json(health);
|
|
316
|
+
else info(`kvbox-server ${health.version} running, pid ${health.pid}`);
|
|
317
|
+
} finally {
|
|
318
|
+
await client.close();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// src/commands/server/stop.ts
|
|
323
|
+
import { readFile } from "fs/promises";
|
|
324
|
+
import { Command as Command8 } from "commander";
|
|
325
|
+
var stopCommand = new Command8("stop").description("stop the local server (running boxes are not affected)").action(async () => {
|
|
326
|
+
const p = paths();
|
|
327
|
+
let pid;
|
|
328
|
+
try {
|
|
329
|
+
pid = Number.parseInt(await readFile(p.serverPid, "utf8"), 10);
|
|
330
|
+
} catch {
|
|
331
|
+
throw new KvboxError("kvbox-server is not running (no pid file)");
|
|
332
|
+
}
|
|
333
|
+
try {
|
|
334
|
+
process.kill(pid, "SIGTERM");
|
|
335
|
+
} catch {
|
|
336
|
+
throw new KvboxError(`kvbox-server pid ${pid} is not running; remove ${p.serverPid}`);
|
|
337
|
+
}
|
|
338
|
+
info(`sent SIGTERM to kvbox-server (pid ${pid})`);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// src/commands/server/index.ts
|
|
342
|
+
var serverCommand = new Command9("server").description("run or manage the kvbox server").addCommand(startCommand).addCommand(statusCommand).addCommand(stopCommand);
|
|
343
|
+
|
|
344
|
+
// src/commands/snapshot/index.ts
|
|
345
|
+
import { Command as Command13 } from "commander";
|
|
346
|
+
|
|
347
|
+
// src/commands/snapshot/ls.ts
|
|
348
|
+
import { Command as Command10 } from "commander";
|
|
349
|
+
var lsCommand2 = new Command10("ls").description("list snapshots").option("--json", "print as JSON").action(() => notImplemented("snapshot ls"));
|
|
350
|
+
|
|
351
|
+
// src/commands/snapshot/rm.ts
|
|
352
|
+
import { Command as Command11 } from "commander";
|
|
353
|
+
var rmCommand2 = new Command11("rm").description("delete a snapshot").argument("<name>", "snapshot name").option("-y, --yes", "do not ask for confirmation").action(() => notImplemented("snapshot rm"));
|
|
354
|
+
|
|
355
|
+
// src/commands/snapshot/save.ts
|
|
356
|
+
import { Command as Command12 } from "commander";
|
|
357
|
+
var saveCommand = new Command12("save").description("freeze a running box (memory + disk) into a snapshot").argument("<box>", "box name").argument("<name>", "snapshot name").option("--json", "print the snapshot as JSON").action(() => notImplemented("snapshot save"));
|
|
358
|
+
|
|
359
|
+
// src/commands/snapshot/index.ts
|
|
360
|
+
var snapshotCommand = new Command13("snapshot").alias("snap").description("manage snapshots").addCommand(saveCommand).addCommand(lsCommand2).addCommand(rmCommand2);
|
|
361
|
+
|
|
362
|
+
// src/program.ts
|
|
363
|
+
function buildProgram() {
|
|
364
|
+
const program = new Command14("kvbox").description("self-hosted Firecracker microVMs with Docker-like ergonomics").version(VERSION, "-v, --version").option("--host <name>", "target a configured remote host (M2)").showHelpAfterError().enablePositionalOptions();
|
|
365
|
+
program.addCommand(machineCommand);
|
|
366
|
+
program.addCommand(snapshotCommand);
|
|
367
|
+
program.addCommand(serverCommand);
|
|
368
|
+
for (const cmd of machineAliases) program.addCommand(cmd);
|
|
369
|
+
return program;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/index.ts
|
|
373
|
+
try {
|
|
374
|
+
await buildProgram().parseAsync(process.argv);
|
|
375
|
+
} catch (err) {
|
|
376
|
+
if (err instanceof KvboxError) {
|
|
377
|
+
process.stderr.write(color.red(`error: ${err.message}`) + "\n");
|
|
378
|
+
process.exit(err.exitCode);
|
|
379
|
+
}
|
|
380
|
+
throw err;
|
|
381
|
+
}
|
|
382
|
+
//# sourceMappingURL=kvbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/output.ts","../src/program.ts","../../shared/src/version.ts","../src/version.ts","../src/commands/machine/index.ts","../src/commands/machine/connect.ts","../src/commands/machine/ls.ts","../src/commands/machine/new.ts","../../shared/src/sizes.ts","../src/commands/machine/rm.ts","../src/commands/server/index.ts","../src/commands/server/start.ts","../../shared/src/paths.ts","../src/client.ts","../../shared/src/api.ts","../../shared/src/errors.ts","../src/commands/server/status.ts","../src/commands/server/stop.ts","../src/commands/snapshot/index.ts","../src/commands/snapshot/ls.ts","../src/commands/snapshot/rm.ts","../src/commands/snapshot/save.ts","../src/index.ts"],"sourcesContent":["/** Printing: tables for humans, `--json` for agents, colors only on a TTY. */\nimport pc from \"picocolors\";\n\nexport const color = pc.createColors(process.stdout.isTTY === true && !process.env[\"NO_COLOR\"]);\n\nexport function json(value: unknown): void {\n process.stdout.write(JSON.stringify(value, null, 2) + \"\\n\");\n}\n\nexport function table(rows: Record<string, string | number | null | undefined>[], columns?: string[]): void {\n if (rows.length === 0) return;\n const cols = columns ?? Object.keys(rows[0]!);\n const cells = rows.map((r) => cols.map((c) => String(r[c] ?? \"\")));\n const widths = cols.map((c, i) => Math.max(c.length, ...cells.map((r) => r[i]!.length)));\n const line = (parts: string[]) => parts.map((p, i) => p.padEnd(widths[i]!)).join(\" \").trimEnd();\n process.stdout.write(color.dim(line(cols.map((c) => c.toUpperCase()))) + \"\\n\");\n for (const row of cells) process.stdout.write(line(row) + \"\\n\");\n}\n\nexport function info(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\nexport function warn(msg: string): void {\n process.stderr.write(color.yellow(msg) + \"\\n\");\n}\n\nexport function notImplemented(command: string): never {\n process.stderr.write(color.yellow(`kvbox ${command}: not implemented yet\\n`));\n process.exit(2);\n}\n\n/** y/N prompt on a TTY; requires `-y` otherwise (boxd's rule for destructive commands). */\nexport async function confirm(question: string, yes: boolean): Promise<boolean> {\n if (yes) return true;\n if (!process.stdin.isTTY) {\n warn(\"refusing without -y when stdin is not a terminal\");\n return false;\n }\n const { createInterface } = await import(\"node:readline/promises\");\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n try {\n const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();\n return answer === \"y\" || answer === \"yes\";\n } finally {\n rl.close();\n }\n}\n","/** Builds the commander tree. Importable without side effects (tests); src/index.ts runs it. */\nimport { Command } from \"commander\";\nimport { VERSION } from \"./version.js\";\nimport { machineAliases, machineCommand } from \"./commands/machine/index.js\";\nimport { serverCommand } from \"./commands/server/index.js\";\nimport { snapshotCommand } from \"./commands/snapshot/index.js\";\n\nexport function buildProgram(): Command {\n const program = new Command(\"kvbox\")\n .description(\"self-hosted Firecracker microVMs with Docker-like ergonomics\")\n .version(VERSION, \"-v, --version\")\n .option(\"--host <name>\", \"target a configured remote host (M2)\")\n .showHelpAfterError()\n .enablePositionalOptions();\n\n program.addCommand(machineCommand);\n program.addCommand(snapshotCommand);\n program.addCommand(serverCommand);\n // Top-level shortcuts: the same Command objects as under `machine`.\n for (const cmd of machineAliases) program.addCommand(cmd);\n\n return program;\n}\n","/**\n * Each binary's version comes from its own package.json: tsup injects\n * `__KVBOX_VERSION__` at build time; the dev path (tsx) reads the file.\n * Usage in a package: `versionOf(import.meta.url)` from its `src/version.ts`.\n */\nimport { createRequire } from \"node:module\";\n\nexport function versionOf(srcFileUrl: string, injected?: string): string {\n if (typeof injected === \"string\") return injected;\n try {\n const require = createRequire(srcFileUrl);\n return (require(\"../package.json\") as { version: string }).version;\n } catch {\n return \"0.0.0-dev\";\n }\n}\n","import { versionOf } from \"@kvbox/shared/version\";\n\ndeclare const __KVBOX_VERSION__: string | undefined;\n\nexport const VERSION: string = versionOf(import.meta.url, typeof __KVBOX_VERSION__ === \"string\" ? __KVBOX_VERSION__ : undefined);\n","import { Command } from \"commander\";\nimport { connectCommand } from \"./connect.js\";\nimport { lsCommand } from \"./ls.js\";\nimport { newCommand } from \"./new.js\";\nimport { rmCommand } from \"./rm.js\";\n\nexport const machineCommand = new Command(\"machine\")\n .alias(\"m\")\n .description(\"manage boxes\")\n .addCommand(newCommand)\n .addCommand(lsCommand)\n .addCommand(connectCommand)\n .addCommand(rmCommand);\n\n/** The same Command objects, added again at the top level so they cannot drift. */\nexport const machineAliases = [newCommand, lsCommand, connectCommand, rmCommand];\n","import { Command } from \"commander\";\nimport { notImplemented } from \"../../output.js\";\n\n/** kvbox machine connect <name> — exec ssh kvbox@<ip> with the kvbox key */\nexport const connectCommand = new Command(\"connect\")\n .description(\"open an interactive shell in a box over SSH\")\n .argument(\"<name>\", \"box name\")\n .action(() => notImplemented(\"machine connect\"));\n","import { Command } from \"commander\";\nimport { notImplemented } from \"../../output.js\";\n\n/** kvbox machine ls [--json] — --json output: Machine[] */\nexport const lsCommand = new Command(\"ls\")\n .description(\"list boxes with status and IP\")\n .option(\"--json\", \"print as JSON\")\n .action(() => notImplemented(\"machine ls\"));\n","import { Command, Option } from \"commander\";\nimport { DEFAULT_SIZE, SIZE_NAMES } from \"@kvbox/shared/sizes\";\nimport { notImplemented } from \"../../output.js\";\n\n/**\n * kvbox machine new [name] [--size s|m|l] [--from <snapshot>] [--force] [--json]\n * --json output: the Machine object (see packages/shared/src/api.ts).\n */\nexport const newCommand = new Command(\"new\")\n .description(\"boot a new box from the base image or a snapshot\")\n .argument(\"[name]\", \"box name (generated when omitted)\")\n .addOption(new Option(\"-s, --size <size>\", \"box size\").choices(SIZE_NAMES).default(DEFAULT_SIZE))\n .option(\"-f, --from <snapshot>\", \"start from a snapshot instead of the base image\")\n .option(\"--force\", \"skip the capacity guard\")\n .option(\"--json\", \"print the box as JSON\")\n .action(() => notImplemented(\"machine new\"));\n","/** Fixed box sizes. Firecracker backs guest memory lazily, so a box only costs what the guest touches. */\nexport const SIZES = {\n s: { vcpu: 2, memMib: 4096 },\n m: { vcpu: 4, memMib: 8192 },\n l: { vcpu: 8, memMib: 16384 },\n} as const;\n\nexport type SizeName = keyof typeof SIZES;\nexport const SIZE_NAMES = Object.keys(SIZES) as SizeName[];\nexport const DEFAULT_SIZE: SizeName = \"s\";\n\nexport function isSizeName(value: string): value is SizeName {\n return value in SIZES;\n}\n","import { Command } from \"commander\";\nimport { notImplemented } from \"../../output.js\";\n\n/** kvbox machine rm <name> [-y] — destructive: asks y/N on a TTY, needs -y otherwise */\nexport const rmCommand = new Command(\"rm\")\n .description(\"destroy a box\")\n .argument(\"<name>\", \"box name\")\n .option(\"-y, --yes\", \"do not ask for confirmation\")\n .action(() => notImplemented(\"machine rm\"));\n","import { Command } from \"commander\";\nimport { startCommand } from \"./start.js\";\nimport { statusCommand } from \"./status.js\";\nimport { stopCommand } from \"./stop.js\";\n\nexport const serverCommand = new Command(\"server\")\n .description(\"run or manage the kvbox server\")\n .addCommand(startCommand)\n .addCommand(statusCommand)\n .addCommand(stopCommand);\n","/**\n * kvbox server start\n * Starts the local `kvbox-server` (a separate package) detached, logging to\n * ~/.kvbox/server.log, and waits for its socket. The CLI never imports server code.\n */\nimport { Command } from \"commander\";\nimport { paths } from \"@kvbox/shared/paths\";\nimport { clientFor, startLocalServer } from \"../../client.js\";\nimport { info } from \"../../output.js\";\n\nexport const startCommand = new Command(\"start\")\n .description(\"start the local kvbox-server in the background\")\n .action(async (_opts: Record<string, never>, cmd: Command) => {\n const client = clientFor(cmd.optsWithGlobals());\n try {\n const health = await client.health();\n info(`kvbox-server ${health.version} is already running (pid ${health.pid})`);\n return;\n } catch {\n // not running: start it\n } finally {\n await client.close();\n }\n const { pid, socketPath } = await startLocalServer(paths());\n info(`kvbox-server started (pid ${pid}), listening on ${socketPath}`);\n });\n","/**\n * Host state layout. The filesystem is the database and `~/.kvbox` is its root.\n * This module is dependency-free so both the CLI (socket path, hosts.json)\n * and core (everything else) can use it without crossing the layer boundary.\n *\n * `KVBOX_HOME` overrides the root, mainly for tests.\n */\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport function kvboxHome(): string {\n return process.env[\"KVBOX_HOME\"] ?? join(homedir(), \".kvbox\");\n}\n\nexport function paths(home: string = kvboxHome()) {\n return {\n home,\n config: join(home, \"config.json\"),\n serverSock: join(home, \"server.sock\"),\n serverPid: join(home, \"server.pid\"),\n serverLog: join(home, \"server.log\"),\n hosts: join(home, \"hosts.json\"),\n ssh: join(home, \"ssh\"),\n sshKey: join(home, \"ssh\", \"id_ed25519\"),\n bin: join(home, \"bin\"),\n firecracker: join(home, \"bin\", \"firecracker\"),\n assets: join(home, \"assets\"),\n kernel: join(home, \"assets\", \"vmlinux\"),\n rootfs: join(home, \"assets\", \"rootfs.ext4\"),\n machines: join(home, \"machines\"),\n snapshots: join(home, \"snapshots\"),\n machine: (name: string) => ({\n dir: join(home, \"machines\", name),\n meta: join(home, \"machines\", name, \"meta.json\"),\n disk: join(home, \"machines\", name, \"disk.ext4\"),\n sock: join(home, \"machines\", name, \"fc.sock\"),\n log: join(home, \"machines\", name, \"fc.log\"),\n }),\n snapshot: (name: string) => ({\n dir: join(home, \"snapshots\", name),\n meta: join(home, \"snapshots\", name, \"meta.json\"),\n vmstate: join(home, \"snapshots\", name, \"vmstate\"),\n mem: join(home, \"snapshots\", name, \"mem\"),\n disk: join(home, \"snapshots\", name, \"disk.ext4\"),\n }),\n } as const;\n}\n\nexport type Paths = ReturnType<typeof paths>;\n","/**\n * ServerClient: one method per route of the kvbox API, over undici.\n * Target resolution: `--host` -> configured hosts (M2) -> local Unix socket.\n * Auto-starting the local server from every command when nothing listens is #17;\n * `startLocalServer` below is the piece it will reuse.\n */\nimport { spawn } from \"node:child_process\";\nimport { openSync } from \"node:fs\";\nimport { mkdir, stat } from \"node:fs/promises\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { Agent, request, type Dispatcher } from \"undici\";\nimport { z } from \"zod\";\nimport {\n ApiErrorSchema,\n CapacitySchema,\n HealthSchema,\n MachineSchema,\n SnapshotSchema,\n type CreateMachineRequest,\n type CreateSnapshotRequest,\n} from \"@kvbox/shared/api\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\nimport { paths, type Paths } from \"@kvbox/shared/paths\";\n\nexport type Target = { kind: \"local\"; socketPath: string } | { kind: \"remote\"; name: string; url: string };\n\nexport function resolveTarget(hostFlag?: string): Target {\n if (hostFlag) {\n throw new KvboxError(`--host ${hostFlag}: remote hosts arrive in M2`);\n }\n return { kind: \"local\", socketPath: paths().serverSock };\n}\n\nexport class ServerClient {\n private readonly dispatcher: Dispatcher;\n private readonly base: string;\n\n constructor(readonly target: Target) {\n if (target.kind === \"local\") {\n this.dispatcher = new Agent({ connect: { socketPath: target.socketPath } });\n this.base = \"http://kvbox\";\n } else {\n this.dispatcher = new Agent();\n this.base = target.url;\n }\n }\n\n private async call<T>(\n method: \"GET\" | \"POST\" | \"DELETE\",\n path: string,\n schema: z.ZodType<T>,\n body?: unknown,\n ): Promise<T> {\n let res;\n try {\n res = await request(this.base + path, {\n dispatcher: this.dispatcher,\n method,\n headers: body === undefined ? {} : { \"content-type\": \"application/json\" },\n body: body === undefined ? null : JSON.stringify(body),\n });\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code ?? (err as Error).cause;\n if (this.target.kind === \"local\") {\n throw new KvboxError(`kvbox-server is not running (${String(code)}); start it with: kvbox server start`);\n }\n throw new KvboxError(`cannot reach ${this.target.name}: ${String(code)}`);\n }\n const text = await res.body.text();\n if (res.statusCode >= 300) {\n const parsed = ApiErrorSchema.safeParse(text ? JSON.parse(text) : {});\n const msg = parsed.success ? parsed.data.error : `${res.statusCode} ${text}`;\n throw new KvboxError(msg, res.statusCode === 501 ? 2 : 1);\n }\n return schema.parse(text ? JSON.parse(text) : undefined);\n }\n\n health() {\n return this.call(\"GET\", \"/health\", HealthSchema);\n }\n\n capacity() {\n return this.call(\"GET\", \"/capacity\", CapacitySchema);\n }\n\n listMachines() {\n return this.call(\"GET\", \"/machines\", z.array(MachineSchema));\n }\n\n createMachine(req: CreateMachineRequest) {\n return this.call(\"POST\", \"/machines\", MachineSchema, req);\n }\n\n deleteMachine(name: string) {\n return this.call(\"DELETE\", `/machines/${encodeURIComponent(name)}`, z.undefined());\n }\n\n listSnapshots() {\n return this.call(\"GET\", \"/snapshots\", z.array(SnapshotSchema));\n }\n\n createSnapshot(req: CreateSnapshotRequest) {\n return this.call(\"POST\", \"/snapshots\", SnapshotSchema, req);\n }\n\n deleteSnapshot(name: string) {\n return this.call(\"DELETE\", `/snapshots/${encodeURIComponent(name)}`, z.undefined());\n }\n\n async close() {\n await this.dispatcher.close();\n }\n}\n\n/** Build a client from the global `--host` option of the root command. */\nexport function clientFor(opts: { host?: string }): ServerClient {\n return new ServerClient(resolveTarget(opts.host));\n}\n\nexport const NOT_A_HOST_HINT =\n \"kvbox-server is not installed, so this machine is not a kvbox host. \" +\n \"Set it up with install-server.sh, or point the CLI at a remote host (M2: kvbox host add).\";\n\n/**\n * Spawn `kvbox-server start` from PATH, detached, stdio to ~/.kvbox/server.log,\n * and resolve once its socket accepts a /health call (or reject after `timeoutMs`).\n */\nexport async function startLocalServer(p: Paths = paths(), timeoutMs = 5000): Promise<{ pid: number; socketPath: string }> {\n await mkdir(p.home, { recursive: true });\n const log = openSync(p.serverLog, \"a\");\n const child = spawn(\"kvbox-server\", [\"start\"], { detached: true, stdio: [\"ignore\", log, log] });\n\n const failed = new Promise<never>((_, reject) => {\n child.once(\"error\", (err: NodeJS.ErrnoException) => {\n reject(new KvboxError(err.code === \"ENOENT\" ? NOT_A_HOST_HINT : `could not start kvbox-server: ${err.message}`));\n });\n child.once(\"exit\", (code) => {\n reject(new KvboxError(`kvbox-server exited with code ${String(code)} before listening; see ${p.serverLog}`));\n });\n });\n\n const ready = (async () => {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await stat(p.serverSock).then(() => true, () => false)) {\n const probe = new ServerClient({ kind: \"local\", socketPath: p.serverSock });\n try {\n await probe.health();\n return;\n } catch {\n // socket exists but not serving yet\n } finally {\n await probe.close();\n }\n }\n await sleep(100);\n }\n throw new KvboxError(`kvbox-server did not start within ${timeoutMs / 1000}s; see ${p.serverLog}`);\n })();\n\n await Promise.race([ready, failed]);\n child.removeAllListeners(\"exit\");\n child.unref();\n return { pid: child.pid ?? -1, socketPath: p.serverSock };\n}\n","/**\n * Wire shapes of the kvbox HTTP API, shared by the client (src/cli) and the\n * server (src/server). Validated with zod on both sides because the client\n * may be talking to an older or newer server.\n */\nimport { z } from \"zod\";\nimport { SIZE_NAMES } from \"./sizes.js\";\n\nexport const SizeSchema = z.enum(SIZE_NAMES as [string, ...string[]]);\n\nexport const MachineSchema = z.object({\n name: z.string(),\n ip: z.string(),\n mac: z.string(),\n pid: z.number().int().nullable(),\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n source: z.string(), // \"base\" or \"snapshot:<name>\"\n status: z.enum([\"running\", \"stopped\"]),\n});\nexport type Machine = z.infer<typeof MachineSchema>;\n\nexport const CreateMachineRequest = z.object({\n name: z.string().optional(),\n size: SizeSchema.optional(),\n from: z.string().optional(),\n force: z.boolean().optional(),\n});\nexport type CreateMachineRequest = z.infer<typeof CreateMachineRequest>;\n\nexport const SnapshotSchema = z.object({\n name: z.string(),\n source: z.string(),\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n});\nexport type Snapshot = z.infer<typeof SnapshotSchema>;\n\nexport const CreateSnapshotRequest = z.object({\n box: z.string(),\n name: z.string(),\n});\nexport type CreateSnapshotRequest = z.infer<typeof CreateSnapshotRequest>;\n\nexport const CapacitySchema = z.object({\n hostMemMib: z.number().int(),\n hostCores: z.number().int(),\n reservedMemMib: z.number().int(),\n reservedVcpu: z.number().int(),\n running: z.number().int(),\n});\nexport type Capacity = z.infer<typeof CapacitySchema>;\n\nexport const HealthSchema = z.object({\n ok: z.literal(true),\n version: z.string(),\n pid: z.number().int(),\n});\nexport type Health = z.infer<typeof HealthSchema>;\n\nexport const ApiErrorSchema = z.object({\n error: z.string(),\n});\n","/** Errors with an exit code; the entry point prints `message` and exits with `exitCode`. */\nexport class KvboxError extends Error {\n constructor(\n message: string,\n public readonly exitCode: number = 1,\n ) {\n super(message);\n this.name = \"KvboxError\";\n }\n}\n\nexport class NotImplementedError extends KvboxError {\n constructor(what: string) {\n super(`${what}: not implemented yet`, 2);\n this.name = \"NotImplementedError\";\n }\n}\n","import { Command } from \"commander\";\nimport { clientFor } from \"../../client.js\";\nimport { info, json } from \"../../output.js\";\n\n/** kvbox server status [--json] — --json output: Health */\nexport const statusCommand = new Command(\"status\")\n .description(\"show whether the server is running\")\n .option(\"--json\", \"print as JSON\")\n .action(async (opts: { json?: boolean }, cmd: Command) => {\n const client = clientFor(cmd.optsWithGlobals());\n try {\n const health = await client.health();\n if (opts.json) json(health);\n else info(`kvbox-server ${health.version} running, pid ${health.pid}`);\n } finally {\n await client.close();\n }\n });\n","import { readFile } from \"node:fs/promises\";\nimport { Command } from \"commander\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\nimport { paths } from \"@kvbox/shared/paths\";\nimport { info } from \"../../output.js\";\n\n/** kvbox server stop — SIGTERM the pid in server.pid. Boxes keep running. */\nexport const stopCommand = new Command(\"stop\")\n .description(\"stop the local server (running boxes are not affected)\")\n .action(async () => {\n const p = paths();\n let pid: number;\n try {\n pid = Number.parseInt(await readFile(p.serverPid, \"utf8\"), 10);\n } catch {\n throw new KvboxError(\"kvbox-server is not running (no pid file)\");\n }\n try {\n process.kill(pid, \"SIGTERM\");\n } catch {\n throw new KvboxError(`kvbox-server pid ${pid} is not running; remove ${p.serverPid}`);\n }\n info(`sent SIGTERM to kvbox-server (pid ${pid})`);\n });\n","import { Command } from \"commander\";\nimport { lsCommand } from \"./ls.js\";\nimport { rmCommand } from \"./rm.js\";\nimport { saveCommand } from \"./save.js\";\n\nexport const snapshotCommand = new Command(\"snapshot\")\n .alias(\"snap\")\n .description(\"manage snapshots\")\n .addCommand(saveCommand)\n .addCommand(lsCommand)\n .addCommand(rmCommand);\n","import { Command } from \"commander\";\nimport { notImplemented } from \"../../output.js\";\n\n/** kvbox snapshot ls [--json] — --json output: Snapshot[] */\nexport const lsCommand = new Command(\"ls\")\n .description(\"list snapshots\")\n .option(\"--json\", \"print as JSON\")\n .action(() => notImplemented(\"snapshot ls\"));\n","import { Command } from \"commander\";\nimport { notImplemented } from \"../../output.js\";\n\n/** kvbox snapshot rm <name> [-y] — destructive: asks y/N on a TTY, needs -y otherwise */\nexport const rmCommand = new Command(\"rm\")\n .description(\"delete a snapshot\")\n .argument(\"<name>\", \"snapshot name\")\n .option(\"-y, --yes\", \"do not ask for confirmation\")\n .action(() => notImplemented(\"snapshot rm\"));\n","import { Command } from \"commander\";\nimport { notImplemented } from \"../../output.js\";\n\n/** kvbox snapshot save <box> <name> [--json] — --json output: Snapshot */\nexport const saveCommand = new Command(\"save\")\n .description(\"freeze a running box (memory + disk) into a snapshot\")\n .argument(\"<box>\", \"box name\")\n .argument(\"<name>\", \"snapshot name\")\n .option(\"--json\", \"print the snapshot as JSON\")\n .action(() => notImplemented(\"snapshot save\"));\n","/** Entry point: parse argv, map KvboxError to an exit code. */\nimport { color } from \"./output.js\";\nimport { buildProgram } from \"./program.js\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\n\ntry {\n await buildProgram().parseAsync(process.argv);\n} catch (err) {\n if (err instanceof KvboxError) {\n process.stderr.write(color.red(`error: ${err.message}`) + \"\\n\");\n process.exit(err.exitCode);\n }\n throw err;\n}\n"],"mappings":"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","names":["Command","require","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","z","z","Command","Command","Command","Command","Command","Command","Command","Command","lsCommand","Command","Command","rmCommand","Command","Command","Command","Command","lsCommand","rmCommand","Command"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghalex/kvbox",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "kvbox CLI: manage Firecracker microVM hosts with Docker-like commands. Thin client; the host runs @ghalex/kvbox-server.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"kvbox": "./dist/kvbox.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/ghalex/kvbox",
|
|
19
|
+
"directory": "packages/cli"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://kvbox.sh",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"firecracker",
|
|
24
|
+
"microvm",
|
|
25
|
+
"sandbox",
|
|
26
|
+
"vm",
|
|
27
|
+
"cli"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"dev": "tsx src/index.ts",
|
|
34
|
+
"build": "tsup"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"commander": "^15.0.0",
|
|
38
|
+
"picocolors": "^1.1.1",
|
|
39
|
+
"undici": "^8.12.3",
|
|
40
|
+
"zod": "^4.6.7"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@kvbox/shared": "workspace:*"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/README.md
DELETED