@pleri/olam-cli 0.1.7

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 (196) hide show
  1. package/dist/__tests__/auth-status.test.d.ts +2 -0
  2. package/dist/__tests__/auth-status.test.d.ts.map +1 -0
  3. package/dist/__tests__/auth-status.test.js +290 -0
  4. package/dist/__tests__/auth-status.test.js.map +1 -0
  5. package/dist/__tests__/auth-upgrade.test.d.ts +9 -0
  6. package/dist/__tests__/auth-upgrade.test.d.ts.map +1 -0
  7. package/dist/__tests__/auth-upgrade.test.js +161 -0
  8. package/dist/__tests__/auth-upgrade.test.js.map +1 -0
  9. package/dist/__tests__/create-app-urls.test.d.ts +2 -0
  10. package/dist/__tests__/create-app-urls.test.d.ts.map +1 -0
  11. package/dist/__tests__/create-app-urls.test.js +102 -0
  12. package/dist/__tests__/create-app-urls.test.js.map +1 -0
  13. package/dist/__tests__/enter.test.d.ts +2 -0
  14. package/dist/__tests__/enter.test.d.ts.map +1 -0
  15. package/dist/__tests__/enter.test.js +90 -0
  16. package/dist/__tests__/enter.test.js.map +1 -0
  17. package/dist/__tests__/host-cp-gh-token.test.d.ts +9 -0
  18. package/dist/__tests__/host-cp-gh-token.test.d.ts.map +1 -0
  19. package/dist/__tests__/host-cp-gh-token.test.js +119 -0
  20. package/dist/__tests__/host-cp-gh-token.test.js.map +1 -0
  21. package/dist/__tests__/host-cp.test.d.ts +9 -0
  22. package/dist/__tests__/host-cp.test.d.ts.map +1 -0
  23. package/dist/__tests__/host-cp.test.js +254 -0
  24. package/dist/__tests__/host-cp.test.js.map +1 -0
  25. package/dist/__tests__/keys.test.d.ts +9 -0
  26. package/dist/__tests__/keys.test.d.ts.map +1 -0
  27. package/dist/__tests__/keys.test.js +145 -0
  28. package/dist/__tests__/keys.test.js.map +1 -0
  29. package/dist/__tests__/logs.test.d.ts +9 -0
  30. package/dist/__tests__/logs.test.d.ts.map +1 -0
  31. package/dist/__tests__/logs.test.js +124 -0
  32. package/dist/__tests__/logs.test.js.map +1 -0
  33. package/dist/__tests__/ps.test.d.ts +2 -0
  34. package/dist/__tests__/ps.test.d.ts.map +1 -0
  35. package/dist/__tests__/ps.test.js +172 -0
  36. package/dist/__tests__/ps.test.js.map +1 -0
  37. package/dist/__tests__/status-app-urls.test.d.ts +2 -0
  38. package/dist/__tests__/status-app-urls.test.d.ts.map +1 -0
  39. package/dist/__tests__/status-app-urls.test.js +125 -0
  40. package/dist/__tests__/status-app-urls.test.js.map +1 -0
  41. package/dist/__tests__/upgrade.test.d.ts +9 -0
  42. package/dist/__tests__/upgrade.test.d.ts.map +1 -0
  43. package/dist/__tests__/upgrade.test.js +262 -0
  44. package/dist/__tests__/upgrade.test.js.map +1 -0
  45. package/dist/commands/__tests__/carry-uncommitted.test.d.ts +14 -0
  46. package/dist/commands/__tests__/carry-uncommitted.test.d.ts.map +1 -0
  47. package/dist/commands/__tests__/carry-uncommitted.test.js +83 -0
  48. package/dist/commands/__tests__/carry-uncommitted.test.js.map +1 -0
  49. package/dist/commands/__tests__/openHostCpUrl.test.d.ts +2 -0
  50. package/dist/commands/__tests__/openHostCpUrl.test.d.ts.map +1 -0
  51. package/dist/commands/__tests__/openHostCpUrl.test.js +63 -0
  52. package/dist/commands/__tests__/openHostCpUrl.test.js.map +1 -0
  53. package/dist/commands/__tests__/refresh.test.d.ts +13 -0
  54. package/dist/commands/__tests__/refresh.test.d.ts.map +1 -0
  55. package/dist/commands/__tests__/refresh.test.js +170 -0
  56. package/dist/commands/__tests__/refresh.test.js.map +1 -0
  57. package/dist/commands/auth-status.d.ts +43 -0
  58. package/dist/commands/auth-status.d.ts.map +1 -0
  59. package/dist/commands/auth-status.js +208 -0
  60. package/dist/commands/auth-status.js.map +1 -0
  61. package/dist/commands/auth-upgrade.d.ts +47 -0
  62. package/dist/commands/auth-upgrade.d.ts.map +1 -0
  63. package/dist/commands/auth-upgrade.js +277 -0
  64. package/dist/commands/auth-upgrade.js.map +1 -0
  65. package/dist/commands/auth.d.ts +16 -0
  66. package/dist/commands/auth.d.ts.map +1 -0
  67. package/dist/commands/auth.js +283 -0
  68. package/dist/commands/auth.js.map +1 -0
  69. package/dist/commands/create.d.ts +8 -0
  70. package/dist/commands/create.d.ts.map +1 -0
  71. package/dist/commands/create.js +512 -0
  72. package/dist/commands/create.js.map +1 -0
  73. package/dist/commands/crystallize.d.ts +8 -0
  74. package/dist/commands/crystallize.d.ts.map +1 -0
  75. package/dist/commands/crystallize.js +101 -0
  76. package/dist/commands/crystallize.js.map +1 -0
  77. package/dist/commands/destroy.d.ts +6 -0
  78. package/dist/commands/destroy.d.ts.map +1 -0
  79. package/dist/commands/destroy.js +54 -0
  80. package/dist/commands/destroy.js.map +1 -0
  81. package/dist/commands/dispatch.d.ts +9 -0
  82. package/dist/commands/dispatch.d.ts.map +1 -0
  83. package/dist/commands/dispatch.js +94 -0
  84. package/dist/commands/dispatch.js.map +1 -0
  85. package/dist/commands/enter.d.ts +63 -0
  86. package/dist/commands/enter.d.ts.map +1 -0
  87. package/dist/commands/enter.js +206 -0
  88. package/dist/commands/enter.js.map +1 -0
  89. package/dist/commands/host-cp.d.ts +191 -0
  90. package/dist/commands/host-cp.d.ts.map +1 -0
  91. package/dist/commands/host-cp.js +797 -0
  92. package/dist/commands/host-cp.js.map +1 -0
  93. package/dist/commands/init.d.ts +9 -0
  94. package/dist/commands/init.d.ts.map +1 -0
  95. package/dist/commands/init.js +143 -0
  96. package/dist/commands/init.js.map +1 -0
  97. package/dist/commands/install.d.ts +22 -0
  98. package/dist/commands/install.d.ts.map +1 -0
  99. package/dist/commands/install.js +203 -0
  100. package/dist/commands/install.js.map +1 -0
  101. package/dist/commands/keys.d.ts +26 -0
  102. package/dist/commands/keys.d.ts.map +1 -0
  103. package/dist/commands/keys.js +151 -0
  104. package/dist/commands/keys.js.map +1 -0
  105. package/dist/commands/lanes.d.ts +18 -0
  106. package/dist/commands/lanes.d.ts.map +1 -0
  107. package/dist/commands/lanes.js +122 -0
  108. package/dist/commands/lanes.js.map +1 -0
  109. package/dist/commands/list.d.ts +6 -0
  110. package/dist/commands/list.d.ts.map +1 -0
  111. package/dist/commands/list.js +39 -0
  112. package/dist/commands/list.js.map +1 -0
  113. package/dist/commands/logs.d.ts +38 -0
  114. package/dist/commands/logs.d.ts.map +1 -0
  115. package/dist/commands/logs.js +177 -0
  116. package/dist/commands/logs.js.map +1 -0
  117. package/dist/commands/observe.d.ts +9 -0
  118. package/dist/commands/observe.d.ts.map +1 -0
  119. package/dist/commands/observe.js +34 -0
  120. package/dist/commands/observe.js.map +1 -0
  121. package/dist/commands/policy-check.d.ts +14 -0
  122. package/dist/commands/policy-check.d.ts.map +1 -0
  123. package/dist/commands/policy-check.js +76 -0
  124. package/dist/commands/policy-check.js.map +1 -0
  125. package/dist/commands/pr.d.ts +17 -0
  126. package/dist/commands/pr.d.ts.map +1 -0
  127. package/dist/commands/pr.js +148 -0
  128. package/dist/commands/pr.js.map +1 -0
  129. package/dist/commands/ps.d.ts +25 -0
  130. package/dist/commands/ps.d.ts.map +1 -0
  131. package/dist/commands/ps.js +164 -0
  132. package/dist/commands/ps.js.map +1 -0
  133. package/dist/commands/refresh-helpers.d.ts +25 -0
  134. package/dist/commands/refresh-helpers.d.ts.map +1 -0
  135. package/dist/commands/refresh-helpers.js +56 -0
  136. package/dist/commands/refresh-helpers.js.map +1 -0
  137. package/dist/commands/refresh.d.ts +23 -0
  138. package/dist/commands/refresh.d.ts.map +1 -0
  139. package/dist/commands/refresh.js +237 -0
  140. package/dist/commands/refresh.js.map +1 -0
  141. package/dist/commands/status.d.ts +6 -0
  142. package/dist/commands/status.d.ts.map +1 -0
  143. package/dist/commands/status.js +51 -0
  144. package/dist/commands/status.js.map +1 -0
  145. package/dist/commands/upgrade.d.ts +67 -0
  146. package/dist/commands/upgrade.d.ts.map +1 -0
  147. package/dist/commands/upgrade.js +358 -0
  148. package/dist/commands/upgrade.js.map +1 -0
  149. package/dist/commands/workspace.d.ts +23 -0
  150. package/dist/commands/workspace.d.ts.map +1 -0
  151. package/dist/commands/workspace.js +198 -0
  152. package/dist/commands/workspace.js.map +1 -0
  153. package/dist/commands/world-snapshot.d.ts +18 -0
  154. package/dist/commands/world-snapshot.d.ts.map +1 -0
  155. package/dist/commands/world-snapshot.js +327 -0
  156. package/dist/commands/world-snapshot.js.map +1 -0
  157. package/dist/context.d.ts +26 -0
  158. package/dist/context.d.ts.map +1 -0
  159. package/dist/context.js +51 -0
  160. package/dist/context.js.map +1 -0
  161. package/dist/index.d.ts +9 -0
  162. package/dist/index.d.ts.map +1 -0
  163. package/dist/index.js +18007 -0
  164. package/dist/index.js.map +1 -0
  165. package/dist/mcp-server.js +32236 -0
  166. package/dist/output.d.ts +10 -0
  167. package/dist/output.d.ts.map +1 -0
  168. package/dist/output.js +31 -0
  169. package/dist/output.js.map +1 -0
  170. package/host-cp/compose.yaml +126 -0
  171. package/host-cp/src/auth-secret-hint.mjs +45 -0
  172. package/host-cp/src/auth.mjs +155 -0
  173. package/host-cp/src/compose-worlds-sources.mjs +170 -0
  174. package/host-cp/src/container-secret-fetcher.mjs +163 -0
  175. package/host-cp/src/docker-events.mjs +184 -0
  176. package/host-cp/src/local-worlds-source.mjs +83 -0
  177. package/host-cp/src/plan-orchestrator.mjs +829 -0
  178. package/host-cp/src/plan-progress.mjs +282 -0
  179. package/host-cp/src/pr-cache.mjs +201 -0
  180. package/host-cp/src/pr-merge-poller.mjs +154 -0
  181. package/host-cp/src/process-poller.mjs +250 -0
  182. package/host-cp/src/proxy.mjs +245 -0
  183. package/host-cp/src/pylon-worlds-source.mjs +68 -0
  184. package/host-cp/src/redact.mjs +67 -0
  185. package/host-cp/src/secret-cache.mjs +104 -0
  186. package/host-cp/src/server.mjs +2215 -0
  187. package/host-cp/src/sse-gate.mjs +117 -0
  188. package/host-cp/src/version-status.mjs +209 -0
  189. package/host-cp/src/workspace-catalog.mjs +149 -0
  190. package/host-cp/src/world-names-store.mjs +176 -0
  191. package/host-cp/src/world-pr-state.mjs +97 -0
  192. package/host-cp/src/world-progress.mjs +322 -0
  193. package/host-cp/src/world-tunnel-manager.mjs +288 -0
  194. package/host-cp/src/worlds-db-source.mjs +191 -0
  195. package/host-cp/src/worlds-source.mjs +59 -0
  196. package/package.json +38 -0
@@ -0,0 +1,191 @@
1
+ /**
2
+ * WorldsDbSource — reconcile loop that reads ~/.olam/worlds.db and
3
+ * auto-registers running worlds into host-cp's in-memory registry.
4
+ *
5
+ * Two triggers (belt-and-suspenders):
6
+ * 1. fs.watch on the worlds.db file — fires within ~100ms of a write
7
+ * 2. 30s setInterval backstop — catches cases where fs.watch silently
8
+ * misses events (network filesystems, some Linux kernels)
9
+ *
10
+ * Uses better-sqlite3 for synchronous, lightweight reads. If the module
11
+ * is not installed (e.g., no native build in the container), the module
12
+ * logs a warning and exits without crashing the server.
13
+ *
14
+ * DB handle: deliberately NOT cached across reconcile calls. A long-lived
15
+ * readonly connection with the DB bind-mounted across the docker boundary
16
+ * does not reliably pick up writes committed on the host side — the host
17
+ * writer appends to the WAL, but the container reader's snapshot is stuck
18
+ * at the point the handle was first opened. Closing and reopening on every
19
+ * reconcile forces a new read transaction that sees all committed WAL
20
+ * frames. Cost: ~1 ms per call at a 30 s interval — negligible. This
21
+ * eliminates the entire class of "olam create world vanishes within 30 s"
22
+ * bugs (regression confirmed: ember-elk-9191 removed by reconciler despite
23
+ * being present in worlds.db with status=running).
24
+ *
25
+ * Interface: thin wrapper so a future "remote" source (cloud orchestrator)
26
+ * can drop in via the same WorldsSource interface in worlds-source.mjs.
27
+ */
28
+
29
+ import fs from 'node:fs';
30
+ import { createRequire } from 'node:module';
31
+
32
+ const require = createRequire(import.meta.url);
33
+
34
+ /**
35
+ * @typedef {object} WorldsDbSourceDeps
36
+ * @property {string} dbPath Path to worlds.db (OLAM_WORLDS_DB or ~/.olam/worlds.db)
37
+ * @property {string} dockerHost Docker API base URL (tcp://host:port)
38
+ * @property {string} worldHost Host used to reach world CPs (127.0.0.1 or host.docker.internal)
39
+ * @property {() => Record<string, number>} getRegistry Current WORLDS map
40
+ * @property {(id: string, port: number) => void} onWorldAdded Called when a new running world is found
41
+ * @property {(id: string) => void} onWorldRemoved Called when a running world disappears
42
+ * @property {(msg: string) => void} [log]
43
+ */
44
+
45
+ /**
46
+ * Derive the per-world CP host port from docker inspect.
47
+ *
48
+ * @param {string} worldId
49
+ * @param {string} dockerHost e.g. 'tcp://docker-socket-proxy:2375'
50
+ * @returns {Promise<number | null>}
51
+ */
52
+ async function getWorldPortFromDocker(worldId, dockerHost) {
53
+ const apiBase = dockerHost.replace(/^tcp:\/\//, 'http://');
54
+ const containerName = `olam-${worldId}-devbox`;
55
+ try {
56
+ const res = await fetch(`${apiBase}/containers/${encodeURIComponent(containerName)}/json`, {
57
+ signal: AbortSignal.timeout(3000),
58
+ });
59
+ if (!res.ok) return null;
60
+ const data = await res.json();
61
+ // Per-world CP runs on internal port 8080; host port is the published binding.
62
+ const ports = data?.NetworkSettings?.Ports ?? {};
63
+ const binding = ports['8080/tcp'];
64
+ if (!Array.isArray(binding) || binding.length === 0) return null;
65
+ const hostPort = parseInt(binding[0].HostPort, 10);
66
+ return Number.isFinite(hostPort) ? hostPort : null;
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Start the worlds-db reconcile loop. Returns a stop function.
74
+ *
75
+ * @param {WorldsDbSourceDeps} deps
76
+ * @returns {{ stop: () => void }}
77
+ */
78
+ export function startWorldsDbReconciler(deps) {
79
+ const { dbPath, dockerHost, getRegistry, onWorldAdded, onWorldRemoved, log = console.log } = deps;
80
+
81
+ let db = null;
82
+ let stopped = false;
83
+ let watcher = null;
84
+
85
+ function tryOpenDb() {
86
+ if (db) return db;
87
+ try {
88
+ // Dynamic require — gracefully degrade if better-sqlite3 is not installed.
89
+ // better-sqlite3 is CommonJS-only; createRequire enables sync dynamic loading in ESM.
90
+ const Database = require('better-sqlite3');
91
+ db = new Database(dbPath, { readonly: true, fileMustExist: true });
92
+ log(`[worlds-db] opened ${dbPath}`);
93
+ return db;
94
+ } catch (err) {
95
+ if (err.code === 'MODULE_NOT_FOUND') {
96
+ log('[worlds-db] better-sqlite3 not available; skipping DB reconciler');
97
+ } else if (err.code !== 'SQLITE_CANTOPEN') {
98
+ log(`[worlds-db] failed to open ${dbPath}: ${err.message}`);
99
+ }
100
+ return null;
101
+ }
102
+ }
103
+
104
+ async function reconcile() {
105
+ if (stopped) return;
106
+
107
+ // Close any cached handle so tryOpenDb() opens a fresh connection below.
108
+ // A long-lived readonly handle under cross-bind-mount WAL mode has its
109
+ // read snapshot frozen at open time; closing and reopening starts a new
110
+ // read transaction that includes all WAL frames committed by the host.
111
+ if (db) {
112
+ try { db.close(); } catch { /* ignore */ }
113
+ db = null;
114
+ }
115
+
116
+ const database = tryOpenDb();
117
+ if (!database) return;
118
+
119
+ try {
120
+ let runningIds;
121
+ try {
122
+ const rows = database.prepare("SELECT id FROM worlds WHERE status = 'running'").all();
123
+ runningIds = new Set(rows.map((r) => r.id));
124
+ } catch (err) {
125
+ log(`[worlds-db] query failed: ${err.message}`);
126
+ return;
127
+ }
128
+
129
+ const registry = getRegistry();
130
+
131
+ // Add worlds that are running in DB but missing from registry.
132
+ for (const id of runningIds) {
133
+ if (id in registry) continue;
134
+ const port = await getWorldPortFromDocker(id, dockerHost);
135
+ if (port === null) {
136
+ log(`[worlds-db] world ${id} running in DB but no docker port found; skipping`);
137
+ continue;
138
+ }
139
+ log(`[worlds-db] reconcile: adding ${id} → :${port}`);
140
+ onWorldAdded(id, port);
141
+ }
142
+
143
+ // Remove worlds that are registered but no longer 'running' in DB.
144
+ for (const id of Object.keys(registry)) {
145
+ if (runningIds.has(id)) continue;
146
+ log(`[worlds-db] reconcile: removing ${id} (not running in DB)`);
147
+ onWorldRemoved(id);
148
+ }
149
+ } finally {
150
+ // Always close — no need to hold the handle between reconciles.
151
+ try { db.close(); } catch { /* ignore */ }
152
+ db = null;
153
+ }
154
+ }
155
+
156
+ // Watch the DB file for changes (fast path).
157
+ if (fs.existsSync(dbPath)) {
158
+ try {
159
+ watcher = fs.watch(dbPath, { persistent: false }, () => {
160
+ void reconcile();
161
+ });
162
+ } catch (err) {
163
+ log(`[worlds-db] fs.watch failed: ${err.message}; relying on 30s poll`);
164
+ }
165
+ // Initial reconcile on startup.
166
+ void reconcile();
167
+ } else {
168
+ log(`[worlds-db] ${dbPath} not found; will poll every 30s`);
169
+ }
170
+
171
+ // 30s backstop poll. Also watches for the file to appear.
172
+ const interval = setInterval(async () => {
173
+ if (!watcher && fs.existsSync(dbPath)) {
174
+ // File appeared since startup — set up watcher now.
175
+ try {
176
+ watcher = fs.watch(dbPath, { persistent: false }, () => { void reconcile(); });
177
+ log(`[worlds-db] ${dbPath} appeared; watcher started`);
178
+ } catch { /* fs.watch failure is non-fatal */ }
179
+ }
180
+ await reconcile();
181
+ }, 30_000);
182
+
183
+ return {
184
+ stop() {
185
+ stopped = true;
186
+ clearInterval(interval);
187
+ if (watcher) { try { watcher.close(); } catch { /* ignore */ } }
188
+ if (db) { try { db.close(); } catch { /* ignore */ } }
189
+ },
190
+ };
191
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Phase E1 (olam-dogfood-vision): WorldsSource interface.
3
+ *
4
+ * Single narrow boundary that both LocalWorldsSource (today's
5
+ * dockerode-driven enumeration) and PylonWorldsSource (future cloud
6
+ * worlds) implement. The interface is the entire contract — there is
7
+ * no shared abstract class, no shared base, no shared utility module.
8
+ *
9
+ * Per Phase E plan (S1 contract carried through C-phase): the wire
10
+ * shape IS the abstraction. Sources implementing this interface are
11
+ * free to pick any backend (dockerode, Pylon SDK, mock, sqlite cache
12
+ * — anything) as long as `list()` returns the WorldSummary shape.
13
+ *
14
+ * Deliberately narrow:
15
+ * - `name` — discriminator for the source. SPA uses this to render
16
+ * the per-world `source` chip (E5).
17
+ * - `list()` — read-only enumeration. NO mutations. Mutations stay
18
+ * on host-cp's existing endpoints (POST /api/worlds delegation,
19
+ * DELETE via per-world CP, etc.). T5 mitigation: keeping the
20
+ * surface narrow lets the future Pylon SDK integration extend
21
+ * `list()`'s implementation without forcing a contract change
22
+ * across consumers.
23
+ *
24
+ * This is a `.mjs` file (matches host-cp's existing module style).
25
+ * Type information is conveyed via JSDoc; consumers reading via
26
+ * TypeScript get the shape via `// @ts-check` + JSDoc inference.
27
+ *
28
+ * @typedef {object} ServiceInfo
29
+ * @property {string} name
30
+ * @property {number} host_port
31
+ * @property {number} internal_port
32
+ * @property {string} url
33
+ * @property {boolean} live
34
+ *
35
+ * @typedef {object} WorldSummary
36
+ * @property {string} id
37
+ * @property {string | null} name
38
+ * @property {'running' | 'starting' | 'unknown' | 'failed'} status
39
+ * @property {ServiceInfo[]} services
40
+ * @property {'local' | 'pylon-cloud'} source
41
+ *
42
+ * @typedef {object} WorldsSource
43
+ * @property {'local' | 'pylon-cloud'} name
44
+ * @property {() => Promise<WorldSummary[]>} list
45
+ */
46
+
47
+ // Re-export the source-name discriminator so consumers don't repeat
48
+ // the literal string. Both implementations + E4's composition layer
49
+ // + E5's SPA badge logic reference this.
50
+ export const SOURCE_NAMES = /** @type {const} */ (['local', 'pylon-cloud']);
51
+
52
+ // `WorldsSource` is a TYPE export — no runtime symbol. Consumers
53
+ // import it via JSDoc references:
54
+ // /** @type {import('./worlds-source.mjs').WorldsSource} */
55
+ // or in TypeScript:
56
+ // import type { WorldsSource } from './worlds-source.mjs';
57
+ //
58
+ // Test files exercising the interface treat it as duck-typed: any
59
+ // object with the right shape passes structural compatibility.
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@pleri/olam-cli",
3
+ "version": "0.1.7",
4
+ "type": "module",
5
+ "bin": {
6
+ "olam": "./dist/index.js"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "host-cp",
11
+ "plugin",
12
+ "README.md"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/pleri/olam.git",
17
+ "directory": "packages/cli"
18
+ },
19
+ "publishConfig": {
20
+ "registry": "https://registry.npmjs.org",
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "dev": "tsx src/index.ts",
26
+ "test": "vitest run --passWithNoTests",
27
+ "test:ci": "vitest run --reporter=basic --passWithNoTests"
28
+ },
29
+ "dependencies": {
30
+ "better-sqlite3": "^12.0.0",
31
+ "commander": "^13.0.0",
32
+ "dockerode": "^4.0.0",
33
+ "ora": "^8.0.0",
34
+ "picocolors": "^1.1.0",
35
+ "ssh2": "^1.16.0",
36
+ "yaml": "^2.7.0"
37
+ }
38
+ }