@hasna/machines 0.0.1 → 0.0.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/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # @hasna/machines
2
2
 
3
- Machine fleet management for developers — provision, sync, and manage multiple dev machines from CLI and MCP.
3
+ Machine fleet management for developers — provision, sync, inspect, and operate multiple development machines from CLI and MCP.
4
4
 
5
- ## Planned surface
5
+ ## Binaries
6
6
 
7
- - `machines`: Commander-based CLI for manifest, setup, sync, and status commands
7
+ - `machines`: Commander-based CLI for manifest, setup, sync, inspection, and dashboard commands
8
8
  - `machines-mcp`: MCP server exposing fleet tools to AI agents
9
9
  - `machines-agent`: lightweight local daemon for heartbeats and runtime reporting
10
10
 
11
11
  ## Manifest
12
12
 
13
- `machines.json` is the desired fleet declaration. Current commands:
13
+ `machines.json` is the desired fleet declaration.
14
14
 
15
15
  ```bash
16
16
  machines manifest init
@@ -28,23 +28,41 @@ machines setup --machine spark01 --json
28
28
  machines setup --machine spark01 --apply --yes
29
29
  machines sync --machine spark01 --json
30
30
  machines sync --machine spark01 --apply --yes
31
+ machines doctor --machine spark01
32
+ machines self-test
31
33
  ```
32
34
 
33
35
  ## Applications and tooling
34
36
 
35
37
  ```bash
36
- machines apps list --machine apple03 --json
38
+ machines apps list --machine apple03
39
+ machines apps status --machine apple03
40
+ machines apps diff --machine apple03
37
41
  machines apps plan --machine apple03 --json
38
42
  machines apps apply --machine apple03 --yes
39
43
 
40
- machines install-claude --machine spark01 --json
41
- machines install-claude --machine spark01 --tool claude codex --apply --yes
44
+ machines install-claude status --machine spark01
45
+ machines install-claude diff --machine spark01
46
+ machines install-claude plan --machine spark01 --tool claude codex --json
47
+ machines install-claude apply --machine spark01 --tool claude codex --yes
42
48
 
43
49
  machines install-tailscale --machine apple03 --json
50
+ ```
51
+
52
+ ## Notifications
53
+
54
+ ```bash
44
55
  machines notifications add --id ops --type webhook --target https://example.com/hook --event sync_failed
45
- machines notifications test --channel ops --json
56
+ machines notifications list
57
+ machines notifications test --channel ops
58
+ machines notifications test --channel ops --apply --yes
59
+ machines notifications dispatch --event manual.test --message "hello fleet"
46
60
  ```
47
61
 
62
+ - `email` channels deliver through local `sendmail` or `mail` when available
63
+ - `webhook` channels deliver JSON via HTTP POST
64
+ - `command` channels execute the configured command with `HASNA_MACHINES_NOTIFICATION_*` env vars
65
+
48
66
  ## Dashboard
49
67
 
50
68
  ```bash
@@ -59,6 +77,13 @@ The dashboard exposes:
59
77
  - `/api/status` fleet status JSON
60
78
  - `/api/manifest` current manifest JSON
61
79
  - `/api/notifications` notification channel JSON
80
+ - `/api/doctor` doctor report JSON
81
+ - `/api/self-test` smoke-check JSON
82
+ - `/api/apps/status` app inventory JSON
83
+ - `/api/apps/diff` app drift JSON
84
+ - `/api/install-claude/status` CLI inventory JSON
85
+ - `/api/install-claude/diff` CLI drift JSON
86
+ - `/api/notifications/test` POST endpoint for test delivery
62
87
 
63
88
  ## Local development
64
89
 
@@ -2084,13 +2084,17 @@ var {
2084
2084
  } = import__.default;
2085
2085
 
2086
2086
  // src/version.ts
2087
- import { readFileSync } from "fs";
2087
+ import { existsSync, readFileSync } from "fs";
2088
2088
  import { dirname, join } from "path";
2089
2089
  import { fileURLToPath } from "url";
2090
2090
  function getPackageVersion() {
2091
2091
  try {
2092
2092
  const here = dirname(fileURLToPath(import.meta.url));
2093
- const pkgPath = join(here, "..", "package.json");
2093
+ const candidates = [join(here, "..", "package.json"), join(here, "..", "..", "package.json")];
2094
+ const pkgPath = candidates.find((candidate) => existsSync(candidate));
2095
+ if (!pkgPath) {
2096
+ return "0.0.0";
2097
+ }
2094
2098
  return JSON.parse(readFileSync(pkgPath, "utf8")).version || "0.0.0";
2095
2099
  } catch {
2096
2100
  return "0.0.0";
@@ -2104,14 +2108,14 @@ import { hostname } from "os";
2104
2108
  import { createRequire } from "module";
2105
2109
  import { Database } from "bun:sqlite";
2106
2110
  import {
2107
- existsSync,
2111
+ existsSync as existsSync2,
2108
2112
  mkdirSync,
2109
2113
  readdirSync,
2110
2114
  copyFileSync
2111
2115
  } from "fs";
2112
2116
  import { homedir } from "os";
2113
2117
  import { join as join2, relative } from "path";
2114
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
2118
+ import { existsSync as existsSync22, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
2115
2119
  import { homedir as homedir2 } from "os";
2116
2120
  import { join as join22 } from "path";
2117
2121
  import { readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
@@ -11313,7 +11317,7 @@ function getConfigPath() {
11313
11317
  return CONFIG_PATH;
11314
11318
  }
11315
11319
  function getCloudConfig() {
11316
- if (!existsSync2(CONFIG_PATH)) {
11320
+ if (!existsSync22(CONFIG_PATH)) {
11317
11321
  return CloudConfigSchema.parse({});
11318
11322
  }
11319
11323
  try {