incus-ts 0.1.0 → 0.1.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/README.md +157 -37
- package/dist/index.js +546 -142
- package/dist/index.js.map +3 -3
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -1,63 +1,183 @@
|
|
|
1
1
|
# incus-ts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lightweight Bun-first TypeScript client for Incus.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
domains are scaffolded and currently return explicit "not implemented yet"
|
|
8
|
-
errors.
|
|
5
|
+
The API is instance-handle oriented: get a handle with
|
|
6
|
+
`client.instances.instance(name)`, then call `.exec()`, `.setState()`, `.remove()`, etc.
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
Operation-returning calls are awaitable by default.
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
- Preserve Incus Go client capability domains.
|
|
14
|
-
- Provide a Gondolin-like ergonomic TypeScript surface:
|
|
15
|
-
- static factories (`Incus.connect*`)
|
|
16
|
-
- grouped resource APIs (`client.instances`, `client.networks`, ...)
|
|
17
|
-
- chainable context scoping (`client.project(...).target(...)`)
|
|
10
|
+
## Mental Model
|
|
18
11
|
|
|
19
|
-
|
|
12
|
+
1. Connect once (`Incus.connect*`).
|
|
13
|
+
2. Use collection APIs for listing/creating (`client.instances.*`).
|
|
14
|
+
3. Use a per-instance handle for day-to-day work (`client.instances.instance(name)`).
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
20
17
|
|
|
21
18
|
```ts
|
|
22
19
|
import { Incus } from "incus-ts";
|
|
23
20
|
|
|
24
|
-
const client = await Incus.
|
|
21
|
+
const client = await Incus.connectUnix(); // default: /var/lib/incus/unix.socket
|
|
22
|
+
const instance = client.instances.instance("my-container");
|
|
23
|
+
|
|
24
|
+
const proc = instance.exec(
|
|
25
|
+
{ command: ["sh", "-lc", "echo hello from incus-ts"], interactive: false },
|
|
26
|
+
{ stdout: "pipe", stderr: "pipe" },
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
for await (const chunk of proc) {
|
|
30
|
+
process.stdout.write(new TextDecoder().decode(chunk));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = await proc;
|
|
34
|
+
console.log(result.exitCode, result.ok);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Common Snippets (From E2E Flow)
|
|
25
38
|
|
|
26
|
-
|
|
39
|
+
### 1. Create a container (same base image style as e2e/gondolin setup)
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
```ts
|
|
42
|
+
import { Incus } from "incus-ts";
|
|
43
|
+
|
|
44
|
+
const client = await Incus.connectUnix();
|
|
45
|
+
const name = `incus-ts-demo-${Date.now().toString(36)}`;
|
|
46
|
+
|
|
47
|
+
await client.instances.create({
|
|
48
|
+
name,
|
|
49
|
+
type: "container",
|
|
50
|
+
source: {
|
|
51
|
+
type: "image",
|
|
52
|
+
mode: "pull",
|
|
53
|
+
server: "https://images.linuxcontainers.org",
|
|
54
|
+
protocol: "simplestreams",
|
|
55
|
+
alias: "alpine/3.20",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Start it
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const instance = client.instances.instance(name);
|
|
64
|
+
await instance.setState({ action: "start", timeout: 180 });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Stream output while command is running
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const instance = client.instances.instance(name);
|
|
71
|
+
|
|
72
|
+
const proc = instance.exec(
|
|
73
|
+
{ command: ["sh", "-lc", "echo stream:1; cat >/dev/null; echo stream:2"], interactive: false },
|
|
74
|
+
{ stdout: "pipe", stderr: "pipe" },
|
|
75
|
+
);
|
|
30
76
|
|
|
31
|
-
const instance = scoped.instances.instance("my-container");
|
|
32
|
-
const proc = instance.exec({ command: ["sh", "-lc", "echo hello"] }, { stdout: "pipe" });
|
|
33
77
|
for await (const chunk of proc) {
|
|
34
78
|
process.stdout.write(new TextDecoder().decode(chunk));
|
|
35
79
|
}
|
|
80
|
+
|
|
36
81
|
const result = await proc;
|
|
37
|
-
console.log(result.exitCode);
|
|
82
|
+
console.log(result.exitCode, result.ok);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 4. Run a network check inside the container
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const instance = client.instances.instance(name);
|
|
89
|
+
const decoder = new TextDecoder();
|
|
90
|
+
let stdout = "";
|
|
91
|
+
let stderr = "";
|
|
92
|
+
|
|
93
|
+
const net = instance.exec(
|
|
94
|
+
{
|
|
95
|
+
command: [
|
|
96
|
+
"sh",
|
|
97
|
+
"-lc",
|
|
98
|
+
"GW=$(ip route | awk '/default/ {print $3; exit}'); "
|
|
99
|
+
+ "ping -c 1 -W 2 \"${GW:-192.168.100.1}\" >/tmp/ping.out 2>&1; "
|
|
100
|
+
+ "rc=$?; cat /tmp/ping.out; "
|
|
101
|
+
+ "if [ \"$rc\" -eq 0 ]; then echo __PING_OK__; fi; "
|
|
102
|
+
+ "exit \"$rc\"",
|
|
103
|
+
],
|
|
104
|
+
interactive: false,
|
|
105
|
+
},
|
|
106
|
+
{ stdout: "pipe", stderr: "pipe" },
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const readStdout = (async () => {
|
|
110
|
+
for await (const chunk of net.stdout) stdout += decoder.decode(chunk, { stream: true });
|
|
111
|
+
stdout += decoder.decode();
|
|
112
|
+
})();
|
|
113
|
+
const readStderr = (async () => {
|
|
114
|
+
for await (const chunk of net.stderr) stderr += decoder.decode(chunk, { stream: true });
|
|
115
|
+
stderr += decoder.decode();
|
|
116
|
+
})();
|
|
117
|
+
|
|
118
|
+
const netResult = await net;
|
|
119
|
+
await Promise.all([readStdout, readStderr]);
|
|
120
|
+
if (!netResult.ok || !`${stdout}\n${stderr}`.includes("__PING_OK__")) {
|
|
121
|
+
throw new Error("network check failed");
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 5. Cleanup
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const instance = client.instances.instance(name);
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
await instance.setState({ action: "stop", timeout: 30, force: true });
|
|
132
|
+
} catch {
|
|
133
|
+
// Instance might already be stopped.
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await instance.remove();
|
|
137
|
+
client.disconnect();
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 6. Snapshot + restore
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const instance = client.instances.instance("my-container");
|
|
144
|
+
|
|
145
|
+
await instance.snapshots.create({ name: "snap0" });
|
|
146
|
+
await instance.restore("snap0");
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 7. Fork (copy) an instance
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const source = client.instances.instance("my-container");
|
|
153
|
+
|
|
154
|
+
// Clone current state
|
|
155
|
+
await source.fork("my-container-copy");
|
|
156
|
+
|
|
157
|
+
// Clone from a specific snapshot
|
|
158
|
+
await source.fork("my-container-from-snap", {
|
|
159
|
+
fromSnapshot: "snap0",
|
|
160
|
+
});
|
|
38
161
|
```
|
|
39
162
|
|
|
40
|
-
## Implemented
|
|
163
|
+
## Implemented
|
|
41
164
|
|
|
42
|
-
- `connection`, `raw`
|
|
43
|
-
- `
|
|
44
|
-
- `
|
|
45
|
-
-
|
|
46
|
-
- `
|
|
47
|
-
|
|
165
|
+
- `connection`, `raw`, `server`, `operations`
|
|
166
|
+
- `images` + `images.aliases` (simple-streams runtime methods are still scaffolded)
|
|
167
|
+
- `instances` collection + instance handles (`instances.instance(name)`) with:
|
|
168
|
+
- CRUD/state
|
|
169
|
+
- snapshots (`create`, `list`, `get`, `update`, `rename`, `remove`, `restore`)
|
|
170
|
+
- `exec` (Unix-socket websocket attach, async streaming, promise-style completion)
|
|
171
|
+
- `logs`, `files`, `metadata`, `console`
|
|
48
172
|
|
|
49
|
-
##
|
|
173
|
+
## Still Scaffolded
|
|
50
174
|
|
|
51
|
-
- `certificates`
|
|
52
|
-
- `
|
|
53
|
-
- `
|
|
54
|
-
- `
|
|
55
|
-
- `storage` and nested groups
|
|
56
|
-
- `cluster`
|
|
57
|
-
- `warnings`
|
|
58
|
-
- `instances.templates`, `instances.snapshots`, `instances.backups`
|
|
175
|
+
- `certificates`, `events`
|
|
176
|
+
- `networks`, `profiles`, `projects`
|
|
177
|
+
- `storage`, `cluster`, `warnings`
|
|
178
|
+
- `instances.templates`, `instances.backups`
|
|
59
179
|
|
|
60
|
-
## Scripts
|
|
180
|
+
## Bun Scripts
|
|
61
181
|
|
|
62
182
|
- `bun run typecheck`
|
|
63
183
|
- `bun run test`
|