@pleri/olam-cli 0.1.69 → 0.1.72

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 (250) hide show
  1. package/dist/cli-version.d.ts +16 -0
  2. package/dist/cli-version.d.ts.map +1 -0
  3. package/dist/cli-version.js +39 -0
  4. package/dist/cli-version.js.map +1 -0
  5. package/dist/commands/create.d.ts.map +1 -1
  6. package/dist/commands/create.js +2 -0
  7. package/dist/commands/create.js.map +1 -1
  8. package/dist/commands/host-cp.d.ts.map +1 -1
  9. package/dist/commands/host-cp.js +10 -0
  10. package/dist/commands/host-cp.js.map +1 -1
  11. package/dist/commands/runbooks.d.ts +13 -0
  12. package/dist/commands/runbooks.d.ts.map +1 -0
  13. package/dist/commands/runbooks.js +189 -0
  14. package/dist/commands/runbooks.js.map +1 -0
  15. package/dist/commands/world-snapshot.d.ts +1 -0
  16. package/dist/commands/world-snapshot.d.ts.map +1 -1
  17. package/dist/commands/world-snapshot.js +126 -1
  18. package/dist/commands/world-snapshot.js.map +1 -1
  19. package/dist/commands/worldspec/compile.d.ts +20 -0
  20. package/dist/commands/worldspec/compile.d.ts.map +1 -0
  21. package/dist/commands/worldspec/compile.js +130 -0
  22. package/dist/commands/worldspec/compile.js.map +1 -0
  23. package/dist/commands/worldspec/index.d.ts +12 -0
  24. package/dist/commands/worldspec/index.d.ts.map +1 -0
  25. package/dist/commands/worldspec/index.js +23 -0
  26. package/dist/commands/worldspec/index.js.map +1 -0
  27. package/dist/commands/worldspec/init.d.ts +15 -0
  28. package/dist/commands/worldspec/init.d.ts.map +1 -0
  29. package/dist/commands/worldspec/init.js +166 -0
  30. package/dist/commands/worldspec/init.js.map +1 -0
  31. package/dist/commands/worldspec/schema.d.ts +11 -0
  32. package/dist/commands/worldspec/schema.d.ts.map +1 -0
  33. package/dist/commands/worldspec/schema.js +55 -0
  34. package/dist/commands/worldspec/schema.js.map +1 -0
  35. package/dist/commands/worldspec/validate.d.ts +15 -0
  36. package/dist/commands/worldspec/validate.d.ts.map +1 -0
  37. package/dist/commands/worldspec/validate.js +66 -0
  38. package/dist/commands/worldspec/validate.js.map +1 -0
  39. package/dist/exit-codes.d.ts +32 -0
  40. package/dist/exit-codes.d.ts.map +1 -1
  41. package/dist/exit-codes.js +32 -0
  42. package/dist/exit-codes.js.map +1 -1
  43. package/dist/image-digests.json +5 -5
  44. package/dist/index.js +4726 -1231
  45. package/dist/index.js.map +1 -1
  46. package/dist/mcp-server.js +1424 -321
  47. package/host-cp/compose.yaml +6 -0
  48. package/host-cp/src/global-config-source.mjs +71 -0
  49. package/host-cp/src/listening-server-poller.mjs +1 -1
  50. package/host-cp/src/plan-orchestrator.mjs +20 -1
  51. package/host-cp/src/port-bridge-manager.mjs +1 -1
  52. package/host-cp/src/server.mjs +46 -7
  53. package/host-cp/src/version-status.mjs +36 -0
  54. package/package.json +4 -2
  55. package/dist/__tests__/audit-publish-deps-contract.test.d.ts +0 -26
  56. package/dist/__tests__/audit-publish-deps-contract.test.d.ts.map +0 -1
  57. package/dist/__tests__/audit-publish-deps-contract.test.js +0 -86
  58. package/dist/__tests__/audit-publish-deps-contract.test.js.map +0 -1
  59. package/dist/__tests__/auth-status.test.d.ts +0 -2
  60. package/dist/__tests__/auth-status.test.d.ts.map +0 -1
  61. package/dist/__tests__/auth-status.test.js +0 -291
  62. package/dist/__tests__/auth-status.test.js.map +0 -1
  63. package/dist/__tests__/auth-upgrade.test.d.ts +0 -9
  64. package/dist/__tests__/auth-upgrade.test.d.ts.map +0 -1
  65. package/dist/__tests__/auth-upgrade.test.js +0 -397
  66. package/dist/__tests__/auth-upgrade.test.js.map +0 -1
  67. package/dist/__tests__/bootstrap-tag-mcp-auth.test.d.ts +0 -22
  68. package/dist/__tests__/bootstrap-tag-mcp-auth.test.d.ts.map +0 -1
  69. package/dist/__tests__/bootstrap-tag-mcp-auth.test.js +0 -63
  70. package/dist/__tests__/bootstrap-tag-mcp-auth.test.js.map +0 -1
  71. package/dist/__tests__/cli-mcp-revoke.test.d.ts +0 -8
  72. package/dist/__tests__/cli-mcp-revoke.test.d.ts.map +0 -1
  73. package/dist/__tests__/cli-mcp-revoke.test.js +0 -124
  74. package/dist/__tests__/cli-mcp-revoke.test.js.map +0 -1
  75. package/dist/__tests__/config.test.d.ts +0 -2
  76. package/dist/__tests__/config.test.d.ts.map +0 -1
  77. package/dist/__tests__/config.test.js +0 -95
  78. package/dist/__tests__/config.test.js.map +0 -1
  79. package/dist/__tests__/create-app-urls.test.d.ts +0 -2
  80. package/dist/__tests__/create-app-urls.test.d.ts.map +0 -1
  81. package/dist/__tests__/create-app-urls.test.js +0 -102
  82. package/dist/__tests__/create-app-urls.test.js.map +0 -1
  83. package/dist/__tests__/docker-host.test.d.ts +0 -14
  84. package/dist/__tests__/docker-host.test.d.ts.map +0 -1
  85. package/dist/__tests__/docker-host.test.js +0 -109
  86. package/dist/__tests__/docker-host.test.js.map +0 -1
  87. package/dist/__tests__/enter.test.d.ts +0 -2
  88. package/dist/__tests__/enter.test.d.ts.map +0 -1
  89. package/dist/__tests__/enter.test.js +0 -90
  90. package/dist/__tests__/enter.test.js.map +0 -1
  91. package/dist/__tests__/help-output.test.d.ts +0 -2
  92. package/dist/__tests__/help-output.test.d.ts.map +0 -1
  93. package/dist/__tests__/help-output.test.js +0 -74
  94. package/dist/__tests__/help-output.test.js.map +0 -1
  95. package/dist/__tests__/host-cp-gh-token.test.d.ts +0 -9
  96. package/dist/__tests__/host-cp-gh-token.test.d.ts.map +0 -1
  97. package/dist/__tests__/host-cp-gh-token.test.js +0 -119
  98. package/dist/__tests__/host-cp-gh-token.test.js.map +0 -1
  99. package/dist/__tests__/host-cp.test.d.ts +0 -9
  100. package/dist/__tests__/host-cp.test.d.ts.map +0 -1
  101. package/dist/__tests__/host-cp.test.js +0 -327
  102. package/dist/__tests__/host-cp.test.js.map +0 -1
  103. package/dist/__tests__/image-presence.test.d.ts +0 -2
  104. package/dist/__tests__/image-presence.test.d.ts.map +0 -1
  105. package/dist/__tests__/image-presence.test.js +0 -44
  106. package/dist/__tests__/image-presence.test.js.map +0 -1
  107. package/dist/__tests__/install-root.test.d.ts +0 -2
  108. package/dist/__tests__/install-root.test.d.ts.map +0 -1
  109. package/dist/__tests__/install-root.test.js +0 -119
  110. package/dist/__tests__/install-root.test.js.map +0 -1
  111. package/dist/__tests__/keys.test.d.ts +0 -9
  112. package/dist/__tests__/keys.test.d.ts.map +0 -1
  113. package/dist/__tests__/keys.test.js +0 -145
  114. package/dist/__tests__/keys.test.js.map +0 -1
  115. package/dist/__tests__/logs.test.d.ts +0 -9
  116. package/dist/__tests__/logs.test.d.ts.map +0 -1
  117. package/dist/__tests__/logs.test.js +0 -124
  118. package/dist/__tests__/logs.test.js.map +0 -1
  119. package/dist/__tests__/mcp-import.test.d.ts +0 -11
  120. package/dist/__tests__/mcp-import.test.d.ts.map +0 -1
  121. package/dist/__tests__/mcp-import.test.js +0 -134
  122. package/dist/__tests__/mcp-import.test.js.map +0 -1
  123. package/dist/__tests__/protocol-version.test.d.ts +0 -2
  124. package/dist/__tests__/protocol-version.test.d.ts.map +0 -1
  125. package/dist/__tests__/protocol-version.test.js +0 -170
  126. package/dist/__tests__/protocol-version.test.js.map +0 -1
  127. package/dist/__tests__/ps.test.d.ts +0 -2
  128. package/dist/__tests__/ps.test.d.ts.map +0 -1
  129. package/dist/__tests__/ps.test.js +0 -172
  130. package/dist/__tests__/ps.test.js.map +0 -1
  131. package/dist/__tests__/registry-allowlist.test.d.ts +0 -2
  132. package/dist/__tests__/registry-allowlist.test.d.ts.map +0 -1
  133. package/dist/__tests__/registry-allowlist.test.js +0 -129
  134. package/dist/__tests__/registry-allowlist.test.js.map +0 -1
  135. package/dist/__tests__/services.test.d.ts +0 -8
  136. package/dist/__tests__/services.test.d.ts.map +0 -1
  137. package/dist/__tests__/services.test.js +0 -185
  138. package/dist/__tests__/services.test.js.map +0 -1
  139. package/dist/__tests__/status-app-urls.test.d.ts +0 -2
  140. package/dist/__tests__/status-app-urls.test.d.ts.map +0 -1
  141. package/dist/__tests__/status-app-urls.test.js +0 -125
  142. package/dist/__tests__/status-app-urls.test.js.map +0 -1
  143. package/dist/__tests__/upgrade-gh-token-contract.test.d.ts +0 -19
  144. package/dist/__tests__/upgrade-gh-token-contract.test.d.ts.map +0 -1
  145. package/dist/__tests__/upgrade-gh-token-contract.test.js +0 -63
  146. package/dist/__tests__/upgrade-gh-token-contract.test.js.map +0 -1
  147. package/dist/__tests__/upgrade.test.d.ts +0 -9
  148. package/dist/__tests__/upgrade.test.d.ts.map +0 -1
  149. package/dist/__tests__/upgrade.test.js +0 -586
  150. package/dist/__tests__/upgrade.test.js.map +0 -1
  151. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts +0 -6
  152. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts.map +0 -1
  153. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js +0 -26
  154. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js.map +0 -1
  155. package/dist/commands/__tests__/begin.test.d.ts +0 -7
  156. package/dist/commands/__tests__/begin.test.d.ts.map +0 -1
  157. package/dist/commands/__tests__/begin.test.js +0 -72
  158. package/dist/commands/__tests__/begin.test.js.map +0 -1
  159. package/dist/commands/__tests__/bootstrap.test.d.ts +0 -2
  160. package/dist/commands/__tests__/bootstrap.test.d.ts.map +0 -1
  161. package/dist/commands/__tests__/bootstrap.test.js +0 -370
  162. package/dist/commands/__tests__/bootstrap.test.js.map +0 -1
  163. package/dist/commands/__tests__/carry-uncommitted.test.d.ts +0 -14
  164. package/dist/commands/__tests__/carry-uncommitted.test.d.ts.map +0 -1
  165. package/dist/commands/__tests__/carry-uncommitted.test.js +0 -83
  166. package/dist/commands/__tests__/carry-uncommitted.test.js.map +0 -1
  167. package/dist/commands/__tests__/clean.test.d.ts +0 -9
  168. package/dist/commands/__tests__/clean.test.d.ts.map +0 -1
  169. package/dist/commands/__tests__/clean.test.js +0 -105
  170. package/dist/commands/__tests__/clean.test.js.map +0 -1
  171. package/dist/commands/__tests__/crystallize.test.d.ts +0 -2
  172. package/dist/commands/__tests__/crystallize.test.d.ts.map +0 -1
  173. package/dist/commands/__tests__/crystallize.test.js +0 -133
  174. package/dist/commands/__tests__/crystallize.test.js.map +0 -1
  175. package/dist/commands/__tests__/diagnose.test.d.ts +0 -9
  176. package/dist/commands/__tests__/diagnose.test.d.ts.map +0 -1
  177. package/dist/commands/__tests__/diagnose.test.js +0 -108
  178. package/dist/commands/__tests__/diagnose.test.js.map +0 -1
  179. package/dist/commands/__tests__/openHostCpUrl.test.d.ts +0 -2
  180. package/dist/commands/__tests__/openHostCpUrl.test.d.ts.map +0 -1
  181. package/dist/commands/__tests__/openHostCpUrl.test.js +0 -63
  182. package/dist/commands/__tests__/openHostCpUrl.test.js.map +0 -1
  183. package/dist/commands/__tests__/refresh.test.d.ts +0 -13
  184. package/dist/commands/__tests__/refresh.test.d.ts.map +0 -1
  185. package/dist/commands/__tests__/refresh.test.js +0 -170
  186. package/dist/commands/__tests__/refresh.test.js.map +0 -1
  187. package/dist/commands/__tests__/status.test.d.ts +0 -8
  188. package/dist/commands/__tests__/status.test.d.ts.map +0 -1
  189. package/dist/commands/__tests__/status.test.js +0 -62
  190. package/dist/commands/__tests__/status.test.js.map +0 -1
  191. package/dist/commands/__tests__/stop.test.d.ts +0 -5
  192. package/dist/commands/__tests__/stop.test.d.ts.map +0 -1
  193. package/dist/commands/__tests__/stop.test.js +0 -30
  194. package/dist/commands/__tests__/stop.test.js.map +0 -1
  195. package/dist/commands/__tests__/update.test.d.ts +0 -7
  196. package/dist/commands/__tests__/update.test.d.ts.map +0 -1
  197. package/dist/commands/__tests__/update.test.js +0 -224
  198. package/dist/commands/__tests__/update.test.js.map +0 -1
  199. package/dist/commands/__tests__/upgrade.all-three.test.d.ts +0 -19
  200. package/dist/commands/__tests__/upgrade.all-three.test.d.ts.map +0 -1
  201. package/dist/commands/__tests__/upgrade.all-three.test.js +0 -80
  202. package/dist/commands/__tests__/upgrade.all-three.test.js.map +0 -1
  203. package/dist/commands/__tests__/upgrade.compose-path.test.d.ts +0 -20
  204. package/dist/commands/__tests__/upgrade.compose-path.test.d.ts.map +0 -1
  205. package/dist/commands/__tests__/upgrade.compose-path.test.js +0 -140
  206. package/dist/commands/__tests__/upgrade.compose-path.test.js.map +0 -1
  207. package/dist/commands/__tests__/upgrade.history.test.d.ts +0 -15
  208. package/dist/commands/__tests__/upgrade.history.test.d.ts.map +0 -1
  209. package/dist/commands/__tests__/upgrade.history.test.js +0 -199
  210. package/dist/commands/__tests__/upgrade.history.test.js.map +0 -1
  211. package/dist/commands/__tests__/upgrade.lock.test.d.ts +0 -15
  212. package/dist/commands/__tests__/upgrade.lock.test.d.ts.map +0 -1
  213. package/dist/commands/__tests__/upgrade.lock.test.js +0 -253
  214. package/dist/commands/__tests__/upgrade.lock.test.js.map +0 -1
  215. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts +0 -21
  216. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts.map +0 -1
  217. package/dist/commands/__tests__/upgrade.olam-tag.test.js +0 -114
  218. package/dist/commands/__tests__/upgrade.olam-tag.test.js.map +0 -1
  219. package/dist/commands/__tests__/upgrade.poll.test.d.ts +0 -14
  220. package/dist/commands/__tests__/upgrade.poll.test.d.ts.map +0 -1
  221. package/dist/commands/__tests__/upgrade.poll.test.js +0 -136
  222. package/dist/commands/__tests__/upgrade.poll.test.js.map +0 -1
  223. package/dist/commands/__tests__/upgrade.recreate.test.d.ts +0 -17
  224. package/dist/commands/__tests__/upgrade.recreate.test.d.ts.map +0 -1
  225. package/dist/commands/__tests__/upgrade.recreate.test.js +0 -83
  226. package/dist/commands/__tests__/upgrade.recreate.test.js.map +0 -1
  227. package/dist/commands/__tests__/upgrade.rollback.test.d.ts +0 -12
  228. package/dist/commands/__tests__/upgrade.rollback.test.d.ts.map +0 -1
  229. package/dist/commands/__tests__/upgrade.rollback.test.js +0 -255
  230. package/dist/commands/__tests__/upgrade.rollback.test.js.map +0 -1
  231. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts +0 -12
  232. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts.map +0 -1
  233. package/dist/commands/__tests__/upgrade.sha-capture.test.js +0 -63
  234. package/dist/commands/__tests__/upgrade.sha-capture.test.js.map +0 -1
  235. package/dist/commands/__tests__/upgrade.smoke.test.d.ts +0 -19
  236. package/dist/commands/__tests__/upgrade.smoke.test.d.ts.map +0 -1
  237. package/dist/commands/__tests__/upgrade.smoke.test.js +0 -87
  238. package/dist/commands/__tests__/upgrade.smoke.test.js.map +0 -1
  239. package/dist/commands/__tests__/upgrade.swap.test.d.ts +0 -19
  240. package/dist/commands/__tests__/upgrade.swap.test.d.ts.map +0 -1
  241. package/dist/commands/__tests__/upgrade.swap.test.js +0 -312
  242. package/dist/commands/__tests__/upgrade.swap.test.js.map +0 -1
  243. package/dist/commands/__tests__/world-upgrade.test.d.ts +0 -8
  244. package/dist/commands/__tests__/world-upgrade.test.d.ts.map +0 -1
  245. package/dist/commands/__tests__/world-upgrade.test.js +0 -73
  246. package/dist/commands/__tests__/world-upgrade.test.js.map +0 -1
  247. package/dist/lib/__tests__/symlink-reconcile.test.d.ts +0 -2
  248. package/dist/lib/__tests__/symlink-reconcile.test.d.ts.map +0 -1
  249. package/dist/lib/__tests__/symlink-reconcile.test.js +0 -106
  250. package/dist/lib/__tests__/symlink-reconcile.test.js.map +0 -1
@@ -1,74 +0,0 @@
1
- // A3 (Phase A — fresh-install self-bootstrap): `olam --help` hides the
2
- // `crystallize` command unless PLERI is configured (env var or pleri block
3
- // in .olam/config.yaml). Command stays callable regardless — A1's runtime
4
- // gate handles the skip path.
5
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
- import * as fs from 'node:fs';
7
- import * as os from 'node:os';
8
- import * as path from 'node:path';
9
- import { Command } from 'commander';
10
- import { registerCrystallize } from '../commands/crystallize.js';
11
- import { isPleriConfigured } from '../pleri-config.js';
12
- function buildProgram(hidden) {
13
- const program = new Command();
14
- program.name('olam').description('test').version('0.0.0');
15
- registerCrystallize(program, { hidden });
16
- return program;
17
- }
18
- describe('crystallize visibility in `olam --help`', () => {
19
- it('omits crystallize from help output when registered with hidden: true', () => {
20
- const program = buildProgram(true);
21
- const help = program.helpInformation();
22
- expect(help).not.toMatch(/\bcrystallize\b/);
23
- });
24
- it('shows crystallize in help output when registered with hidden: false', () => {
25
- const program = buildProgram(false);
26
- const help = program.helpInformation();
27
- expect(help).toMatch(/\bcrystallize\b/);
28
- });
29
- it('keeps crystallize callable even when hidden (does not error on unknown command)', async () => {
30
- // commander treats hidden commands the same way for parsing — they
31
- // simply aren't listed in help output. Verify the parser still
32
- // recognizes the command and would dispatch its action.
33
- const program = buildProgram(true);
34
- // commander.commands is the public list. It includes the hidden one.
35
- expect(program.commands.map((c) => c.name())).toContain('crystallize');
36
- });
37
- });
38
- describe('isPleriConfigured', () => {
39
- let tmpDir;
40
- let originalPleriUrl;
41
- beforeEach(() => {
42
- originalPleriUrl = process.env.PLERI_BASE_URL;
43
- delete process.env.PLERI_BASE_URL;
44
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'olam-pleri-detect-'));
45
- });
46
- afterEach(() => {
47
- if (originalPleriUrl !== undefined) {
48
- process.env.PLERI_BASE_URL = originalPleriUrl;
49
- }
50
- else {
51
- delete process.env.PLERI_BASE_URL;
52
- }
53
- fs.rmSync(tmpDir, { recursive: true, force: true });
54
- });
55
- it('returns true when PLERI_BASE_URL is set in env', () => {
56
- process.env.PLERI_BASE_URL = 'https://pleri.example.com';
57
- expect(isPleriConfigured(tmpDir)).toBe(true);
58
- });
59
- it('returns false when neither env nor config has pleri', () => {
60
- expect(isPleriConfigured(tmpDir)).toBe(false);
61
- });
62
- it('returns false when config dir does not exist', () => {
63
- expect(isPleriConfigured('/tmp/no-such-dir-' + Date.now())).toBe(false);
64
- });
65
- it('returns true when config.yaml has a top-level pleri: block', () => {
66
- fs.writeFileSync(path.join(tmpDir, 'config.yaml'), 'project: test\npleri:\n base_url: https://pleri.example.com\n');
67
- expect(isPleriConfigured(tmpDir)).toBe(true);
68
- });
69
- it('returns false when pleri: line is commented out', () => {
70
- fs.writeFileSync(path.join(tmpDir, 'config.yaml'), 'project: test\n# pleri:\n# base_url: https://pleri.example.com\n');
71
- expect(isPleriConfigured(tmpDir)).toBe(false);
72
- });
73
- });
74
- //# sourceMappingURL=help-output.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"help-output.test.js","sourceRoot":"","sources":["../../src/__tests__/help-output.test.ts"],"names":[],"mappings":"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"}
@@ -1,9 +0,0 @@
1
- /**
2
- * Unit tests for captureGhToken and updated buildComposeEnv (GH_TOKEN injection).
3
- *
4
- * Isolated from host-cp.test.ts because these tests mock node:child_process
5
- * (spawnSync) — the mock is file-scoped and would otherwise break the pure-
6
- * function tests in the sibling file.
7
- */
8
- export {};
9
- //# sourceMappingURL=host-cp-gh-token.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"host-cp-gh-token.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/host-cp-gh-token.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -1,119 +0,0 @@
1
- /**
2
- * Unit tests for captureGhToken and updated buildComposeEnv (GH_TOKEN injection).
3
- *
4
- * Isolated from host-cp.test.ts because these tests mock node:child_process
5
- * (spawnSync) — the mock is file-scoped and would otherwise break the pure-
6
- * function tests in the sibling file.
7
- */
8
- import { describe, it, expect, vi, afterEach } from 'vitest';
9
- // Mock external packages that don't exist in the test environment.
10
- vi.mock('dockerode', () => ({ default: vi.fn() }));
11
- vi.mock('@olam/adapters', () => ({
12
- auditPortsForZombies: vi.fn(),
13
- PortHeldByZombieError: class PortHeldByZombieError extends Error {
14
- },
15
- }));
16
- vi.mock('@olam/core/src/util/open-url.js', () => ({ openUrl: vi.fn() }));
17
- vi.mock('../context.js', () => ({ loadContext: vi.fn() }));
18
- vi.mock('../output.js', () => ({
19
- printError: vi.fn(),
20
- printSuccess: vi.fn(),
21
- printInfo: vi.fn(),
22
- printHeader: vi.fn(),
23
- printWarning: vi.fn(),
24
- }));
25
- // Mock node:child_process before importing the module under test so
26
- // spawnSync is replaceable per-test.
27
- const mockSpawnSync = vi.fn();
28
- vi.mock('node:child_process', async (importOriginal) => {
29
- const mod = await importOriginal();
30
- return { ...mod, spawnSync: mockSpawnSync };
31
- });
32
- // Import AFTER the mocks are registered.
33
- const { captureGhToken, buildComposeEnv } = await import('../commands/host-cp.js');
34
- afterEach(() => {
35
- vi.clearAllMocks();
36
- });
37
- // ── captureGhToken ────────────────────────────────────────────────
38
- describe('captureGhToken', () => {
39
- it('returns the token when gh auth token exits 0 with output', () => {
40
- mockSpawnSync.mockReturnValue({
41
- status: 0,
42
- stdout: 'ghp_abc123\n',
43
- stderr: '',
44
- });
45
- expect(captureGhToken()).toBe('ghp_abc123');
46
- expect(mockSpawnSync).toHaveBeenCalledWith('gh', ['auth', 'token'], expect.objectContaining({
47
- encoding: 'utf-8',
48
- }));
49
- });
50
- it('returns null when gh auth token exits non-zero (not authenticated)', () => {
51
- mockSpawnSync.mockReturnValue({
52
- status: 1,
53
- stdout: '',
54
- stderr: 'not logged in\n',
55
- });
56
- expect(captureGhToken()).toBeNull();
57
- });
58
- it('returns null when gh exits 0 but stdout is empty (edge case)', () => {
59
- mockSpawnSync.mockReturnValue({
60
- status: 0,
61
- stdout: '',
62
- stderr: '',
63
- });
64
- expect(captureGhToken()).toBeNull();
65
- });
66
- it('returns null when gh exits 0 but stdout is only whitespace', () => {
67
- mockSpawnSync.mockReturnValue({
68
- status: 0,
69
- stdout: ' \n',
70
- stderr: '',
71
- });
72
- expect(captureGhToken()).toBeNull();
73
- });
74
- it('returns null when spawnSync throws (gh not installed)', () => {
75
- mockSpawnSync.mockImplementation(() => {
76
- throw new Error('spawn gh ENOENT');
77
- });
78
- expect(captureGhToken()).toBeNull();
79
- });
80
- it('strips trailing newline from token', () => {
81
- mockSpawnSync.mockReturnValue({
82
- status: 0,
83
- stdout: 'ghs_token_value\n',
84
- stderr: '',
85
- });
86
- expect(captureGhToken()).toBe('ghs_token_value');
87
- });
88
- });
89
- // ── buildComposeEnv — GH_TOKEN extension ─────────────────────────
90
- describe('buildComposeEnv with ghToken', () => {
91
- it('includes GH_TOKEN when provided alongside auth secret', () => {
92
- const env = buildComposeEnv('auth-secret-value', 'ghp_abc123');
93
- expect(env).toEqual({
94
- OLAM_AUTH_SECRET: 'auth-secret-value',
95
- GH_TOKEN: 'ghp_abc123',
96
- });
97
- });
98
- it('includes GH_TOKEN when auth secret is null', () => {
99
- const env = buildComposeEnv(null, 'ghp_abc123');
100
- expect(env).toEqual({ GH_TOKEN: 'ghp_abc123' });
101
- });
102
- it('omits GH_TOKEN when ghToken is null', () => {
103
- const env = buildComposeEnv('auth-secret-value', null);
104
- expect(env).toEqual({ OLAM_AUTH_SECRET: 'auth-secret-value' });
105
- });
106
- it('omits GH_TOKEN when ghToken is undefined (backward-compat)', () => {
107
- const env = buildComposeEnv('auth-secret-value');
108
- expect(env).toEqual({ OLAM_AUTH_SECRET: 'auth-secret-value' });
109
- });
110
- it('omits GH_TOKEN when ghToken is empty string', () => {
111
- const env = buildComposeEnv(null, '');
112
- expect(env).toEqual({});
113
- });
114
- it('returns empty object when both are null/undefined', () => {
115
- expect(buildComposeEnv(null, null)).toEqual({});
116
- expect(buildComposeEnv(null)).toEqual({});
117
- });
118
- });
119
- //# sourceMappingURL=host-cp-gh-token.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"host-cp-gh-token.test.js","sourceRoot":"","sources":["../../src/__tests__/host-cp-gh-token.test.ts"],"names":[],"mappings":"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"}
@@ -1,9 +0,0 @@
1
- /**
2
- * Phase F-2-D (D1+D2+D3): unit tests for host-cp.ts pure helpers.
3
- *
4
- * Covers token + PID file management. Docker-dependent paths
5
- * (findHostCpContainer, probeHealth, handleStart, etc.) are integration-
6
- * tested in M5 dogfood (D5) against a live docker daemon.
7
- */
8
- export {};
9
- //# sourceMappingURL=host-cp.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"host-cp.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/host-cp.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -1,327 +0,0 @@
1
- /**
2
- * Phase F-2-D (D1+D2+D3): unit tests for host-cp.ts pure helpers.
3
- *
4
- * Covers token + PID file management. Docker-dependent paths
5
- * (findHostCpContainer, probeHealth, handleStart, etc.) are integration-
6
- * tested in M5 dogfood (D5) against a live docker daemon.
7
- */
8
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
9
- import * as fs from 'node:fs';
10
- import * as os from 'node:os';
11
- import * as path from 'node:path';
12
- import { writeToken, readToken, removeToken, writePid, readPid, removePid, readAuthSecret, authSecretPath, buildComposeEnv, readR2Credentials, r2CredentialsPath, SIDECAR_PULL_SERVICE, } from '../commands/host-cp.js';
13
- let tmpHome;
14
- let originalHome;
15
- beforeEach(() => {
16
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'olam-host-cp-test-'));
17
- originalHome = process.env.OLAM_HOME;
18
- process.env.OLAM_HOME = tmpHome;
19
- });
20
- afterEach(() => {
21
- if (originalHome === undefined)
22
- delete process.env.OLAM_HOME;
23
- else
24
- process.env.OLAM_HOME = originalHome;
25
- fs.rmSync(tmpHome, { recursive: true, force: true });
26
- });
27
- // ── writeToken / readToken / removeToken ─────────────────────────
28
- describe('writeToken', () => {
29
- it('writes a 64-char hex token', () => {
30
- const token = writeToken();
31
- expect(token).toMatch(/^[0-9a-f]{64}$/);
32
- });
33
- it('writes to ~/.olam/host-cp.token by default', () => {
34
- writeToken();
35
- const filePath = path.join(tmpHome, 'host-cp.token');
36
- expect(fs.existsSync(filePath)).toBe(true);
37
- });
38
- it('sets file mode 0600', () => {
39
- writeToken();
40
- const filePath = path.join(tmpHome, 'host-cp.token');
41
- const mode = fs.statSync(filePath).mode & 0o777;
42
- expect(mode).toBe(0o600);
43
- });
44
- it('creates the parent directory if missing', () => {
45
- fs.rmSync(tmpHome, { recursive: true, force: true });
46
- writeToken();
47
- expect(fs.existsSync(path.join(tmpHome, 'host-cp.token'))).toBe(true);
48
- });
49
- it('overwrites existing token (each start regenerates)', () => {
50
- const t1 = writeToken();
51
- const t2 = writeToken();
52
- expect(t1).not.toBe(t2);
53
- expect(readToken()).toBe(t2);
54
- });
55
- it('produces fresh entropy across calls', () => {
56
- const tokens = new Set();
57
- for (let i = 0; i < 10; i++) {
58
- tokens.add(writeToken());
59
- }
60
- expect(tokens.size).toBe(10);
61
- });
62
- });
63
- describe('readToken', () => {
64
- it('returns null when token file is absent', () => {
65
- expect(readToken()).toBeNull();
66
- });
67
- it('returns the written value (stripped of trailing whitespace)', () => {
68
- const written = writeToken();
69
- expect(readToken()).toBe(written);
70
- });
71
- });
72
- describe('removeToken', () => {
73
- it('returns true when removing an existing token', () => {
74
- writeToken();
75
- expect(removeToken()).toBe(true);
76
- expect(readToken()).toBeNull();
77
- });
78
- it('returns false when the token file is absent', () => {
79
- expect(removeToken()).toBe(false);
80
- });
81
- it('is idempotent (second call returns false)', () => {
82
- writeToken();
83
- expect(removeToken()).toBe(true);
84
- expect(removeToken()).toBe(false);
85
- });
86
- });
87
- // ── writePid / readPid / removePid ────────────────────────────────
88
- describe('writePid', () => {
89
- it('writes the PID number as a string', () => {
90
- writePid(12345);
91
- const filePath = path.join(tmpHome, 'host-cp.pid');
92
- expect(fs.readFileSync(filePath, 'utf-8').trim()).toBe('12345');
93
- });
94
- it('uses mode 0644 (world-readable; not secret like the token)', () => {
95
- writePid(12345);
96
- const filePath = path.join(tmpHome, 'host-cp.pid');
97
- const mode = fs.statSync(filePath).mode & 0o777;
98
- expect(mode).toBe(0o644);
99
- });
100
- it('overwrites existing PID', () => {
101
- writePid(1);
102
- writePid(2);
103
- expect(readPid()).toBe(2);
104
- });
105
- });
106
- describe('readPid', () => {
107
- it('returns null when PID file is absent', () => {
108
- expect(readPid()).toBeNull();
109
- });
110
- it('returns the parsed number', () => {
111
- writePid(99999);
112
- expect(readPid()).toBe(99999);
113
- });
114
- it('returns null for non-numeric content (corruption defense)', () => {
115
- fs.writeFileSync(path.join(tmpHome, 'host-cp.pid'), 'not-a-number');
116
- expect(readPid()).toBeNull();
117
- });
118
- });
119
- describe('removePid', () => {
120
- it('returns true when removing an existing PID file', () => {
121
- writePid(1);
122
- expect(removePid()).toBe(true);
123
- expect(readPid()).toBeNull();
124
- });
125
- it('returns false when the PID file is absent', () => {
126
- expect(removePid()).toBe(false);
127
- });
128
- });
129
- // ── readAuthSecret + buildComposeEnv ─────────────────────────────
130
- //
131
- // Regression: `docker compose up -d` recreated host-cp with an empty
132
- // OLAM_AUTH_SECRET because the operator's shell didn't export it,
133
- // silently breaking every credentials/* call against auth-service.
134
- // `olam host-cp start` now reads the secret from ~/.olam/auth-secret
135
- // and passes it via spawn env — these tests lock the shape in.
136
- describe('readAuthSecret', () => {
137
- it('returns null when ~/.olam/auth-secret does not exist', () => {
138
- expect(readAuthSecret()).toBeNull();
139
- });
140
- it('returns the file contents (trimmed) when the file exists', () => {
141
- fs.mkdirSync(tmpHome, { recursive: true });
142
- fs.writeFileSync(authSecretPath(), 'Xt7pDfjdj-7fA0HALipPpGRRWomCJPvZ_pywxvkDkYs\n');
143
- expect(readAuthSecret()).toBe('Xt7pDfjdj-7fA0HALipPpGRRWomCJPvZ_pywxvkDkYs');
144
- });
145
- it('treats empty / whitespace-only files as null', () => {
146
- // A 0-byte file would round-trip to compose as an empty env var,
147
- // which is exactly the silent-failure mode the helper exists to
148
- // prevent. Caller (handleStart) prints a warning when this returns
149
- // null instead of dropping the operator into the empty-secret
150
- // footgun.
151
- fs.mkdirSync(tmpHome, { recursive: true });
152
- fs.writeFileSync(authSecretPath(), '');
153
- expect(readAuthSecret()).toBeNull();
154
- fs.writeFileSync(authSecretPath(), ' \n\n');
155
- expect(readAuthSecret()).toBeNull();
156
- });
157
- it('honors OLAM_HOME so tests do not stomp on the real ~/.olam', () => {
158
- fs.mkdirSync(tmpHome, { recursive: true });
159
- fs.writeFileSync(authSecretPath(), 'sentinel-value-for-test');
160
- expect(readAuthSecret()).toBe('sentinel-value-for-test');
161
- expect(authSecretPath()).toBe(path.join(tmpHome, 'auth-secret'));
162
- });
163
- });
164
- describe('buildComposeEnv', () => {
165
- it('emits OLAM_AUTH_SECRET when a non-empty secret is provided', () => {
166
- const env = buildComposeEnv('Xt7pDfjdj-7fA0HALipPpGRRWomCJPvZ_pywxvkDkYs');
167
- expect(env).toEqual({
168
- OLAM_AUTH_SECRET: 'Xt7pDfjdj-7fA0HALipPpGRRWomCJPvZ_pywxvkDkYs',
169
- });
170
- });
171
- it('returns an empty object when the secret is null', () => {
172
- // We deliberately omit the env var (rather than setting it to "")
173
- // so process.env can still win if the caller has already exported
174
- // it — caller code prints the loud warning when readAuthSecret
175
- // returns null.
176
- expect(buildComposeEnv(null)).toEqual({});
177
- });
178
- it('returns an empty object when the secret is empty', () => {
179
- // Defensive: this branch is unreachable in practice because
180
- // readAuthSecret already returns null for empty files, but the
181
- // helper must be safe to call with any string.
182
- expect(buildComposeEnv('')).toEqual({});
183
- });
184
- });
185
- // ── readR2Credentials + r2CredentialsPath ────────────────────────
186
- describe('r2CredentialsPath', () => {
187
- it('returns path under OLAM_HOME', () => {
188
- expect(r2CredentialsPath()).toBe(path.join(tmpHome, 'r2-credentials.json'));
189
- });
190
- });
191
- const VALID_CREDS = {
192
- account_id: 'abc123',
193
- bucket: 'olam-test-artifacts',
194
- access_key_id: 'key-id',
195
- secret_access_key: 'secret',
196
- public_url_base: 'https://pub-abc.r2.dev',
197
- };
198
- describe('readR2Credentials', () => {
199
- it('returns null when the file is absent', () => {
200
- expect(readR2Credentials()).toBeNull();
201
- });
202
- it('returns parsed credentials when the file is valid JSON', () => {
203
- fs.mkdirSync(tmpHome, { recursive: true });
204
- fs.writeFileSync(r2CredentialsPath(), JSON.stringify(VALID_CREDS));
205
- expect(readR2Credentials()).toEqual(VALID_CREDS);
206
- });
207
- it('returns null for empty / whitespace-only files', () => {
208
- fs.mkdirSync(tmpHome, { recursive: true });
209
- fs.writeFileSync(r2CredentialsPath(), '');
210
- expect(readR2Credentials()).toBeNull();
211
- fs.writeFileSync(r2CredentialsPath(), ' \n\n');
212
- expect(readR2Credentials()).toBeNull();
213
- });
214
- it('returns null for malformed JSON', () => {
215
- fs.mkdirSync(tmpHome, { recursive: true });
216
- fs.writeFileSync(r2CredentialsPath(), '{not valid json');
217
- expect(readR2Credentials()).toBeNull();
218
- });
219
- it('returns null when a required field is missing', () => {
220
- fs.mkdirSync(tmpHome, { recursive: true });
221
- const { public_url_base: _dropped, ...partial } = VALID_CREDS;
222
- fs.writeFileSync(r2CredentialsPath(), JSON.stringify(partial));
223
- expect(readR2Credentials()).toBeNull();
224
- });
225
- it('returns null when a field has the wrong type', () => {
226
- fs.mkdirSync(tmpHome, { recursive: true });
227
- fs.writeFileSync(r2CredentialsPath(), JSON.stringify({ ...VALID_CREDS, account_id: 42 }));
228
- expect(readR2Credentials()).toBeNull();
229
- });
230
- it('honors OLAM_HOME so tests do not stomp on the real ~/.olam', () => {
231
- fs.mkdirSync(tmpHome, { recursive: true });
232
- fs.writeFileSync(r2CredentialsPath(), JSON.stringify(VALID_CREDS));
233
- const result = readR2Credentials();
234
- expect(result).not.toBeNull();
235
- expect(result?.account_id).toBe('abc123');
236
- expect(r2CredentialsPath()).toBe(path.join(tmpHome, 'r2-credentials.json'));
237
- });
238
- });
239
- // ── OLAM_HOME override ─────────────────────────────────────────────
240
- describe('OLAM_HOME env override', () => {
241
- it('writeToken honors OLAM_HOME (verified by tmpdir setup above)', () => {
242
- writeToken();
243
- expect(fs.existsSync(path.join(tmpHome, 'host-cp.token'))).toBe(true);
244
- // Sanity: the real ~/.olam should NOT have been touched.
245
- const realHome = path.join(os.homedir(), '.olam', 'host-cp.token');
246
- if (fs.existsSync(realHome)) {
247
- // If it exists from a real `olam host-cp start` it predates the
248
- // test; can't assert absence. Just assert tmpHome is the active
249
- // path via the file we just wrote.
250
- // (Earlier mode/length assertions already cover correctness.)
251
- }
252
- });
253
- });
254
- // ── Regression: bootstrap compose-pull bare-name Docker Hub failure ──
255
- //
256
- // Bug: `olam bootstrap` succeeded at pulling 3 images by digest (step 2),
257
- // then `olam host-cp start` ran `docker compose pull` against a compose.yaml
258
- // that had `image: olam-host-cp:latest` (bare, no registry prefix). Docker
259
- // tried to pull from Docker Hub → "pull access denied for olam-host-cp,
260
- // repository does not exist". Root cause: compose.yaml used a bare image
261
- // name AND the pull command pulled all services instead of just the sidecar.
262
- //
263
- // Fix (PR #302):
264
- // 1. compose.yaml: `image: ghcr.io/pleri/olam-host-cp:latest` (registry-prefixed)
265
- // 2. host-cp start: pull only SIDECAR_PULL_SERVICE (`docker-socket-proxy`)
266
- // 3. bootstrap: tag digest-pulled image as `ghcr.io/pleri/olam-host-cp:latest`
267
- //
268
- // These tests lock both invariants so future drift is caught at test time.
269
- describe('SIDECAR_PULL_SERVICE regression guard', () => {
270
- it('names the sidecar, not the host-cp image', () => {
271
- // The pre-pull in handleStart must target only the third-party sidecar.
272
- // If this were empty or omitted, `docker compose pull` would pull ALL
273
- // services — including olam-host-cp, bypassing bootstrap's digest pin.
274
- expect(SIDECAR_PULL_SERVICE).toBe('docker-socket-proxy');
275
- });
276
- it('does not reference the olam-host-cp image', () => {
277
- // Guard against accidental reversion to a bare pull that drags in
278
- // the host-cp service. The host-cp image is already digest-pinned
279
- // by `olam bootstrap`; a compose pull for it would hit the registry
280
- // and potentially pull a different layer than what was verified.
281
- expect(SIDECAR_PULL_SERVICE).not.toMatch(/host.?cp/i);
282
- expect(SIDECAR_PULL_SERVICE).not.toMatch(/olam/i);
283
- });
284
- });
285
- describe('compose.yaml image field guard', () => {
286
- // The compose.yaml lives in packages/host-cp/; from packages/cli the
287
- // path resolves one level up.
288
- const composeYamlPath = path.resolve(process.cwd(), '../host-cp/compose.yaml');
289
- it('compose.yaml exists at the expected path', () => {
290
- expect(fs.existsSync(composeYamlPath)).toBe(true);
291
- });
292
- it('host-cp service image has a fully-qualified registry prefix', () => {
293
- // Bare image names (e.g. `olam-host-cp:latest` without a registry)
294
- // cause Docker to query Docker Hub, which returns "pull access denied"
295
- // for private/non-existent repos. The image MUST be registry-prefixed
296
- // (e.g. `ghcr.io/pleri/olam-host-cp:latest`) so that:
297
- // a) `docker compose up` finds it locally after `olam bootstrap` tags it
298
- // b) If a pull IS attempted, it targets GHCR (not Docker Hub)
299
- const yaml = fs.readFileSync(composeYamlPath, 'utf-8');
300
- // Extract the image: line that immediately follows `container_name: olam-host-cp`.
301
- // We match the section between `container_name: olam-host-cp` and the next service.
302
- const hostCpSection = yaml.match(/container_name: olam-host-cp[\s\S]*?(?=\n \w|\nnetworks:|\z)/)?.[0] ?? '';
303
- const imageMatch = hostCpSection.match(/^\s*image:\s*(.+)$/m);
304
- expect(imageMatch).not.toBeNull();
305
- const imageRef = imageMatch[1].trim();
306
- // Must contain a dot-separated registry hostname (ghcr.io, docker.io, etc.)
307
- expect(imageRef).toMatch(/^[a-z0-9][\w.-]+\.[a-z]{2,}\//);
308
- expect(imageRef).toContain('olam-host-cp');
309
- });
310
- it('bootstrap tag target for host-cp matches compose.yaml image field', () => {
311
- // bootstrap.ts step 3b tags the digest-pulled image as `${registry}/olam-host-cp:latest`.
312
- // compose.yaml's `image:` field must use the same canonical name so
313
- // `docker compose up` finds the locally-tagged image and doesn't re-pull.
314
- const yaml = fs.readFileSync(composeYamlPath, 'utf-8');
315
- const hostCpSection = yaml.match(/container_name: olam-host-cp[\s\S]*?(?=\n \w|\nnetworks:|\z)/)?.[0] ?? '';
316
- const imageMatch = hostCpSection.match(/^\s*image:\s*(.+)$/m);
317
- expect(imageMatch).not.toBeNull();
318
- const composeImage = imageMatch[1].trim();
319
- // bootstrap.ts hard-codes the tag as `${registry}/olam-host-cp:latest`
320
- // where registry defaults to 'ghcr.io/pleri' (from image-digests.json).
321
- // The compose.yaml image field must match this pattern.
322
- const registryPrefix = composeImage.replace(/\/olam-host-cp.*$/, '');
323
- const expectedTag = `${registryPrefix}/olam-host-cp:latest`;
324
- expect(composeImage).toBe(expectedTag);
325
- });
326
- });
327
- //# sourceMappingURL=host-cp.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"host-cp.test.js","sourceRoot":"","sources":["../../src/__tests__/host-cp.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=image-presence.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"image-presence.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/image-presence.test.ts"],"names":[],"mappings":""}
@@ -1,44 +0,0 @@
1
- // C5 (Phase C — fresh-install self-bootstrap): image-presence pre-flight
2
- // helper. Decision #6 (revised pass 3) — `olam bootstrap` is the SOLE
3
- // on-ramp; commands that need an image fail-fast with a named remedy
4
- // instead of auto-pulling.
5
- import { describe, it, expect } from 'vitest';
6
- import { checkImagePresent } from '../image-presence.js';
7
- describe('checkImagePresent', () => {
8
- it('returns present:true when docker image inspect exits 0', () => {
9
- const inspect = () => ({ exitCode: 0 });
10
- const result = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-devbox:0.2.0', callerCommand: 'olam create' }, inspect);
11
- expect(result.present).toBe(true);
12
- expect(result.stderrLine).toBe('');
13
- });
14
- it('returns present:false with named remedy when inspect exits non-zero', () => {
15
- const inspect = () => ({ exitCode: 1 });
16
- const result = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-devbox:0.2.0', callerCommand: 'olam create' }, inspect);
17
- expect(result.present).toBe(false);
18
- expect(result.stderrLine).toContain("'ghcr.io/pleri/olam-devbox:0.2.0' not found locally");
19
- expect(result.stderrLine).toContain('olam create does not auto-pull');
20
- expect(result.stderrLine).toContain('Remedy: run `olam bootstrap`');
21
- });
22
- it('names the caller command in the remedy (so operators know which command failed)', () => {
23
- const inspect = () => ({ exitCode: 1 });
24
- const create = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-devbox:0.2.0', callerCommand: 'olam create' }, inspect);
25
- const hostCp = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-host-cp:0.2.0', callerCommand: 'olam host-cp start' }, inspect);
26
- const authUp = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-auth:0.2.0', callerCommand: 'olam auth up' }, inspect);
27
- expect(create.stderrLine).toContain('olam create');
28
- expect(hostCp.stderrLine).toContain('olam host-cp start');
29
- expect(authUp.stderrLine).toContain('olam auth up');
30
- });
31
- it('mentions Decision #6 lineage in the remedy (audit trail for support questions)', () => {
32
- const inspect = () => ({ exitCode: 1 });
33
- const result = checkImagePresent({ imageRef: 'foo:bar', callerCommand: 'olam x' }, inspect);
34
- expect(result.stderrLine).toContain('Decision #6');
35
- expect(result.stderrLine).toContain('olam-fresh-install-self-bootstrap');
36
- });
37
- it('handles inspect timeout / docker daemon down (negative exitCode)', () => {
38
- const inspect = () => ({ exitCode: -1 });
39
- const result = checkImagePresent({ imageRef: 'foo:bar', callerCommand: 'olam x' }, inspect);
40
- expect(result.present).toBe(false);
41
- expect(result.stderrLine).toContain('not found locally');
42
- });
43
- });
44
- //# sourceMappingURL=image-presence.test.js.map