kiwivm-cli 0.1.0

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/dist/admin-fOud1ZmX.mjs +15 -0
  4. package/dist/admin-fOud1ZmX.mjs.map +1 -0
  5. package/dist/backup-D1UJ4aap.mjs +12 -0
  6. package/dist/backup-D1UJ4aap.mjs.map +1 -0
  7. package/dist/help-Dk-WApoi.mjs +40 -0
  8. package/dist/help-Dk-WApoi.mjs.map +1 -0
  9. package/dist/index.d.mts +5 -0
  10. package/dist/index.d.mts.map +1 -0
  11. package/dist/index.mjs +177 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/dist/info-DKExtFYH.mjs +13 -0
  14. package/dist/info-DKExtFYH.mjs.map +1 -0
  15. package/dist/monitoring-BSuv8fj9.mjs +13 -0
  16. package/dist/monitoring-BSuv8fj9.mjs.map +1 -0
  17. package/dist/network-1ycEIJqT.mjs +15 -0
  18. package/dist/network-1ycEIJqT.mjs.map +1 -0
  19. package/dist/power-CDg0Mx1A.mjs +14 -0
  20. package/dist/power-CDg0Mx1A.mjs.map +1 -0
  21. package/dist/snapshot-LO_ufoj5.mjs +23 -0
  22. package/dist/snapshot-LO_ufoj5.mjs.map +1 -0
  23. package/dist/system-Bl-dsqX9.mjs +21 -0
  24. package/dist/system-Bl-dsqX9.mjs.map +1 -0
  25. package/package.json +46 -0
  26. package/src/client.test.ts +68 -0
  27. package/src/client.ts +55 -0
  28. package/src/commands/admin.test.ts +65 -0
  29. package/src/commands/admin.ts +25 -0
  30. package/src/commands/backup.test.ts +66 -0
  31. package/src/commands/backup.ts +23 -0
  32. package/src/commands/help.test.ts +50 -0
  33. package/src/commands/help.ts +36 -0
  34. package/src/commands/info.test.ts +67 -0
  35. package/src/commands/info.ts +20 -0
  36. package/src/commands/monitoring.test.ts +82 -0
  37. package/src/commands/monitoring.ts +20 -0
  38. package/src/commands/network.test.ts +85 -0
  39. package/src/commands/network.ts +24 -0
  40. package/src/commands/power.test.ts +68 -0
  41. package/src/commands/power.ts +22 -0
  42. package/src/commands/snapshot.test.ts +159 -0
  43. package/src/commands/snapshot.ts +40 -0
  44. package/src/commands/system.test.ts +98 -0
  45. package/src/commands/system.ts +29 -0
  46. package/src/index.test.ts +375 -0
  47. package/src/index.ts +172 -0
  48. package/src/types.ts +94 -0
@@ -0,0 +1,24 @@
1
+ import type { KiwiVMClient } from "../client.ts";
2
+
3
+ export async function run(
4
+ action: string,
5
+ flags: Record<string, string>,
6
+ client: KiwiVMClient,
7
+ ): Promise<unknown> {
8
+ switch (action) {
9
+ case "ipv6-add":
10
+ return client.call("ipv6/add");
11
+ case "ipv6-delete":
12
+ return client.call("ipv6/delete", { ip: flags["ip"] });
13
+ case "private-list":
14
+ return client.call("privateIp/getAvailableIps");
15
+ case "private-assign":
16
+ return client.call("privateIp/assign", { ip: flags["ip"] });
17
+ case "private-delete":
18
+ return client.call("privateIp/delete", { ip: flags["ip"] });
19
+ default:
20
+ throw new Error(
21
+ `Unknown network action: ${action}. Valid: ipv6-add, ipv6-delete, private-list, private-assign, private-delete`,
22
+ );
23
+ }
24
+ }
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import type { KiwiVMClient } from "../client.ts";
3
+ import { run } from "./power.ts";
4
+
5
+ function mockClient() {
6
+ const call = vi.fn();
7
+ return { client: { call } as unknown as KiwiVMClient, call };
8
+ }
9
+
10
+ describe("power command", () => {
11
+ it("start calls client.call('start')", async () => {
12
+ const { client, call } = mockClient();
13
+ call.mockResolvedValueOnce({ error: 0 });
14
+
15
+ const result = await run("start", {}, client);
16
+
17
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("start");
18
+ expect(result).toEqual({ error: 0 });
19
+ });
20
+
21
+ it("stop calls client.call('stop')", async () => {
22
+ const { client, call } = mockClient();
23
+ call.mockResolvedValueOnce({ error: 0 });
24
+
25
+ const result = await run("stop", {}, client);
26
+
27
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("stop");
28
+ expect(result).toEqual({ error: 0 });
29
+ });
30
+
31
+ it("restart calls client.call('restart')", async () => {
32
+ const { client, call } = mockClient();
33
+ call.mockResolvedValueOnce({ error: 0 });
34
+
35
+ const result = await run("restart", {}, client);
36
+
37
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("restart");
38
+ expect(result).toEqual({ error: 0 });
39
+ });
40
+
41
+ it("kill calls client.call('kill')", async () => {
42
+ const { client, call } = mockClient();
43
+ call.mockResolvedValueOnce({ error: 0 });
44
+
45
+ const result = await run("kill", {}, client);
46
+
47
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("kill");
48
+ expect(result).toEqual({ error: 0 });
49
+ });
50
+
51
+ it("returns the raw API response", async () => {
52
+ const { client, call } = mockClient();
53
+ const apiResponse = { error: 0, message: "Virtual server is running." };
54
+ call.mockResolvedValueOnce(apiResponse);
55
+
56
+ const result = await run("start", {}, client);
57
+
58
+ expect(result).toBe(apiResponse);
59
+ });
60
+
61
+ it("propagates errors from the client", async () => {
62
+ const { client, call } = mockClient();
63
+ const apiError = new Error("API failure");
64
+ call.mockRejectedValueOnce(apiError);
65
+
66
+ await expect(run("restart", {}, client)).rejects.toThrow("API failure");
67
+ });
68
+ });
@@ -0,0 +1,22 @@
1
+ import type { KiwiVMClient } from "../client.ts";
2
+
3
+ export async function run(
4
+ action: string,
5
+ _flags: Record<string, string>,
6
+ client: KiwiVMClient,
7
+ ): Promise<unknown> {
8
+ switch (action) {
9
+ case "start":
10
+ return client.call("start");
11
+ case "stop":
12
+ return client.call("stop");
13
+ case "restart":
14
+ return client.call("restart");
15
+ case "kill":
16
+ return client.call("kill");
17
+ default:
18
+ throw new Error(
19
+ `Unknown power action: ${action}. Valid: start, stop, restart, kill`,
20
+ );
21
+ }
22
+ }
@@ -0,0 +1,159 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import type { KiwiVMClient } from "../client.ts";
3
+ import { run } from "./snapshot.ts";
4
+
5
+ function mockClient() {
6
+ const call = vi.fn();
7
+ return { client: { call } as unknown as KiwiVMClient, call };
8
+ }
9
+
10
+ describe("snapshot command", () => {
11
+ describe("create", () => {
12
+ it("calls snapshot/create with description flag", async () => {
13
+ const { client, call } = mockClient();
14
+ call.mockResolvedValueOnce({ error: 0 });
15
+
16
+ const result = await run(
17
+ "create",
18
+ { description: "pre-upgrade" },
19
+ client,
20
+ );
21
+
22
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/create", {
23
+ description: "pre-upgrade",
24
+ });
25
+ expect(result).toEqual({ error: 0 });
26
+ });
27
+
28
+ it("calls snapshot/create without description when flag is omitted", async () => {
29
+ const { client, call } = mockClient();
30
+ call.mockResolvedValueOnce({ error: 0 });
31
+
32
+ await run("create", {}, client);
33
+
34
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/create", {
35
+ description: undefined,
36
+ });
37
+ });
38
+ });
39
+
40
+ describe("list", () => {
41
+ it("calls snapshot/list", async () => {
42
+ const { client, call } = mockClient();
43
+ const snapshots = {
44
+ error: 0,
45
+ snapshots: [
46
+ {
47
+ fileName: "snap1",
48
+ os: "Ubuntu",
49
+ description: "",
50
+ size: 1024,
51
+ md5: "",
52
+ sticky: 0,
53
+ purgesIn: 0,
54
+ downloadLink: "",
55
+ downloadLinkSSL: "",
56
+ },
57
+ ],
58
+ };
59
+ call.mockResolvedValueOnce(snapshots);
60
+
61
+ const result = await run("list", {}, client);
62
+
63
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/list");
64
+ expect(result).toEqual(snapshots);
65
+ });
66
+ });
67
+
68
+ describe("delete", () => {
69
+ it("calls snapshot/delete with snapshot flag", async () => {
70
+ const { client, call } = mockClient();
71
+ call.mockResolvedValueOnce({ error: 0 });
72
+
73
+ await run("delete", { snapshot: "snap1" }, client);
74
+
75
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/delete", {
76
+ snapshot: "snap1",
77
+ });
78
+ });
79
+ });
80
+
81
+ describe("restore", () => {
82
+ it("calls snapshot/restore with snapshot flag", async () => {
83
+ const { client, call } = mockClient();
84
+ call.mockResolvedValueOnce({ error: 0 });
85
+
86
+ await run("restore", { snapshot: "snap1" }, client);
87
+
88
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/restore", {
89
+ snapshot: "snap1",
90
+ });
91
+ });
92
+ });
93
+
94
+ describe("sticky", () => {
95
+ it("calls snapshot/toggleSticky with snapshot and sticky flags", async () => {
96
+ const { client, call } = mockClient();
97
+ call.mockResolvedValueOnce({ error: 0 });
98
+
99
+ await run("sticky", { snapshot: "snap1", sticky: "1" }, client);
100
+
101
+ expect(client.call).toHaveBeenCalledExactlyOnceWith(
102
+ "snapshot/toggleSticky",
103
+ { snapshot: "snap1", sticky: 1 },
104
+ );
105
+ });
106
+
107
+ it("converts sticky flag string '0' to number 0", async () => {
108
+ const { client, call } = mockClient();
109
+ call.mockResolvedValueOnce({ error: 0 });
110
+
111
+ await run("sticky", { snapshot: "snap1", sticky: "0" }, client);
112
+
113
+ expect(client.call).toHaveBeenCalledExactlyOnceWith(
114
+ "snapshot/toggleSticky",
115
+ { snapshot: "snap1", sticky: 0 },
116
+ );
117
+ });
118
+ });
119
+
120
+ describe("export", () => {
121
+ it("calls snapshot/export with snapshot flag", async () => {
122
+ const { client, call } = mockClient();
123
+ call.mockResolvedValueOnce({ error: 0 });
124
+
125
+ await run("export", { snapshot: "snap1" }, client);
126
+
127
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/export", {
128
+ snapshot: "snap1",
129
+ });
130
+ });
131
+ });
132
+
133
+ describe("import", () => {
134
+ it("calls snapshot/import with sourceVeid and sourceToken", async () => {
135
+ const { client, call } = mockClient();
136
+ call.mockResolvedValueOnce({ error: 0 });
137
+
138
+ await run(
139
+ "import",
140
+ { sourceVeid: "67890", sourceToken: "abc123" },
141
+ client,
142
+ );
143
+
144
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/import", {
145
+ sourceVeid: "67890",
146
+ sourceToken: "abc123",
147
+ });
148
+ });
149
+ });
150
+
151
+ it("propagates errors from the client", async () => {
152
+ const { client, call } = mockClient();
153
+ call.mockRejectedValueOnce(new Error("Snapshot not found"));
154
+
155
+ await expect(
156
+ run("delete", { snapshot: "nonexistent" }, client),
157
+ ).rejects.toThrow("Snapshot not found");
158
+ });
159
+ });
@@ -0,0 +1,40 @@
1
+ import type { KiwiVMClient } from "../client.ts";
2
+ import type { KiwiVMResponse, Snapshot } from "../types.ts";
3
+
4
+ export async function run(
5
+ action: string,
6
+ flags: Record<string, string>,
7
+ client: KiwiVMClient,
8
+ ): Promise<unknown> {
9
+ switch (action) {
10
+ case "create":
11
+ return client.call("snapshot/create", {
12
+ description: flags["description"],
13
+ });
14
+ case "list":
15
+ return client.call<KiwiVMResponse & { snapshots: Snapshot[] }>(
16
+ "snapshot/list",
17
+ );
18
+ case "delete":
19
+ return client.call("snapshot/delete", { snapshot: flags["snapshot"] });
20
+ case "restore":
21
+ return client.call("snapshot/restore", { snapshot: flags["snapshot"] });
22
+ case "sticky":
23
+ return client.call("snapshot/toggleSticky", {
24
+ snapshot: flags["snapshot"],
25
+ sticky:
26
+ flags["sticky"] !== undefined ? Number(flags["sticky"]) : undefined,
27
+ });
28
+ case "export":
29
+ return client.call("snapshot/export", { snapshot: flags["snapshot"] });
30
+ case "import":
31
+ return client.call("snapshot/import", {
32
+ sourceVeid: flags["sourceVeid"],
33
+ sourceToken: flags["sourceToken"],
34
+ });
35
+ default:
36
+ throw new Error(
37
+ `Unknown snapshot action: ${action}. Valid: create, list, delete, restore, sticky, export, import`,
38
+ );
39
+ }
40
+ }
@@ -0,0 +1,98 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import type { KiwiVMClient } from "../client.ts";
3
+ import { run } from "./system.ts";
4
+
5
+ function mockClient() {
6
+ const call = vi.fn();
7
+ return { client: { call } as unknown as KiwiVMClient, call };
8
+ }
9
+
10
+ describe("system command", () => {
11
+ describe("hostname", () => {
12
+ it("calls setHostname with new-hostname flag", async () => {
13
+ const { client, call } = mockClient();
14
+ call.mockResolvedValueOnce({ error: 0 });
15
+
16
+ await run("hostname", { newHostname: "my-vps.example.com" }, client);
17
+
18
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("setHostname", {
19
+ newHostname: "my-vps.example.com",
20
+ });
21
+ });
22
+ });
23
+
24
+ describe("rdns", () => {
25
+ it("calls setPTR with ip and ptr flags", async () => {
26
+ const { client, call } = mockClient();
27
+ call.mockResolvedValueOnce({ error: 0 });
28
+
29
+ await run("rdns", { ip: "1.2.3.4", ptr: "my-vps.example.com" }, client);
30
+
31
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("setPTR", {
32
+ ip: "1.2.3.4",
33
+ ptr: "my-vps.example.com",
34
+ });
35
+ });
36
+ });
37
+
38
+ describe("password", () => {
39
+ it("calls resetRootPassword with no params", async () => {
40
+ const { client, call } = mockClient();
41
+ const pwResponse = { error: 0, message: "Password reset successfully" };
42
+ call.mockResolvedValueOnce(pwResponse);
43
+
44
+ const result = await run("password", {}, client);
45
+
46
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("resetRootPassword");
47
+ expect(result).toEqual(pwResponse);
48
+ });
49
+ });
50
+
51
+ describe("sshkey", () => {
52
+ it("calls getSshKeys when --ssh-keys flag is not provided", async () => {
53
+ const { client, call } = mockClient();
54
+ call.mockResolvedValueOnce({
55
+ error: 0,
56
+ sshKeys: ["ssh-rsa AAA..."],
57
+ });
58
+
59
+ const result = await run("sshkey", {}, client);
60
+
61
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("getSshKeys");
62
+ expect(result).toMatchObject({ sshKeys: ["ssh-rsa AAA..."] });
63
+ });
64
+
65
+ it("calls updateSshKeys when --ssh-keys flag is provided", async () => {
66
+ const { client, call } = mockClient();
67
+ call.mockResolvedValueOnce({ error: 0 });
68
+
69
+ await run("sshkey", { sshKeys: "ssh-rsa AAA..." }, client);
70
+
71
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("updateSshKeys", {
72
+ sshKeys: "ssh-rsa AAA...",
73
+ });
74
+ });
75
+ });
76
+
77
+ describe("reinstall", () => {
78
+ it("calls reinstallOS with os flag", async () => {
79
+ const { client, call } = mockClient();
80
+ call.mockResolvedValueOnce({ error: 0 });
81
+
82
+ await run("reinstall", { os: "ubuntu-22.04" }, client);
83
+
84
+ expect(client.call).toHaveBeenCalledExactlyOnceWith("reinstallOS", {
85
+ os: "ubuntu-22.04",
86
+ });
87
+ });
88
+ });
89
+
90
+ it("propagates errors from the client", async () => {
91
+ const { client, call } = mockClient();
92
+ call.mockRejectedValueOnce(new Error("Invalid hostname"));
93
+
94
+ await expect(run("hostname", { newHostname: "" }, client)).rejects.toThrow(
95
+ "Invalid hostname",
96
+ );
97
+ });
98
+ });
@@ -0,0 +1,29 @@
1
+ import type { KiwiVMClient } from "../client.ts";
2
+
3
+ export async function run(
4
+ action: string,
5
+ flags: Record<string, string>,
6
+ client: KiwiVMClient,
7
+ ): Promise<unknown> {
8
+ switch (action) {
9
+ case "hostname":
10
+ return client.call("setHostname", { newHostname: flags["newHostname"] });
11
+ case "rdns":
12
+ return client.call("setPTR", { ip: flags["ip"], ptr: flags["ptr"] });
13
+ case "password":
14
+ return client.call("resetRootPassword");
15
+ case "sshkey":
16
+ if (flags["sshKeys"] !== undefined) {
17
+ return client.call("updateSshKeys", { sshKeys: flags["sshKeys"] });
18
+ }
19
+ return client.call("getSshKeys");
20
+ case "os":
21
+ return client.call("getAvailableOS");
22
+ case "reinstall":
23
+ return client.call("reinstallOS", { os: flags["os"] });
24
+ default:
25
+ throw new Error(
26
+ `Unknown system action: ${action}. Valid: hostname, rdns, password, sshkey, os, reinstall`,
27
+ );
28
+ }
29
+ }