@knowzai/cli 0.4.0 → 0.5.1

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 (298) hide show
  1. package/README.md +68 -0
  2. package/assets/portable/releases.json +66 -2
  3. package/assets/portable/runtime-contract.json +2 -2
  4. package/assets/selfhosted/docker-compose.yml +8 -2
  5. package/assets/selfhosted/releases.json +130 -5
  6. package/bin/run.js +7 -2
  7. package/bin/wasm-runtime.js +27 -0
  8. package/dist/.tsbuildinfo +1 -1
  9. package/dist/base-command.d.ts +2 -0
  10. package/dist/base-command.d.ts.map +1 -1
  11. package/dist/base-command.js +11 -1
  12. package/dist/base-command.js.map +1 -1
  13. package/dist/commands/ask.d.ts.map +1 -1
  14. package/dist/commands/ask.js +2 -0
  15. package/dist/commands/ask.js.map +1 -1
  16. package/dist/commands/backup.d.ts +1 -0
  17. package/dist/commands/backup.d.ts.map +1 -1
  18. package/dist/commands/backup.js +29 -10
  19. package/dist/commands/backup.js.map +1 -1
  20. package/dist/commands/chat.d.ts +2 -1
  21. package/dist/commands/chat.d.ts.map +1 -1
  22. package/dist/commands/chat.js +10 -4
  23. package/dist/commands/chat.js.map +1 -1
  24. package/dist/commands/down.d.ts +14 -27
  25. package/dist/commands/down.d.ts.map +1 -1
  26. package/dist/commands/down.js +6 -31
  27. package/dist/commands/down.js.map +1 -1
  28. package/dist/commands/knowledge/amend.d.ts +2 -2
  29. package/dist/commands/knowledge/amend.d.ts.map +1 -1
  30. package/dist/commands/knowledge/amend.js +5 -0
  31. package/dist/commands/knowledge/amend.js.map +1 -1
  32. package/dist/commands/login.d.ts +4 -0
  33. package/dist/commands/login.d.ts.map +1 -1
  34. package/dist/commands/login.js +21 -2
  35. package/dist/commands/login.js.map +1 -1
  36. package/dist/commands/menu.js +1 -1
  37. package/dist/commands/menu.js.map +1 -1
  38. package/dist/commands/platform/destroy.d.ts +15 -5
  39. package/dist/commands/platform/destroy.d.ts.map +1 -1
  40. package/dist/commands/platform/destroy.js +6 -33
  41. package/dist/commands/platform/destroy.js.map +1 -1
  42. package/dist/commands/platform/doctor.d.ts +18 -3
  43. package/dist/commands/platform/doctor.d.ts.map +1 -1
  44. package/dist/commands/platform/doctor.js +4 -10
  45. package/dist/commands/platform/doctor.js.map +1 -1
  46. package/dist/commands/platform/down.d.ts +15 -15
  47. package/dist/commands/platform/down.d.ts.map +1 -1
  48. package/dist/commands/platform/down.js +4 -8
  49. package/dist/commands/platform/down.js.map +1 -1
  50. package/dist/commands/platform/logs.d.ts +15 -14
  51. package/dist/commands/platform/logs.d.ts.map +1 -1
  52. package/dist/commands/platform/logs.js +5 -16
  53. package/dist/commands/platform/logs.js.map +1 -1
  54. package/dist/commands/platform/reset.d.ts +16 -13
  55. package/dist/commands/platform/reset.d.ts.map +1 -1
  56. package/dist/commands/platform/reset.js +7 -36
  57. package/dist/commands/platform/reset.js.map +1 -1
  58. package/dist/commands/platform/setup.d.ts +1 -0
  59. package/dist/commands/platform/setup.d.ts.map +1 -1
  60. package/dist/commands/platform/setup.js +1 -1
  61. package/dist/commands/platform/setup.js.map +1 -1
  62. package/dist/commands/platform/status.d.ts +14 -16
  63. package/dist/commands/platform/status.d.ts.map +1 -1
  64. package/dist/commands/platform/status.js +4 -29
  65. package/dist/commands/platform/status.js.map +1 -1
  66. package/dist/commands/restore.d.ts +10 -1
  67. package/dist/commands/restore.d.ts.map +1 -1
  68. package/dist/commands/restore.js +56 -13
  69. package/dist/commands/restore.js.map +1 -1
  70. package/dist/commands/runtime/destroy.d.ts +29 -0
  71. package/dist/commands/runtime/destroy.d.ts.map +1 -0
  72. package/dist/commands/runtime/destroy.js +13 -0
  73. package/dist/commands/runtime/destroy.js.map +1 -0
  74. package/dist/commands/runtime/doctor.d.ts +27 -0
  75. package/dist/commands/runtime/doctor.d.ts.map +1 -0
  76. package/dist/commands/runtime/doctor.js +11 -0
  77. package/dist/commands/runtime/doctor.js.map +1 -0
  78. package/dist/commands/runtime/logs.d.ts +28 -0
  79. package/dist/commands/runtime/logs.d.ts.map +1 -0
  80. package/dist/commands/runtime/logs.js +12 -0
  81. package/dist/commands/runtime/logs.js.map +1 -0
  82. package/dist/commands/runtime/status.d.ts +27 -0
  83. package/dist/commands/runtime/status.d.ts.map +1 -0
  84. package/dist/commands/runtime/status.js +11 -0
  85. package/dist/commands/runtime/status.js.map +1 -0
  86. package/dist/commands/runtime/upgrade.d.ts +28 -0
  87. package/dist/commands/runtime/upgrade.d.ts.map +1 -0
  88. package/dist/commands/runtime/upgrade.js +12 -0
  89. package/dist/commands/runtime/upgrade.js.map +1 -0
  90. package/dist/commands/setup.d.ts +1 -0
  91. package/dist/commands/setup.d.ts.map +1 -1
  92. package/dist/commands/setup.js +1 -1
  93. package/dist/commands/transfer.js +1 -1
  94. package/dist/commands/transfer.js.map +1 -1
  95. package/dist/commands/up.d.ts +3 -11
  96. package/dist/commands/up.d.ts.map +1 -1
  97. package/dist/commands/up.js +48 -37
  98. package/dist/commands/up.js.map +1 -1
  99. package/dist/commands/whoami.js +1 -1
  100. package/dist/commands/whoami.js.map +1 -1
  101. package/dist/hooks/init/launch-menu.d.ts.map +1 -1
  102. package/dist/hooks/init/launch-menu.js +2 -0
  103. package/dist/hooks/init/launch-menu.js.map +1 -1
  104. package/dist/lib/anonymous-registry.d.ts +3 -0
  105. package/dist/lib/anonymous-registry.d.ts.map +1 -0
  106. package/dist/lib/anonymous-registry.js +25 -0
  107. package/dist/lib/anonymous-registry.js.map +1 -0
  108. package/dist/lib/async-process.d.ts +15 -0
  109. package/dist/lib/async-process.d.ts.map +1 -0
  110. package/dist/lib/async-process.js +145 -0
  111. package/dist/lib/async-process.js.map +1 -0
  112. package/dist/lib/backup-process.d.ts +4 -0
  113. package/dist/lib/backup-process.d.ts.map +1 -0
  114. package/dist/lib/backup-process.js +20 -0
  115. package/dist/lib/backup-process.js.map +1 -0
  116. package/dist/lib/brand.d.ts +25 -0
  117. package/dist/lib/brand.d.ts.map +1 -0
  118. package/dist/lib/brand.js +105 -0
  119. package/dist/lib/brand.js.map +1 -0
  120. package/dist/lib/chat.d.ts +1 -0
  121. package/dist/lib/chat.d.ts.map +1 -1
  122. package/dist/lib/chat.js +12 -3
  123. package/dist/lib/chat.js.map +1 -1
  124. package/dist/lib/compose-status.d.ts +14 -0
  125. package/dist/lib/compose-status.d.ts.map +1 -0
  126. package/dist/lib/compose-status.js +31 -0
  127. package/dist/lib/compose-status.js.map +1 -0
  128. package/dist/lib/container-engine.js +2 -2
  129. package/dist/lib/container-engine.js.map +1 -1
  130. package/dist/lib/local-engine-context.d.ts +16 -0
  131. package/dist/lib/local-engine-context.d.ts.map +1 -0
  132. package/dist/lib/local-engine-context.js +123 -0
  133. package/dist/lib/local-engine-context.js.map +1 -0
  134. package/dist/lib/local-mcp-server.d.ts.map +1 -1
  135. package/dist/lib/local-mcp-server.js +18 -2
  136. package/dist/lib/local-mcp-server.js.map +1 -1
  137. package/dist/lib/login-guided.d.ts.map +1 -1
  138. package/dist/lib/login-guided.js +2 -1
  139. package/dist/lib/login-guided.js.map +1 -1
  140. package/dist/lib/login.d.ts +3 -0
  141. package/dist/lib/login.d.ts.map +1 -1
  142. package/dist/lib/login.js +25 -5
  143. package/dist/lib/login.js.map +1 -1
  144. package/dist/lib/oci-registry-probe.d.ts +43 -0
  145. package/dist/lib/oci-registry-probe.d.ts.map +1 -0
  146. package/dist/lib/oci-registry-probe.js +110 -0
  147. package/dist/lib/oci-registry-probe.js.map +1 -0
  148. package/dist/lib/peer-command.d.ts.map +1 -1
  149. package/dist/lib/peer-command.js +3 -1
  150. package/dist/lib/peer-command.js.map +1 -1
  151. package/dist/lib/platform-acquisition.d.ts +27 -0
  152. package/dist/lib/platform-acquisition.d.ts.map +1 -0
  153. package/dist/lib/platform-acquisition.js +21 -0
  154. package/dist/lib/platform-acquisition.js.map +1 -0
  155. package/dist/lib/platform-backup.d.ts +1 -0
  156. package/dist/lib/platform-backup.d.ts.map +1 -1
  157. package/dist/lib/platform-backup.js +375 -157
  158. package/dist/lib/platform-backup.js.map +1 -1
  159. package/dist/lib/platform-command-shared.d.ts +2 -0
  160. package/dist/lib/platform-command-shared.d.ts.map +1 -1
  161. package/dist/lib/platform-command-shared.js +13 -5
  162. package/dist/lib/platform-command-shared.js.map +1 -1
  163. package/dist/lib/platform-edition.d.ts +3 -6
  164. package/dist/lib/platform-edition.d.ts.map +1 -1
  165. package/dist/lib/platform-edition.js +9 -17
  166. package/dist/lib/platform-edition.js.map +1 -1
  167. package/dist/lib/platform-frontdoor-runtime.d.ts.map +1 -1
  168. package/dist/lib/platform-frontdoor-runtime.js +42 -10
  169. package/dist/lib/platform-frontdoor-runtime.js.map +1 -1
  170. package/dist/lib/platform-frontdoor.d.ts +32 -5
  171. package/dist/lib/platform-frontdoor.d.ts.map +1 -1
  172. package/dist/lib/platform-frontdoor.js +249 -171
  173. package/dist/lib/platform-frontdoor.js.map +1 -1
  174. package/dist/lib/platform-image-admission.d.ts +16 -0
  175. package/dist/lib/platform-image-admission.d.ts.map +1 -0
  176. package/dist/lib/platform-image-admission.js +73 -0
  177. package/dist/lib/platform-image-admission.js.map +1 -0
  178. package/dist/lib/platform-image-source.d.ts.map +1 -1
  179. package/dist/lib/platform-image-source.js +2 -0
  180. package/dist/lib/platform-image-source.js.map +1 -1
  181. package/dist/lib/platform-lifecycle.d.ts +4 -0
  182. package/dist/lib/platform-lifecycle.d.ts.map +1 -1
  183. package/dist/lib/platform-lifecycle.js +42 -8
  184. package/dist/lib/platform-lifecycle.js.map +1 -1
  185. package/dist/lib/platform-onboarding.d.ts.map +1 -1
  186. package/dist/lib/platform-onboarding.js +1 -1
  187. package/dist/lib/platform-onboarding.js.map +1 -1
  188. package/dist/lib/platform-preflight.d.ts +6 -3
  189. package/dist/lib/platform-preflight.d.ts.map +1 -1
  190. package/dist/lib/platform-preflight.js +52 -19
  191. package/dist/lib/platform-preflight.js.map +1 -1
  192. package/dist/lib/platform-release-manifest.d.ts +6 -1
  193. package/dist/lib/platform-release-manifest.d.ts.map +1 -1
  194. package/dist/lib/platform-release-manifest.js +10 -0
  195. package/dist/lib/platform-release-manifest.js.map +1 -1
  196. package/dist/lib/platform-runtime.d.ts +5 -2
  197. package/dist/lib/platform-runtime.d.ts.map +1 -1
  198. package/dist/lib/platform-runtime.js +7 -40
  199. package/dist/lib/platform-runtime.js.map +1 -1
  200. package/dist/lib/portability-shared.d.ts +1 -1
  201. package/dist/lib/portability-shared.d.ts.map +1 -1
  202. package/dist/lib/portability-shared.js +6 -2
  203. package/dist/lib/portability-shared.js.map +1 -1
  204. package/dist/lib/portable-release-digests.d.ts +5 -0
  205. package/dist/lib/portable-release-digests.d.ts.map +1 -0
  206. package/dist/lib/portable-release-digests.js +53 -0
  207. package/dist/lib/portable-release-digests.js.map +1 -0
  208. package/dist/lib/release-pin-matrix.d.ts +29 -0
  209. package/dist/lib/release-pin-matrix.d.ts.map +1 -0
  210. package/dist/lib/release-pin-matrix.js +67 -0
  211. package/dist/lib/release-pin-matrix.js.map +1 -0
  212. package/dist/lib/repo-local-mcp-backend.d.ts +2 -5
  213. package/dist/lib/repo-local-mcp-backend.d.ts.map +1 -1
  214. package/dist/lib/repo-local-mcp-backend.js +61 -44
  215. package/dist/lib/repo-local-mcp-backend.js.map +1 -1
  216. package/dist/lib/runtime-backup-archive.d.ts +20 -0
  217. package/dist/lib/runtime-backup-archive.d.ts.map +1 -0
  218. package/dist/lib/runtime-backup-archive.js +269 -0
  219. package/dist/lib/runtime-backup-archive.js.map +1 -0
  220. package/dist/lib/runtime-command.d.ts +37 -0
  221. package/dist/lib/runtime-command.d.ts.map +1 -0
  222. package/dist/lib/runtime-command.js +50 -0
  223. package/dist/lib/runtime-command.js.map +1 -0
  224. package/dist/lib/runtime-discovery.d.ts +18 -0
  225. package/dist/lib/runtime-discovery.d.ts.map +1 -0
  226. package/dist/lib/runtime-discovery.js +56 -0
  227. package/dist/lib/runtime-discovery.js.map +1 -0
  228. package/dist/lib/runtime-lock.d.ts +3 -0
  229. package/dist/lib/runtime-lock.d.ts.map +1 -0
  230. package/dist/lib/runtime-lock.js +50 -0
  231. package/dist/lib/runtime-lock.js.map +1 -0
  232. package/dist/lib/runtime-operations.d.ts +98 -0
  233. package/dist/lib/runtime-operations.d.ts.map +1 -0
  234. package/dist/lib/runtime-operations.js +182 -0
  235. package/dist/lib/runtime-operations.js.map +1 -0
  236. package/dist/lib/runtime-provider.d.ts +17 -0
  237. package/dist/lib/runtime-provider.d.ts.map +1 -0
  238. package/dist/lib/runtime-provider.js +52 -0
  239. package/dist/lib/runtime-provider.js.map +1 -0
  240. package/dist/lib/runtime-status.d.ts +24 -0
  241. package/dist/lib/runtime-status.d.ts.map +1 -0
  242. package/dist/lib/runtime-status.js +128 -0
  243. package/dist/lib/runtime-status.js.map +1 -0
  244. package/dist/lib/selfhosted-backup.d.ts +37 -0
  245. package/dist/lib/selfhosted-backup.d.ts.map +1 -0
  246. package/dist/lib/selfhosted-backup.js +446 -0
  247. package/dist/lib/selfhosted-backup.js.map +1 -0
  248. package/dist/lib/selfhosted-keyring.d.ts +19 -0
  249. package/dist/lib/selfhosted-keyring.d.ts.map +1 -0
  250. package/dist/lib/selfhosted-keyring.js +193 -0
  251. package/dist/lib/selfhosted-keyring.js.map +1 -0
  252. package/dist/lib/selfhosted-lifecycle.d.ts +45 -7
  253. package/dist/lib/selfhosted-lifecycle.d.ts.map +1 -1
  254. package/dist/lib/selfhosted-lifecycle.js +214 -31
  255. package/dist/lib/selfhosted-lifecycle.js.map +1 -1
  256. package/dist/lib/selfhosted-login.d.ts +34 -0
  257. package/dist/lib/selfhosted-login.d.ts.map +1 -0
  258. package/dist/lib/selfhosted-login.js +116 -0
  259. package/dist/lib/selfhosted-login.js.map +1 -0
  260. package/dist/lib/selfhosted-readiness.d.ts +3 -0
  261. package/dist/lib/selfhosted-readiness.d.ts.map +1 -0
  262. package/dist/lib/selfhosted-readiness.js +24 -0
  263. package/dist/lib/selfhosted-readiness.js.map +1 -0
  264. package/dist/lib/setup-capabilities.d.ts +15 -0
  265. package/dist/lib/setup-capabilities.d.ts.map +1 -0
  266. package/dist/lib/setup-capabilities.js +21 -0
  267. package/dist/lib/setup-capabilities.js.map +1 -0
  268. package/dist/lib/setup-console.d.ts +8 -0
  269. package/dist/lib/setup-console.d.ts.map +1 -1
  270. package/dist/lib/setup-console.js +104 -74
  271. package/dist/lib/setup-console.js.map +1 -1
  272. package/dist/lib/setup-page.d.ts +12 -0
  273. package/dist/lib/setup-page.d.ts.map +1 -1
  274. package/dist/lib/setup-page.js +40 -22
  275. package/dist/lib/setup-page.js.map +1 -1
  276. package/dist/lib/setup-plan.d.ts +1 -0
  277. package/dist/lib/setup-plan.d.ts.map +1 -1
  278. package/dist/lib/setup-plan.js +3 -0
  279. package/dist/lib/setup-plan.js.map +1 -1
  280. package/dist/lib/setup-server.d.ts +11 -2
  281. package/dist/lib/setup-server.d.ts.map +1 -1
  282. package/dist/lib/setup-server.js +139 -28
  283. package/dist/lib/setup-server.js.map +1 -1
  284. package/dist/lib/ui.d.ts +3 -1
  285. package/dist/lib/ui.d.ts.map +1 -1
  286. package/dist/lib/ui.js +39 -7
  287. package/dist/lib/ui.js.map +1 -1
  288. package/dist/lib/up-result.d.ts +3 -0
  289. package/dist/lib/up-result.d.ts.map +1 -1
  290. package/dist/lib/up-result.js.map +1 -1
  291. package/dist/menu/run.js +1 -1
  292. package/dist/menu/run.js.map +1 -1
  293. package/dist/menu/should-launch.d.ts +0 -14
  294. package/dist/menu/should-launch.d.ts.map +1 -1
  295. package/dist/menu/should-launch.js +5 -1
  296. package/dist/menu/should-launch.js.map +1 -1
  297. package/oclif.manifest.json +5490 -3263
  298. package/package.json +14 -11
@@ -1,18 +1,23 @@
1
- import { readFileSync } from 'node:fs';
1
+ import { validateSelfHostedSetupOptions } from './setup-capabilities.js';
2
+ import { hasAdmittedPlatformImages } from './platform-image-admission.js';
3
+ import { existsSync, readFileSync } from 'node:fs';
2
4
  import { join } from 'node:path';
3
5
  import { registerSecret, getPlatformDefaultProvider, setPlatformDefaultProvider, } from '@knowzai/core';
4
6
  import { applyPlatform, resolvePackagedAssetsDir, } from './platform-lifecycle.js';
5
7
  import { parseEnv, redactPlatformSecrets, } from './platform-runtime.js';
6
8
  import { resolveProviderSelection, } from './platform-provider-prompt.js';
7
- import { acquireGhcrLogin, detectRepoCheckout, resolveImageSource } from './platform-image-source.js';
9
+ import { acquireGhcrLogin, resolveImageSource } from './platform-image-source.js';
8
10
  import { resolveEditionIntent, shouldUseSelfHostedEdition } from './platform-edition.js';
9
- import { STABLE_SELFHOSTED_RELEASE, applySelfHosted, resolvePackagedSelfHostedAssetsDir, selfHostedToPlatformResult, } from './selfhosted-lifecycle.js';
11
+ import { STABLE_SELFHOSTED_RELEASE, applySelfHostedAsync, resolvePackagedSelfHostedAssetsDir, selfHostedToPlatformResult, } from './selfhosted-lifecycle.js';
10
12
  import { runOnboarding, runOnboardingProviderProbe, waitForOnboardingReady, DEFAULT_LOCAL_PROFILE, } from './platform-onboarding.js';
11
13
  import { ensureEngineReady as ensureContainerEngineReady, } from './container-engine.js';
12
14
  import { withSpinner } from './ui.js';
13
15
  import { buildRegistryLoginInvocation } from './platform-image-source.js';
14
- import { parsePlatformReleaseManifest, PlatformReleaseCatalogHttpError, releaseFreshnessNotice, resolvePlatformRelease, SelfServeResolutionError, } from './platform-release-manifest.js';
16
+ import { catalogStaleError, newestManifestVersion, parsePlatformReleaseManifest, PlatformReleaseCatalogHttpError, releaseFreshnessNotice, resolvePlatformRelease, SelfServeResolutionError, } from './platform-release-manifest.js';
17
+ import { pullRegistryOriginFromSource, registryNotOciError, } from './oci-registry-probe.js';
18
+ import { catalogLagsPackagedPin } from './release-pin-matrix.js';
15
19
  import { normalizeMothershipOrigin, } from './activate.js';
20
+ import { discoverRuntime } from './runtime-discovery.js';
16
21
  import { AGE_ELIGIBILITY_TEXT, AGE_ELIGIBILITY_UNDER_13_MESSAGE } from './login.js';
17
22
  import { KnowzError, ExitCode } from '@knowzai/core';
18
23
  /**
@@ -36,7 +41,7 @@ import { KnowzError, ExitCode } from '@knowzai/core';
36
41
  * up.test.ts anti-rot tripwire). The /release-platform skill bumps BOTH in the
37
42
  * same release commit; bump one without the other and the tripwire goes red.
38
43
  */
39
- export const STABLE_PORTABLE_RELEASE = '0.44.1';
44
+ export const STABLE_PORTABLE_RELEASE = '0.47.8';
40
45
  export const DEFAULT_PORTABLE_VERSION = STABLE_PORTABLE_RELEASE;
41
46
  const DEFAULT_INSTANCE_NAME = 'knowz';
42
47
  /**
@@ -102,6 +107,88 @@ function licenseRequiredError(preflight) {
102
107
  const detail = sanitizeProblemDetail(preflight.detail);
103
108
  return new SelfServeResolutionError(`${detail ?? LICENSE_REQUIRED_FALLBACK} ${LICENSE_REQUIRED_REMEDIATION}`, 'LICENSE_REQUIRED');
104
109
  }
110
+ function registryAuthUnreachable(mothershipUrl) {
111
+ return new SelfServeResolutionError(`Could not reach the Knowz image authorization service at ${mothershipUrl}/api/v1/registry/token. ` +
112
+ 'Check your network connection, then retry.', 'REGISTRY_AUTH_UNREACHABLE');
113
+ }
114
+ /**
115
+ * Commercial grant / entitlement preflight for paid portable pulls.
116
+ * `GHCR_PULL_TOKEN` may authenticate to GHCR after this returns; it must never skip this gate.
117
+ */
118
+ async function resolveEntitledInstallation(input) {
119
+ const { deps, mothershipUrl, log } = input;
120
+ if (!deps.loadStoredActivation || !deps.preflightRegistryCredential) {
121
+ throw new Error('Portable image authorization dependencies are not configured.');
122
+ }
123
+ let activation = await deps.loadStoredActivation({
124
+ projectName: input.projectName,
125
+ configDir: input.configDir,
126
+ engine: input.engine,
127
+ });
128
+ let activatedNow = false;
129
+ if (!activation) {
130
+ if (input.json || !deps.activateInstallation)
131
+ throw activationRequired();
132
+ log("This device isn't linked to a Knowz account yet.");
133
+ log('Linking is free and takes about a minute — your browser will open so you can approve this device.');
134
+ log('');
135
+ try {
136
+ activation = await deps.activateInstallation({
137
+ projectName: input.projectName,
138
+ configDir: input.configDir,
139
+ mothershipUrl,
140
+ engine: input.engine,
141
+ });
142
+ activatedNow = true;
143
+ }
144
+ catch {
145
+ throw e5();
146
+ }
147
+ }
148
+ let preflight = await deps.preflightRegistryCredential({ mothershipUrl, activation });
149
+ if (preflight.kind === 'transport-failure')
150
+ throw registryAuthUnreachable(mothershipUrl);
151
+ let preflightStatus = preflight.status;
152
+ if (preflightStatus === 403)
153
+ throw licenseRequiredError(preflight);
154
+ if (preflightStatus === 410)
155
+ throw e4(activation.installationId);
156
+ if (preflightStatus === 401) {
157
+ if (activatedNow || !input.canPrompt || !deps.confirmReactivation || !deps.activateInstallation) {
158
+ throw new SelfServeResolutionError(`Stored installation ${activation.installationId} is no longer valid. Run 'knowz activate' to link this device again.`, 'INSTALLATION_INVALID');
159
+ }
160
+ const accepted = await deps.confirmReactivation(activation.installationId);
161
+ if (!accepted)
162
+ throw e5();
163
+ try {
164
+ activation = await deps.activateInstallation({
165
+ projectName: input.projectName,
166
+ configDir: input.configDir,
167
+ mothershipUrl,
168
+ engine: input.engine,
169
+ });
170
+ activatedNow = true;
171
+ }
172
+ catch {
173
+ throw e5();
174
+ }
175
+ preflight = await deps.preflightRegistryCredential({ mothershipUrl, activation });
176
+ if (preflight.kind === 'transport-failure')
177
+ throw registryAuthUnreachable(mothershipUrl);
178
+ preflightStatus = preflight.status;
179
+ if (preflightStatus === 403)
180
+ throw licenseRequiredError(preflight);
181
+ if (preflightStatus === 410)
182
+ throw e4(activation.installationId);
183
+ }
184
+ if (preflightStatus !== 200) {
185
+ throw new SelfServeResolutionError(`Knowz could not authorize installation ${activation.installationId} for image pulls (HTTP ${preflightStatus}).`, 'INSTALLATION_INVALID');
186
+ }
187
+ registerSecret(activation.credential);
188
+ if (activatedNow)
189
+ log(`✓ Linked — installation "${input.projectName}"`);
190
+ return activation;
191
+ }
105
192
  function sanitizeProblemDetail(detail) {
106
193
  if (typeof detail !== 'string' || detail.length === 0)
107
194
  return undefined;
@@ -188,19 +275,28 @@ function emitNextSteps(log) {
188
275
  }
189
276
  export async function runFrontDoor(input, deps) {
190
277
  const log = input.json
191
- ? (_line) => { }
278
+ ? deps.progressLog ?? ((_line) => { })
192
279
  : deps.log ?? ((line) => console.log(line));
280
+ const explicitIntent = resolveEditionIntent({ flags: { edition: input.edition }, env: deps.env });
281
+ const existing = discoverRuntime({ name: input.base.name, configDir: input.base.configDir, edition: explicitIntent.explicit ? explicitIntent.edition : undefined });
282
+ const editionIntent = resolveEditionIntent({ flags: { edition: input.edition }, env: deps.env, existingEdition: existing?.edition });
283
+ const useSelfHosted = shouldUseSelfHostedEdition({ intent: editionIntent, imageFlags: input.imageFlags });
284
+ if (useSelfHosted)
285
+ validateSelfHostedSetupOptions({ provider: input.providerFlags.ai, storage: input.base.storage, publicUrls: input.base.publicUrls, orgName: input.base.orgName, seedDemo: input.seedDemo, components: input.base.components, axes: input.base.axes });
286
+ const checkCanceled = () => { if (input.signal?.aborted)
287
+ throw Object.assign(new Error('Setup canceled. Runtime data has been preserved.'), { code: 'SETUP_CANCELED', exitCode: 130 }); };
288
+ checkCanceled();
193
289
  const ports = {
194
- api: input.base.ports?.api ?? 5000,
195
- web: input.base.ports?.web ?? 3000,
196
- mcp: input.base.ports?.mcp ?? 3001,
197
- publicSite: input.base.ports?.publicSite ?? 3002,
290
+ api: input.base.ports?.api ?? existing?.state.ports.api ?? 5000,
291
+ web: input.base.ports?.web ?? existing?.state.ports.web ?? 3000,
292
+ mcp: input.base.ports?.mcp ?? existing?.state.ports.mcp ?? 3001,
293
+ publicSite: input.base.ports?.publicSite ?? existing?.state.ports.publicSite ?? 3002,
198
294
  };
199
295
  // 0) Environment-only preflight. It deliberately runs before config loading
200
296
  // or provider prompting so credentials are never collected on an unusable host.
201
297
  let engine;
202
298
  if (deps.preflight) {
203
- let report = await deps.preflight({ name: input.base.name, configDir: input.base.configDir, ports });
299
+ let report = await deps.preflight({ name: input.base.name, configDir: input.base.configDir, ports, components: input.base.components ?? existing?.state.components, edition: editionIntent.edition });
204
300
  engine = report.engine;
205
301
  if (!report.ok) {
206
302
  const machineIsFixable = report.engine?.engine === 'podman'
@@ -212,7 +308,7 @@ export async function runFrontDoor(input, deps) {
212
308
  ? withSpinner
213
309
  : async (_text, run) => run();
214
310
  await (deps.ensureEngineReady ?? ensureContainerEngineReady)(report.engine, deps.runner, progress);
215
- report = await deps.preflight({ name: input.base.name, configDir: input.base.configDir, ports });
311
+ report = await deps.preflight({ name: input.base.name, configDir: input.base.configDir, ports, components: input.base.components ?? existing?.state.components, edition: editionIntent.edition });
216
312
  engine = report.engine;
217
313
  if (!report.ok)
218
314
  throwPreflightFailure(report);
@@ -220,7 +316,7 @@ export async function runFrontDoor(input, deps) {
220
316
  }
221
317
  // 1) AI-provider picker (flags → persisted default → interactive → offline fallback).
222
318
  const cfg = deps.loadConfig();
223
- const cfgDefault = getPlatformDefaultProvider(cfg);
319
+ const cfgDefault = existing?.state.provider ?? getPlatformDefaultProvider(cfg);
224
320
  const canPrompt = deps.isTty && !input.json;
225
321
  const interactivePromptOccurred = canPrompt &&
226
322
  !input.providerFlags.offline &&
@@ -277,6 +373,7 @@ export async function runFrontDoor(input, deps) {
277
373
  }
278
374
  const applyInput = {
279
375
  ...input.base,
376
+ version: input.versionExplicit ? input.base.version : existing?.state.version ?? input.base.version,
280
377
  provider: selection.provider,
281
378
  azure: selection.azure ?? input.base.azure,
282
379
  gcp: selection.gcp ?? input.base.gcp,
@@ -286,80 +383,57 @@ export async function runFrontDoor(input, deps) {
286
383
  };
287
384
  // 2) Edition then image source. Unentitled `knowz up` uses public selfhosted
288
385
  // images; the portable resolver is unchanged when edition is portable.
289
- const envForEdition = input.ghcrToken
290
- ? { ...deps.env, GHCR_PULL_TOKEN: input.ghcrToken }
291
- : deps.env;
292
- const editionIntent = resolveEditionIntent({
293
- flags: { edition: input.edition },
294
- env: envForEdition,
295
- });
296
- const checkoutDetected = Boolean(detectRepoCheckout(deps.cwd, deps.pathExists));
297
- let useSelfHosted = shouldUseSelfHostedEdition({
298
- intent: editionIntent,
299
- imageFlags: input.imageFlags,
300
- checkoutDetected,
301
- });
302
- // R-ED3: omitted edition + stored kzin_ may promote to portable after a 200
303
- // preflight. 403/401/410/transport stay selfhosted and MUST NOT reactivate.
304
- if (useSelfHosted &&
305
- !editionIntent.explicit &&
306
- deps.loadStoredActivation &&
307
- deps.preflightRegistryCredential) {
308
- const mothershipUrl = normalizeMothershipOrigin(deps.env.KNOWZ_LICENSING_MOTHERSHIP_URL?.trim() || DEFAULT_MOTHERSHIP_URL);
309
- try {
310
- const stored = await deps.loadStoredActivation({
311
- projectName: input.base.name,
312
- configDir: input.base.configDir,
313
- engine: engine ?? LEGACY_DOCKER_ENGINE,
314
- });
315
- if (stored) {
316
- const preflight = await deps.preflightRegistryCredential({
317
- mothershipUrl,
318
- activation: stored,
319
- });
320
- if (preflight.kind === 'http-response' && preflight.status === 200) {
321
- useSelfHosted = false;
322
- }
323
- else {
324
- log('Stored installation is not entitled for portable images; using public selfhosted.');
325
- }
386
+ const savedRuntimeEnv = existing && existsSync(existing.paths.envFile) ? parseEnv(readFileSync(existing.paths.envFile, 'utf8')) : {};
387
+ const envForEdition = { ...deps.env, KNOWZ_REGISTRY: deps.env.KNOWZ_REGISTRY ?? savedRuntimeEnv.KNOWZ_REGISTRY, ...(input.ghcrToken ? { GHCR_PULL_TOKEN: input.ghcrToken } : {}) };
388
+ const cachedRestart = !useSelfHosted && existing && input.base.action === 'up'
389
+ && (!input.versionExplicit || input.base.version === existing.state.version)
390
+ && !input.imageFlags.build && !input.imageFlags.pull && !deps.env.KNOWZ_REGISTRY
391
+ && await (deps.admittedImages ?? hasAdmittedPlatformImages)({ name: input.base.name, version: existing.state.version, paths: existing.paths, engine: engine ?? LEGACY_DOCKER_ENGINE, components: existing.state.components ?? { mcp: true, publicSite: false } }, deps.asyncRunner ?? deps.runner);
392
+ const source = cachedRestart
393
+ ? { mode: 'preloaded', composeFile: existing.paths.composeFile, projectDir: existing.paths.runtimeDir, composePlan: 'config-up' }
394
+ : useSelfHosted
395
+ ? {
396
+ mode: 'pull',
397
+ composeFile: 'selfhosted',
398
+ projectDir: 'selfhosted',
399
+ composePlan: 'config-pull-up',
326
400
  }
327
- }
328
- catch {
329
- // Default knowz up must not fail because a mothership probe threw.
330
- }
331
- }
332
- const source = useSelfHosted
333
- ? {
334
- mode: 'pull',
335
- composeFile: 'selfhosted',
336
- projectDir: 'selfhosted',
337
- composePlan: 'config-pull-up',
338
- }
339
- : resolveImageSource({
340
- cwd: deps.cwd,
341
- flags: input.imageFlags,
342
- canPrompt: deps.isTty && Boolean(deps.ghcrPrompt),
343
- pathExists: deps.pathExists,
344
- packagedAssetsDir: deps.packagedAssetsDir,
345
- env: envForEdition,
346
- });
401
+ : resolveImageSource({
402
+ cwd: deps.cwd,
403
+ flags: input.imageFlags,
404
+ canPrompt: deps.isTty && Boolean(deps.ghcrPrompt),
405
+ pathExists: deps.pathExists,
406
+ packagedAssetsDir: deps.packagedAssetsDir,
407
+ env: envForEdition,
408
+ });
347
409
  const seam = source.mode === 'build'
348
410
  ? { composeFile: source.composeFile, projectDir: source.projectDir, composePlan: source.composePlan }
349
411
  : source.mode === 'pull'
350
412
  ? { composePlan: source.composePlan, registry: 'registry' in source ? source.registry ?? null : null }
351
413
  : { composePlan: source.composePlan };
414
+ const resultImageMode = cachedRestart ? existing.state.imageMode ?? 'pull' : source.mode;
415
+ if (input.imageFlags.pull)
416
+ seam.forceAcquisition = true;
352
417
  // §2026-08-31 R7: portable images are linux/amd64-only. Selfhosted GHCR is multi-arch.
353
418
  const hostArch = deps.hostArch ?? process.arch;
354
419
  if (!useSelfHosted && hostArch === 'arm64' && (source.mode === 'pull' || source.mode === 'preloaded')) {
355
420
  log('Note: knowz images run under amd64 emulation on this Mac; native arm64 images are not yet published.');
356
421
  }
357
422
  const selfServePlan = source.loginPlan?.kind === 'self-serve' ? source.loginPlan : undefined;
423
+ // #862: fail closed when the advertised facade is SPA HTML, not OCI.
424
+ // Skip when probeRegistry is omitted so injected unit tests stay network-free.
425
+ const pullOrigin = pullRegistryOriginFromSource(source);
426
+ if (pullOrigin && deps.probeRegistry) {
427
+ const probe = await deps.probeRegistry(pullOrigin);
428
+ if (!probe.ok)
429
+ throw registryNotOciError(pullOrigin, probe);
430
+ }
358
431
  if (selfServePlan) {
359
432
  const mothershipUrl = normalizeMothershipOrigin(deps.env.KNOWZ_LICENSING_MOTHERSHIP_URL?.trim() || DEFAULT_MOTHERSHIP_URL);
360
433
  const manifestUrl = `${mothershipUrl}${RELEASE_MANIFEST_PATH}`;
361
434
  log('◇ Checking Knowz releases…');
362
435
  let manifest;
436
+ let catalogFromMothership = true;
363
437
  try {
364
438
  const raw = await (deps.fetchReleaseManifest ?? defaultFetchReleaseManifest)(manifestUrl);
365
439
  // Parsing stays outside the fallback contract: a malformed 200 is a server
@@ -390,6 +464,7 @@ export async function runFrontDoor(input, deps) {
390
464
  ? await deps.loadPackagedReleaseManifest(fallbackPath)
391
465
  : JSON.parse(readFileSync(fallbackPath, 'utf8'));
392
466
  manifest = parsePlatformReleaseManifest(fallback);
467
+ catalogFromMothership = false;
393
468
  }
394
469
  catch {
395
470
  throw e1(manifestUrl);
@@ -397,6 +472,37 @@ export async function runFrontDoor(input, deps) {
397
472
  log(`Warning: could not reach the Knowz release catalog at ${manifestUrl} — using the release list packaged with this CLI, which may be out of date.`);
398
473
  }
399
474
  const flavor = selection.provider === 'gcp' ? 'portable-gcp' : 'portable';
475
+ // #865: a successful but stale mothership catalog must not silently pin an
476
+ // older blessed row. Skip packaged-fallback, empty catalog (existing
477
+ // RELEASE_NOT_PUBLISHED), and an explicit pin that is still blessed.
478
+ if (!input.versionExplicit && catalogFromMothership) {
479
+ const catalogNewest = newestManifestVersion(manifest, flavor);
480
+ if (catalogNewest) {
481
+ try {
482
+ const packagedPath = join(source.projectDir, 'releases.json');
483
+ const packagedRaw = deps.loadPackagedReleaseManifest
484
+ ? await deps.loadPackagedReleaseManifest(packagedPath)
485
+ : JSON.parse(readFileSync(packagedPath, 'utf8'));
486
+ const packaged = parsePlatformReleaseManifest(packagedRaw);
487
+ const stale = catalogLagsPackagedPin({
488
+ catalogNewest,
489
+ packagedNewest: newestManifestVersion(packaged, flavor),
490
+ });
491
+ if (stale.stale && stale.packagedNewest) {
492
+ throw catalogStaleError({
493
+ manifestUrl,
494
+ catalogNewest: stale.catalogNewest,
495
+ packagedNewest: stale.packagedNewest,
496
+ });
497
+ }
498
+ }
499
+ catch (staleError) {
500
+ if (staleError instanceof SelfServeResolutionError)
501
+ throw staleError;
502
+ // Packaged list unreadable — keep catalog resolution; do not invent a pin.
503
+ }
504
+ }
505
+ }
400
506
  const resolved = resolvePlatformRelease({
401
507
  manifest,
402
508
  requestedVersion: input.versionExplicit ? input.base.version : undefined,
@@ -405,6 +511,7 @@ export async function runFrontDoor(input, deps) {
405
511
  manifestUrl,
406
512
  });
407
513
  applyInput.version = resolved.release.version;
514
+ seam.expectedDigests = resolved.release.digests;
408
515
  if (resolved.note)
409
516
  log(resolved.note);
410
517
  log(`✓ Knowz Platform ${resolved.release.version} (${flavor}) — released ${resolved.release.releasedAt.slice(0, 10)}`);
@@ -422,7 +529,7 @@ export async function runFrontDoor(input, deps) {
422
529
  }
423
530
  }
424
531
  else if (selection.provider !== 'offline' &&
425
- (source.mode === 'preloaded' || source.loginPlan?.kind === 'ghcr') &&
532
+ !cachedRestart && (source.mode === 'preloaded' || source.loginPlan?.kind === 'ghcr') &&
426
533
  deps.fetchReleaseManifest) {
427
534
  // §2026-08-31 R4 (ghcr-token / preloaded): no manifest resolution runs on these
428
535
  // paths, so the version about to boot is the pin/explicit choice. Advisory-only
@@ -435,6 +542,7 @@ export async function runFrontDoor(input, deps) {
435
542
  loadPackagedReleaseManifest: deps.loadPackagedReleaseManifest,
436
543
  });
437
544
  if (advisoryManifest) {
545
+ seam.expectedDigests = advisoryManifest.releases.find(release => release.version === (applyInput.version ?? DEFAULT_PORTABLE_VERSION) && release.flavors.includes(selection.provider === 'gcp' ? 'portable-gcp' : 'portable'))?.digests;
438
546
  const freshness = releaseFreshnessNotice({
439
547
  currentVersion: applyInput.version ?? DEFAULT_PORTABLE_VERSION,
440
548
  manifest: advisoryManifest,
@@ -459,7 +567,7 @@ export async function runFrontDoor(input, deps) {
459
567
  if (shouldConfirm) {
460
568
  const accepted = await deps.confirmPrompt({
461
569
  provider: selection.provider,
462
- imageMode: source.mode,
570
+ imageMode: resultImageMode,
463
571
  version: applyInput.version ?? DEFAULT_PORTABLE_VERSION,
464
572
  ports,
465
573
  profileName: input.profileName ?? DEFAULT_LOCAL_PROFILE,
@@ -472,7 +580,7 @@ export async function runFrontDoor(input, deps) {
472
580
  setPlatformDefaultProvider(cfg, selection.provider);
473
581
  deps.saveConfig(cfg);
474
582
  const applyDeps = {
475
- runner: deps.runner,
583
+ runner: deps.asyncRunner ?? deps.runner,
476
584
  assetsDir: deps.assetsDir,
477
585
  randomBytes: deps.randomBytes,
478
586
  now: deps.now,
@@ -482,27 +590,58 @@ export async function runFrontDoor(input, deps) {
482
590
  engine: engine ?? LEGACY_DOCKER_ENGINE,
483
591
  };
484
592
  // 3) Bring the stack up; anonymous pull first, authenticate + retry once on auth failure.
593
+ // Paid GHCR pulls still require mothership grant preflight first — the token is
594
+ // registry auth only and must not skip commercial entitlement (#863).
595
+ const entitledInstallationInput = {
596
+ projectName: input.base.name,
597
+ configDir: input.base.configDir,
598
+ json: input.json,
599
+ mothershipUrl: normalizeMothershipOrigin(deps.env.KNOWZ_LICENSING_MOTHERSHIP_URL?.trim() || DEFAULT_MOTHERSHIP_URL),
600
+ engine: engine ?? LEGACY_DOCKER_ENGINE,
601
+ canPrompt,
602
+ log,
603
+ deps,
604
+ };
605
+ let entitledActivation;
606
+ applyDeps.authorizeAcquisition = async () => {
607
+ entitledActivation ??= await resolveEntitledInstallation(entitledInstallationInput);
608
+ return { installationId: entitledActivation.installationId, authorizedAt: new Date().toISOString() };
609
+ };
485
610
  let applied;
486
611
  if (useSelfHosted) {
487
612
  log('Using public selfhosted images. Portable platform requires --edition=portable and an entitled key.');
488
613
  const selfhostedVersion = input.versionExplicit && applyInput.version
489
614
  ? applyInput.version
490
- : (deps.env.KNOWZ_SELFHOSTED_VERSION?.trim() || STABLE_SELFHOSTED_RELEASE);
491
- const selfhosted = applySelfHosted({
615
+ : (deps.env.KNOWZ_SELFHOSTED_VERSION?.trim() || existing?.state.version || STABLE_SELFHOSTED_RELEASE);
616
+ const selfhosted = await applySelfHostedAsync({
492
617
  name: input.base.name,
493
618
  version: selfhostedVersion,
494
619
  configDir: input.base.configDir,
495
620
  ports,
496
621
  openAiCompatible: applyInput.openAiCompatible,
497
622
  adminPassword: applyInput.superAdminPassphrase,
623
+ provider: selection.provider,
624
+ providerSettingsExplicit: Boolean(input.providerFlags.offline || input.providerFlags.ai || [input.providerFlags.azure, input.providerFlags.openAiCompatible].some(values => values && Object.values(values).some(value => value !== undefined && value !== ''))),
625
+ disableAI: input.providerFlags.offline || input.providerFlags.ai === 'offline' ? true : input.providerFlags.ai ? false : undefined,
626
+ azure: applyInput.azure,
627
+ components: applyInput.components,
498
628
  }, {
499
- runner: deps.runner,
629
+ runner: deps.asyncRunner ?? deps.runner,
630
+ engine: engine ?? LEGACY_DOCKER_ENGINE,
631
+ signal: input.signal,
632
+ progress: log,
633
+ waitUntilReady: async (apiUrl, signal) => {
634
+ const ready = await waitForOnboardingReady({}, deps.onboardingDepsFor(apiUrl));
635
+ return ready && !signal?.aborted;
636
+ },
500
637
  randomBytes: deps.randomBytes,
501
638
  packagedAssetsDir: deps.selfHostedAssetsDir ?? resolvePackagedSelfHostedAssetsDir(),
502
639
  });
503
640
  applied = selfHostedToPlatformResult(selfhosted, selection.provider, deps.now);
504
641
  }
505
642
  else {
643
+ if (!cachedRestart)
644
+ await applyDeps.authorizeAcquisition({ name: input.base.name, mode: source.mode, engine: engine ?? LEGACY_DOCKER_ENGINE, env: deps.env });
506
645
  if (selfServePlan)
507
646
  log(`◇ Pulling images from ${selfServePlan.registry}…`);
508
647
  try {
@@ -524,84 +663,7 @@ export async function runFrontDoor(input, deps) {
524
663
  applied = await (deps.applyPlatform ?? applyPlatform)(applyInput, applyDeps, { ...seam, login });
525
664
  }
526
665
  else if (source.mode === 'pull' && selfServePlan && isRegistryAuthError(error)) {
527
- const activeEngine = engine ?? LEGACY_DOCKER_ENGINE;
528
- const mothershipUrl = normalizeMothershipOrigin(deps.env.KNOWZ_LICENSING_MOTHERSHIP_URL?.trim() || DEFAULT_MOTHERSHIP_URL);
529
- if (!deps.loadStoredActivation ||
530
- !deps.activateInstallation ||
531
- !deps.preflightRegistryCredential) {
532
- throw new Error('Self-serve registry authentication dependencies are not configured.');
533
- }
534
- let activation = await deps.loadStoredActivation({
535
- projectName: input.base.name,
536
- configDir: input.base.configDir,
537
- engine: activeEngine,
538
- });
539
- let activatedNow = false;
540
- if (!activation) {
541
- if (input.json)
542
- throw activationRequired();
543
- log("This device isn't linked to a Knowz account yet.");
544
- log('Linking is free and takes about a minute — your browser will open so you can approve this device.');
545
- log('');
546
- try {
547
- activation = await deps.activateInstallation({
548
- projectName: input.base.name,
549
- configDir: input.base.configDir,
550
- mothershipUrl,
551
- engine: activeEngine,
552
- });
553
- activatedNow = true;
554
- }
555
- catch {
556
- throw e5();
557
- }
558
- }
559
- let preflight = await deps.preflightRegistryCredential({ mothershipUrl, activation });
560
- if (preflight.kind === 'transport-failure') {
561
- throw new SelfServeResolutionError(`Could not reach the Knowz image authorization service at ${mothershipUrl}/api/v1/registry/token. ` +
562
- 'Check your network connection, then retry.', 'REGISTRY_AUTH_UNREACHABLE');
563
- }
564
- let preflightStatus = preflight.status;
565
- if (preflightStatus === 403)
566
- throw licenseRequiredError(preflight);
567
- if (preflightStatus === 410)
568
- throw e4(activation.installationId);
569
- if (preflightStatus === 401) {
570
- if (activatedNow || !canPrompt || !deps.confirmReactivation) {
571
- throw new SelfServeResolutionError(`Stored installation ${activation.installationId} is no longer valid. Run 'knowz activate' to link this device again.`, 'INSTALLATION_INVALID');
572
- }
573
- const accepted = await deps.confirmReactivation(activation.installationId);
574
- if (!accepted)
575
- throw e5();
576
- try {
577
- activation = await deps.activateInstallation({
578
- projectName: input.base.name,
579
- configDir: input.base.configDir,
580
- mothershipUrl,
581
- engine: activeEngine,
582
- });
583
- activatedNow = true;
584
- }
585
- catch {
586
- throw e5();
587
- }
588
- preflight = await deps.preflightRegistryCredential({ mothershipUrl, activation });
589
- if (preflight.kind === 'transport-failure') {
590
- throw new SelfServeResolutionError(`Could not reach the Knowz image authorization service at ${mothershipUrl}/api/v1/registry/token. ` +
591
- 'Check your network connection, then retry.', 'REGISTRY_AUTH_UNREACHABLE');
592
- }
593
- preflightStatus = preflight.status;
594
- if (preflightStatus === 403)
595
- throw licenseRequiredError(preflight);
596
- if (preflightStatus === 410)
597
- throw e4(activation.installationId);
598
- }
599
- if (preflightStatus !== 200) {
600
- throw new SelfServeResolutionError(`Knowz could not authorize installation ${activation.installationId} for image pulls (HTTP ${preflightStatus}).`, 'INSTALLATION_INVALID');
601
- }
602
- registerSecret(activation.credential);
603
- if (activatedNow)
604
- log(`✓ Linked — installation "${input.base.name}"`);
666
+ const activation = entitledActivation ?? await resolveEntitledInstallation(entitledInstallationInput);
605
667
  const login = buildRegistryLoginInvocation(selfServePlan.registry, activation.installationId, activation.credential);
606
668
  try {
607
669
  applied = await (deps.applyPlatform ?? applyPlatform)(applyInput, applyDeps, {
@@ -627,9 +689,10 @@ export async function runFrontDoor(input, deps) {
627
689
  }
628
690
  }
629
691
  }
692
+ checkCanceled();
630
693
  for (const line of applied.output.split('\n'))
631
694
  log(line);
632
- const profileName = input.profileName ?? DEFAULT_LOCAL_PROFILE;
695
+ const profileName = input.profileName ?? localProfileNameFor(input.base.name);
633
696
  const apiUrl = applied.state.serviceUrls.api;
634
697
  const webUrl = applied.state.serviceUrls.web;
635
698
  const renderedEnv = parseEnv(readFileSync(applied.paths.envFile, 'utf8'));
@@ -653,24 +716,37 @@ export async function runFrontDoor(input, deps) {
653
716
  // 4a) The same bounded health gate precedes every credential path, including
654
717
  // profile reuse. Passing the result into onboarding avoids a second poll.
655
718
  const apiReady = await waitForOnboardingReady({}, onboardingDeps);
656
- if (!apiReady) {
657
- identity = await collectRegistrationIdentity();
658
- const onboarding = await runOnboarding({ ...onboardingInput, register: identity, apiReady }, onboardingDeps);
659
- emitNextSteps(log);
660
- return {
661
- state: applied.state,
662
- imageMode: source.mode,
663
- provider: selection.provider,
664
- profile: onboarding.profile ?? null,
665
- loginMode: onboarding.loginMode,
666
- seeded: onboarding.seeded,
667
- edition: useSelfHosted ? 'selfhosted' : 'portable',
668
- };
719
+ checkCanceled();
720
+ if (!apiReady)
721
+ throw new Error('Runtime health check did not become ready. Inspect knowz runtime doctor and logs, then retry setup.');
722
+ if (useSelfHosted) {
723
+ let profile = null;
724
+ let nextAction;
725
+ try {
726
+ const reused = await deps.reuseExistingProfile?.(profileName, apiUrl, { dialect: 'selfhosted', signal: input.signal });
727
+ if (reused)
728
+ profile = profileName;
729
+ else if (deps.selfHostedLogin) {
730
+ const login = await deps.selfHostedLogin({ profile: profileName, apiUrl, username: renderedEnv.ADMIN_USERNAME || 'admin', password: renderedEnv.ADMIN_PASSWORD || '', signal: input.signal });
731
+ profile = login.profile;
732
+ }
733
+ else
734
+ nextAction = `Run knowz login --self-hosted --api-url ${apiUrl} --profile ${profileName} to finish CLI access.`;
735
+ }
736
+ catch (error) {
737
+ nextAction = `${redactPlatformSecrets(errorMessage(error), Object.values(renderedEnv))} Sign in with the current administrator password: knowz login --self-hosted --username ${renderedEnv.ADMIN_USERNAME || 'admin'} --api-url ${apiUrl} --profile ${profileName}.`;
738
+ }
739
+ checkCanceled();
740
+ if (input.open)
741
+ onboardingDeps.openBrowser(webUrl);
742
+ if (nextAction)
743
+ log(nextAction);
744
+ return { status: profile ? 'complete' : 'attention-required', ...(profile ? {} : { code: 'SETUP_ACTION_REQUIRED', nextAction }), state: applied.state, imageMode: 'pull', provider: selection.provider, profile, loginMode: profile ? 'existing' : 'manual', seeded: false, edition: 'selfhosted' };
669
745
  }
670
746
  // 4b) If a prior `up` already signed the CLI profile in and its key still authenticates,
671
747
  // skip the register/onboarding dance entirely — avoids the register → "Email already
672
748
  // registered" → manual-degrade noise on every idempotent re-up.
673
- const reused = deps.reuseExistingProfile ? await deps.reuseExistingProfile(profileName, apiUrl) : null;
749
+ const reused = deps.reuseExistingProfile ? await deps.reuseExistingProfile(profileName, apiUrl, { dialect: 'platform' }) : null;
674
750
  if (reused) {
675
751
  await runOnboardingProviderProbe(selection.provider, () => onboardingDeps.apiClientFor(profileName), onboardingDeps);
676
752
  log(`Already signed in — reusing the '${profileName}' CLI profile${reused.tenantId ? ` (tenant ${reused.tenantId})` : ''}.`);
@@ -678,15 +754,17 @@ export async function runFrontDoor(input, deps) {
678
754
  log(` API: ${apiUrl}`);
679
755
  if (input.open)
680
756
  onboardingDeps.openBrowser(webUrl);
681
- return { state: applied.state, imageMode: source.mode, provider: selection.provider, profile: profileName, loginMode: 'existing', seeded: false, edition: useSelfHosted ? 'selfhosted' : 'portable' };
757
+ return { status: 'complete', state: applied.state, imageMode: resultImageMode, provider: selection.provider, profile: profileName, loginMode: 'existing', seeded: false, edition: useSelfHosted ? 'selfhosted' : 'portable' };
682
758
  }
683
759
  // 4c) Onboarding: bootstrap → register fallback → manual degrade.
684
760
  identity = await collectRegistrationIdentity();
685
761
  const onboarding = await runOnboarding({ ...onboardingInput, register: identity, apiReady }, onboardingDeps);
686
762
  emitNextSteps(log);
687
763
  return {
764
+ status: onboarding.profile ? 'complete' : 'attention-required',
765
+ ...(onboarding.profile ? {} : { code: 'SETUP_ACTION_REQUIRED', nextAction: 'Finish account sign-in with knowz login for this endpoint.' }),
688
766
  state: applied.state,
689
- imageMode: source.mode,
767
+ imageMode: resultImageMode,
690
768
  provider: selection.provider,
691
769
  profile: onboarding.profile ?? null,
692
770
  loginMode: onboarding.loginMode,