hostctl 0.1.41 → 0.1.44
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 +195 -59
- package/dist/bin/hostctl.js +1556 -357
- package/dist/bin/hostctl.js.map +1 -1
- package/dist/index.d.ts +3040 -1337
- package/dist/index.js +10588 -2591
- package/dist/index.js.map +1 -1
- package/package.json +13 -10
package/README.md
CHANGED
|
@@ -1,30 +1,63 @@
|
|
|
1
1
|
# hostctl
|
|
2
2
|
|
|
3
|
-
`hostctl` is a modern task runner for managing fleets of hosts. It
|
|
3
|
+
`hostctl` is a modern task runner for managing fleets of hosts. It executes TypeScript or JavaScript automations locally or over SSH while keeping inventories, tags, and secrets in one place.
|
|
4
4
|
|
|
5
5
|
## Why hostctl
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
- Treat remote automation like regular code: tasks are modules with strong typing and structured logging.
|
|
7
8
|
- First-class inventory and secrets management built around [AGE](https://age-encryption.org/).
|
|
8
|
-
- Layered runtime
|
|
9
|
+
- Layered runtime cleanly separates CLI parsing, orchestration, and execution for easy extension.
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
## System Requirements
|
|
12
|
+
|
|
13
|
+
- Node.js **24+** (CLI runs through `tsx`).
|
|
14
|
+
- [`age`](https://age-encryption.org) for encrypting secrets.
|
|
15
|
+
- Git (recommended) for cloning packages and publishing tasks.
|
|
16
|
+
|
|
17
|
+
Check your toolchain:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
node -v
|
|
21
|
+
npm -v
|
|
22
|
+
age --version
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Install hostctl
|
|
26
|
+
|
|
27
|
+
### Clone & Develop
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/monopod/hostctl.git
|
|
31
|
+
cd hostctl
|
|
32
|
+
npm install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `./hostctl` during development or `npm run build && ./dist/bin/hostctl.js ...` to test the bundled CLI.
|
|
36
|
+
|
|
37
|
+
### Global CLI
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g hostctl
|
|
41
|
+
hostctl --help
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### One-off Execution
|
|
45
|
+
|
|
46
|
+
Run straight from npm without installing globally:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx hostctl run core.echo message:hello
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Bootstrap identities, inventory, and secrets
|
|
53
|
+
|
|
54
|
+
1. **Generate AGE identities**
|
|
21
55
|
```bash
|
|
22
56
|
mkdir -p ~/.hostctl/age
|
|
23
|
-
age-keygen -o ~/.hostctl/age/
|
|
24
|
-
age-keygen -y ~/.hostctl/age/
|
|
57
|
+
age-keygen -o ~/.hostctl/age/you.priv
|
|
58
|
+
age-keygen -y ~/.hostctl/age/you.priv > ~/.hostctl/age/you.pub
|
|
25
59
|
```
|
|
26
|
-
|
|
27
|
-
Create `~/.hostctl/hostctl.yaml`:
|
|
60
|
+
2. **Author an inventory** at `~/.hostctl/hostctl.yaml`:
|
|
28
61
|
```yaml
|
|
29
62
|
hosts:
|
|
30
63
|
ubuntu-vm:
|
|
@@ -34,59 +67,162 @@
|
|
|
34
67
|
tags: [ubuntu, testvm]
|
|
35
68
|
secrets:
|
|
36
69
|
vagrant-password:
|
|
37
|
-
ids:
|
|
70
|
+
ids: you
|
|
38
71
|
value: vagrant
|
|
39
72
|
ids:
|
|
40
|
-
|
|
73
|
+
you: age1...
|
|
74
|
+
```
|
|
75
|
+
3. **Manage the file**
|
|
76
|
+
```bash
|
|
77
|
+
hostctl inventory encrypt # wrap with AGE recipients
|
|
78
|
+
hostctl inventory decrypt # view/edit locally
|
|
79
|
+
hostctl inventory list # inspect hosts & tags
|
|
41
80
|
```
|
|
42
|
-
|
|
43
|
-
|
|
81
|
+
Set `AGE_IDS="~/.hostctl/age/*.priv"` before running commands if the file is encrypted.
|
|
82
|
+
|
|
83
|
+
## Running tasks
|
|
84
|
+
|
|
85
|
+
- **Local script**
|
|
44
86
|
```bash
|
|
45
87
|
npm run build
|
|
46
88
|
./dist/bin/hostctl.js run example/echo.ts args:hello
|
|
47
89
|
```
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- `--json` emits machine-readable results per host.
|
|
90
|
+
- **Local package**
|
|
91
|
+
```bash
|
|
92
|
+
hostctl run ./my-package hello name:Sam
|
|
93
|
+
```
|
|
94
|
+
Local directories are executed directly and are not installable via `hostctl pkg install`.
|
|
95
|
+
- **Remote orchestration**
|
|
96
|
+
```bash
|
|
97
|
+
hostctl run -r -t ubuntu core.net.interfaces --json
|
|
98
|
+
```
|
|
58
99
|
|
|
59
|
-
|
|
60
|
-
-
|
|
100
|
+
- `-r/--remote` targets hosts selected by tags via SSH.
|
|
101
|
+
- `-t/--tag` is greedy; use `--` before positional args when needed.
|
|
102
|
+
- **From npm or git**
|
|
103
|
+
```bash
|
|
104
|
+
hostctl run hostctl-hello greet name:Phil
|
|
105
|
+
hostctl run https://github.com/monopod/hostctl-example echo args:hello,world
|
|
106
|
+
```
|
|
107
|
+
- **Install Docker anywhere**
|
|
61
108
|
```bash
|
|
62
|
-
|
|
63
|
-
hostctl run example/echo.ts args:hello
|
|
109
|
+
hostctl run -r -t ubuntu core.docker.install users:hostctl
|
|
64
110
|
```
|
|
65
|
-
|
|
111
|
+
The task follows Docker’s official installation guides for Ubuntu/Debian and Fedora/RHEL/Rocky (matching the current `xcpng-e2e` templates), so the same invocation works across your lab images.
|
|
112
|
+
- **Run containers**
|
|
66
113
|
```bash
|
|
67
|
-
|
|
114
|
+
hostctl run core.docker.run-container image:alpine command:'["/bin/sh","-c","echo from container"]'
|
|
115
|
+
hostctl run core.docker.run-container-detached image:redis name:ci-cache
|
|
68
116
|
```
|
|
69
|
-
The
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
117
|
+
The first task streams container output back to the CLI; the detached variant returns the created container ID/name so you can manage it later.
|
|
118
|
+
|
|
119
|
+
**Passing parameters**
|
|
120
|
+
|
|
121
|
+
- `key:value` pairs after the task name.
|
|
122
|
+
- `--params '{"key":"value"}'` for JSON blobs.
|
|
123
|
+
- `--file params.json` to load structured arguments.
|
|
124
|
+
|
|
125
|
+
## Managing task packages
|
|
126
|
+
|
|
127
|
+
`hostctl pkg` commands wrap the npm-only package manager:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
hostctl pkg create my-task --lang typescript
|
|
131
|
+
hostctl pkg install hostctl-hello
|
|
132
|
+
hostctl pkg list
|
|
133
|
+
hostctl pkg remove hostctl-hello
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Installed packages live under `~/.hostctl/packages` and can be run offline once cached.
|
|
137
|
+
Use `pkg install` for npm registry names (scoped + versioned) or git URLs. Local directories should be run directly without installing (`hostctl run ./path/to/pkg task args`); this avoids polluting the manifest with workstation paths and keeps the install story aligned with reproducible npm/git sources.
|
|
138
|
+
|
|
139
|
+
## Designing tasks
|
|
140
|
+
|
|
141
|
+
Define tasks with the `task` helper and a typed `TaskContext`:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { task, type TaskContext } from 'hostctl';
|
|
145
|
+
|
|
146
|
+
interface EchoParams {
|
|
147
|
+
message: string;
|
|
148
|
+
}
|
|
149
|
+
interface EchoResult {
|
|
150
|
+
repeated: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function run(context: TaskContext<EchoParams>): Promise<EchoResult> {
|
|
154
|
+
const { params, info } = context;
|
|
155
|
+
info(`Echo: ${params.message}`);
|
|
156
|
+
return { repeated: params.message };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default task(run, { name: 'echo', description: 'Prints a message' });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Export one default task per file. Qualified task names come from a registry when present; otherwise they are derived from the task file path relative to the package root.
|
|
163
|
+
|
|
164
|
+
Key `TaskContext` capabilities (see [`docs/task-api.md`](docs/task-api.md) for details):
|
|
165
|
+
|
|
166
|
+
- Structured logging via `info`, `warn`, `error`, `debug`, or `log(level, ...)`.
|
|
167
|
+
- Command execution with `exec(command, { sudo, env, cwd, stdin, pty })`.
|
|
168
|
+
- Remote fan-out using `ssh(tags, remoteFn)`.
|
|
169
|
+
- Subtask orchestration with `run(otherTask(params))`.
|
|
170
|
+
- Secrets & credentials via `getSecret(name)` and `getPassword()`.
|
|
171
|
+
- Inventory helpers: `inventory(tags)` and `selectedInventory(tags?)`.
|
|
172
|
+
- File helpers: `file.read`, `file.write`, `file.exists`, `file.mkdir`, `file.rm` that respect local vs. remote runtime.
|
|
173
|
+
|
|
174
|
+
## Building & testing task packages
|
|
175
|
+
|
|
176
|
+
1. Scaffold: `hostctl pkg create awesome-firewall --lang typescript`.
|
|
177
|
+
2. Install deps: `cd awesome-firewall && npm install`.
|
|
178
|
+
3. Build: `npm run build` (defaults to `tsc`).
|
|
179
|
+
4. Test locally without publishing: `npx hostctl run . args:foo`.
|
|
180
|
+
5. Add unit tests with Vitest if needed, or invoke tasks directly in scripts.
|
|
181
|
+
|
|
182
|
+
## Publishing task packages
|
|
183
|
+
|
|
184
|
+
1. Update `package.json` metadata (`name`, `version`, `description`, `files`).
|
|
185
|
+
2. Build artifacts (`npm run build` or rely on `prepublishOnly`).
|
|
186
|
+
3. Authenticate with npm (`npm login` or `NPM_TOKEN`).
|
|
187
|
+
4. Publish:
|
|
188
|
+
```bash
|
|
189
|
+
npm version patch # or minor/major
|
|
190
|
+
npm publish --access public
|
|
191
|
+
```
|
|
192
|
+
5. Tag releases in git or automate with tools like `release-it` (this repo ships `release.sh` as an example flow).
|
|
193
|
+
6. Verify: `npm info your-package version`.
|
|
194
|
+
|
|
195
|
+
## Consuming published packages
|
|
196
|
+
|
|
197
|
+
- One-off: `npx hostctl run your-package taskName param:value`.
|
|
198
|
+
- Cache locally: `hostctl pkg install your-package@1.2.3` then run offline.
|
|
199
|
+
- Compose in other codebases by adding the package to `dependencies` and importing its exported tasks.
|
|
200
|
+
|
|
201
|
+
## Troubleshooting
|
|
202
|
+
|
|
203
|
+
- **Task not found**: confirm the package exports the task name and that `exports` exposes it.
|
|
204
|
+
- **Version mismatch**: keep `package.json`, `src/version.ts`, and `jsr.json` in sync before releasing.
|
|
205
|
+
- **SSH failures**: verify inventory entries (hostname, user, auth) and only pass `-r` when you intend remote execution.
|
|
206
|
+
- **Secrets**: when inventories are encrypted, ensure `AGE_IDS` includes the private keys so `hostctl` can decrypt secrets.
|
|
207
|
+
- **Environment drift**: run `hostctl runtime` to inspect prerequisites or `hostctl runtime install` to bootstrap them.
|
|
208
|
+
|
|
209
|
+
## Developer workflow
|
|
210
|
+
|
|
211
|
+
- Format & typing: `npm run format` (Prettier) and `npm run lint` (`tsc --noEmit`).
|
|
212
|
+
- Build artifacts: `npm run build` (tsup) produces `dist/` bundles for the CLI and published package.
|
|
82
213
|
- Tests:
|
|
83
214
|
- `npm run test` → unit + functional suites.
|
|
84
215
|
- `npm run test:unit`, `npm run test:functional`, `npm run test:e2e` for focused runs.
|
|
85
|
-
- VM
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
##
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
216
|
+
- VM helpers (`npm run vm:*`) are legacy while XCP-ng provisioning matures; see `docs/xcp-ng-operations.md`.
|
|
217
|
+
- Releases rely on `npm run release` + `release-it`; see `NPM_MIGRATION_PLAN.md` for npm-only context.
|
|
218
|
+
|
|
219
|
+
## Documentation map
|
|
220
|
+
|
|
221
|
+
- **Task API reference**: [`docs/task-api.md`](docs/task-api.md).
|
|
222
|
+
- **Task package authoring**: [`docs/task-package-authoring.md`](docs/task-package-authoring.md).
|
|
223
|
+
- **Operational guides**: `docs/*.md` (process, package, system management, etc.).
|
|
224
|
+
- **Architecture & design**: [`ARCHITECTURE.md`](ARCHITECTURE.md) and supporting design docs.
|
|
225
|
+
- **Contributor guide**: [`CONTRIBUTING.md`](CONTRIBUTING.md) and [`AGENTS.md`](AGENTS.md).
|
|
226
|
+
- **Issue tracking**: <https://github.com/monopod/hostctl/issues>.
|
|
227
|
+
|
|
228
|
+
Explore the examples under `example/`, get familiar with the task API, and share automations with the community via npm-compatible packages.
|