@flowscripter/dynamic-cli-framework 6.0.0 → 7.0.0

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 (124) hide show
  1. package/README.md +2 -0
  2. package/dist/index.d.ts +10 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +7 -2
  5. package/dist/src/cli/BaseCLI.d.ts +8 -0
  6. package/dist/src/cli/BaseCLI.d.ts.map +1 -1
  7. package/dist/src/cli/BaseCLI.js +37 -4
  8. package/dist/src/launcher.d.ts +3 -3
  9. package/dist/src/launcher.d.ts.map +1 -1
  10. package/dist/src/launcher.js +6 -4
  11. package/dist/src/runtime/lifecycle/PriorityTaskList.d.ts +22 -0
  12. package/dist/src/runtime/lifecycle/PriorityTaskList.d.ts.map +1 -0
  13. package/dist/src/runtime/lifecycle/PriorityTaskList.js +23 -0
  14. package/dist/src/runtime/runner.d.ts +15 -7
  15. package/dist/src/runtime/runner.d.ts.map +1 -1
  16. package/dist/src/runtime/runner.js +82 -53
  17. package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts +26 -82
  18. package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts.map +1 -1
  19. package/dist/src/service/configuration/ConfigurationServiceProvider.js +68 -163
  20. package/dist/src/service/configuration/DefaultKeyValueService.d.ts +6 -3
  21. package/dist/src/service/configuration/DefaultKeyValueService.d.ts.map +1 -1
  22. package/dist/src/service/configuration/DefaultKeyValueService.js +7 -24
  23. package/dist/src/service/configuration/DefaultSecretService.d.ts +1 -3
  24. package/dist/src/service/configuration/DefaultSecretService.d.ts.map +1 -1
  25. package/dist/src/service/configuration/DefaultSecretService.js +2 -10
  26. package/dist/src/service/configuration/KeyValueServiceProvider.d.ts +105 -0
  27. package/dist/src/service/configuration/KeyValueServiceProvider.d.ts.map +1 -0
  28. package/dist/src/service/configuration/KeyValueServiceProvider.js +201 -0
  29. package/dist/src/service/configuration/command/ConfigCommand.d.ts +4 -5
  30. package/dist/src/service/configuration/command/ConfigCommand.d.ts.map +1 -1
  31. package/dist/src/service/configuration/command/ConfigCommand.js +7 -6
  32. package/dist/src/service/configuration/command/DumpConfigCommand.d.ts +7 -3
  33. package/dist/src/service/configuration/command/DumpConfigCommand.d.ts.map +1 -1
  34. package/dist/src/service/configuration/command/DumpConfigCommand.js +10 -5
  35. package/dist/src/service/fetch/DefaultFetchService.d.ts +2 -2
  36. package/dist/src/service/fetch/DefaultFetchService.d.ts.map +1 -1
  37. package/dist/src/service/fetch/DefaultFetchService.js +17 -12
  38. package/dist/src/service/fetch/FetchServiceProvider.d.ts.map +1 -1
  39. package/dist/src/service/fetch/FetchServiceProvider.js +2 -3
  40. package/dist/src/service/printer/PrinterServiceProvider.d.ts.map +1 -1
  41. package/dist/src/service/printer/PrinterServiceProvider.js +11 -7
  42. package/dist/src/service/printer/command/DarkModeCommand.d.ts +3 -5
  43. package/dist/src/service/printer/command/DarkModeCommand.d.ts.map +1 -1
  44. package/dist/src/service/printer/command/DarkModeCommand.js +6 -6
  45. package/dist/src/service/printer/command/LogLevelCommand.d.ts +3 -5
  46. package/dist/src/service/printer/command/LogLevelCommand.d.ts.map +1 -1
  47. package/dist/src/service/printer/command/LogLevelCommand.js +9 -9
  48. package/dist/src/service/printer/command/NoColorCommand.d.ts +3 -5
  49. package/dist/src/service/printer/command/NoColorCommand.d.ts.map +1 -1
  50. package/dist/src/service/printer/command/NoColorCommand.js +6 -6
  51. package/dist/src/service/printer/command/NoHyperlinksCommand.d.ts +2 -4
  52. package/dist/src/service/printer/command/NoHyperlinksCommand.d.ts.map +1 -1
  53. package/dist/src/service/printer/command/NoHyperlinksCommand.js +5 -5
  54. package/dist/src/service/prompter/PrompterServiceProvider.js +1 -1
  55. package/dist/src/service/prompter/command/NoPromptCommand.d.ts +2 -4
  56. package/dist/src/service/prompter/command/NoPromptCommand.d.ts.map +1 -1
  57. package/dist/src/service/prompter/command/NoPromptCommand.js +5 -5
  58. package/dist/src/service/shutdown/DefaultShutdownService.d.ts +4 -3
  59. package/dist/src/service/shutdown/DefaultShutdownService.d.ts.map +1 -1
  60. package/dist/src/service/shutdown/DefaultShutdownService.js +4 -3
  61. package/dist/src/service/shutdown/ShutdownServiceProvider.js +2 -2
  62. package/dist/src/service/spawn/DefaultSpawnService.d.ts +2 -2
  63. package/dist/src/service/spawn/DefaultSpawnService.d.ts.map +1 -1
  64. package/dist/src/service/spawn/DefaultSpawnService.js +17 -14
  65. package/dist/src/service/spawn/SpawnServiceProvider.d.ts.map +1 -1
  66. package/dist/src/service/spawn/SpawnServiceProvider.js +1 -4
  67. package/dist/src/service/startup/DefaultStartupService.d.ts +10 -0
  68. package/dist/src/service/startup/DefaultStartupService.d.ts.map +1 -0
  69. package/dist/src/service/startup/DefaultStartupService.js +13 -0
  70. package/dist/src/service/startup/StartupServiceProvider.d.ts +17 -0
  71. package/dist/src/service/startup/StartupServiceProvider.d.ts.map +1 -0
  72. package/dist/src/service/startup/StartupServiceProvider.js +26 -0
  73. package/dist/src/service/upgrade/DefaultUpgradeService.d.ts +10 -4
  74. package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -1
  75. package/dist/src/service/upgrade/DefaultUpgradeService.js +156 -37
  76. package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts +10 -0
  77. package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts.map +1 -1
  78. package/dist/src/service/upgrade/UpgradeServiceProvider.js +25 -6
  79. package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts +0 -3
  80. package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts.map +1 -1
  81. package/dist/src/service/upgrade/command/UpgradeSubCommand.js +5 -13
  82. package/dist/src/startup/banner/bannerStartupTask.d.ts +17 -0
  83. package/dist/src/startup/banner/bannerStartupTask.d.ts.map +1 -0
  84. package/dist/src/startup/banner/bannerStartupTask.js +66 -0
  85. package/dist/src/{service → startup}/banner/command/NoBannerCommand.d.ts +8 -2
  86. package/dist/src/startup/banner/command/NoBannerCommand.d.ts.map +1 -0
  87. package/dist/src/{service → startup}/banner/command/NoBannerCommand.js +4 -4
  88. package/index.ts +19 -3
  89. package/package.json +7 -7
  90. package/src/cli/BaseCLI.ts +49 -3
  91. package/src/launcher.ts +7 -2
  92. package/src/runtime/lifecycle/PriorityTaskList.ts +34 -0
  93. package/src/runtime/runner.ts +106 -67
  94. package/src/service/configuration/ConfigurationServiceProvider.ts +80 -180
  95. package/src/service/configuration/DefaultKeyValueService.ts +8 -27
  96. package/src/service/configuration/DefaultSecretService.ts +3 -13
  97. package/src/service/configuration/KeyValueServiceProvider.ts +246 -0
  98. package/src/service/configuration/command/ConfigCommand.ts +12 -11
  99. package/src/service/configuration/command/DumpConfigCommand.ts +10 -9
  100. package/src/service/fetch/DefaultFetchService.ts +18 -12
  101. package/src/service/fetch/FetchServiceProvider.ts +2 -4
  102. package/src/service/printer/PrinterServiceProvider.ts +11 -7
  103. package/src/service/printer/command/DarkModeCommand.ts +7 -8
  104. package/src/service/printer/command/LogLevelCommand.ts +10 -11
  105. package/src/service/printer/command/NoColorCommand.ts +7 -8
  106. package/src/service/printer/command/NoHyperlinksCommand.ts +6 -7
  107. package/src/service/prompter/PrompterServiceProvider.ts +1 -1
  108. package/src/service/prompter/command/NoPromptCommand.ts +6 -7
  109. package/src/service/shutdown/DefaultShutdownService.ts +5 -4
  110. package/src/service/shutdown/ShutdownServiceProvider.ts +2 -2
  111. package/src/service/spawn/DefaultSpawnService.ts +18 -14
  112. package/src/service/spawn/SpawnServiceProvider.ts +1 -5
  113. package/src/service/startup/DefaultStartupService.ts +17 -0
  114. package/src/service/startup/StartupServiceProvider.ts +36 -0
  115. package/src/service/upgrade/DefaultUpgradeService.ts +202 -42
  116. package/src/service/upgrade/UpgradeServiceProvider.ts +31 -8
  117. package/src/service/upgrade/command/UpgradeSubCommand.ts +5 -15
  118. package/src/startup/banner/bannerStartupTask.ts +96 -0
  119. package/src/{service → startup}/banner/command/NoBannerCommand.ts +12 -5
  120. package/dist/src/service/banner/BannerServiceProvider.d.ts +0 -29
  121. package/dist/src/service/banner/BannerServiceProvider.d.ts.map +0 -1
  122. package/dist/src/service/banner/BannerServiceProvider.js +0 -69
  123. package/dist/src/service/banner/command/NoBannerCommand.d.ts.map +0 -1
  124. package/src/service/banner/BannerServiceProvider.ts +0 -89
@@ -1,16 +1,32 @@
1
1
  import process from "node:process";
2
+ import { realpathSync } from "node:fs";
2
3
  import { mkdtemp, rename, rm } from "node:fs/promises";
3
4
  import { tmpdir } from "node:os";
4
5
  import { dirname, join } from "node:path";
5
- import { InstallMethod, SupportedArch, SupportedOs, } from "@flowscripter/dynamic-cli-framework-api";
6
+ import { FETCH_SERVICE_ID, InstallMethod, KEY_VALUE_SERVICE_ID, PRINTER_SERVICE_ID, SPAWN_SERVICE_ID, SupportedArch, SupportedOs, } from "@flowscripter/dynamic-cli-framework-api";
6
7
  import semver from "semver";
7
8
  import getLogger from "../../util/logger.js";
8
9
  const logger = getLogger("DefaultUpgradeService");
9
- // checkForUpgrade() runs opportunistically on every CLI invocation (via BannerServiceProvider),
10
- // so that opportunistic call is raced against this timeout so it never stalls CLI startup.
11
- // A deliberate caller (waitForResult=true, e.g. the `upgrade` command) awaits checkForUpgrade()
12
- // directly with no timeout, so its underlying network/spawn calls are never artificially cut off.
13
- export const VERSION_CHECK_TIMEOUT_MS = 250;
10
+ // KeyValueService key caching the resolved InstallMethod (see detectInstallMethod()). Stored
11
+ // value shape: { method: InstallMethod, checkedAt: number } where checkedAt is Date.now() at
12
+ // detection time - refreshed on the same CACHE_TTL_MILLIS cycle as the latest-version cache below,
13
+ // rather than trusted indefinitely, so a reinstall via a different method is eventually picked up
14
+ // even outside the no-spawn signals (Cellar path, linux script prefix) that self-heal immediately.
15
+ const INSTALL_METHOD_CACHE_KEY = "install-method";
16
+ // KeyValueService key prefix caching a #getLatestVersion() lookup per install method, e.g.
17
+ // "latest-version:homebrew". Stored value shape: { version: string, checkedAt: number } where
18
+ // checkedAt is Date.now() at lookup time - see CACHE_TTL_MILLIS.
19
+ const LATEST_VERSION_CACHE_KEY_PREFIX = "latest-version:";
20
+ // How long a cached install-method or latest-version lookup is trusted before a fresh
21
+ // spawn/network lookup runs again - long enough to remove the cost from routine invocations,
22
+ // short enough that a genuine reinstall or new release surfaces within about a day.
23
+ const CACHE_TTL_MILLIS = 24 * 60 * 60 * 1000;
24
+ // KeyValueService key holding the last checkForUpgrade() result, refreshed opportunistically by
25
+ // UpgradeServiceProvider's background StartupTask and by the `upgrade` command - shared by both so
26
+ // there's one cache, not two. The banner task only ever reads this key (kv.has()/kv.get()) - it
27
+ // never calls checkForUpgrade()/getUpgradeCheckResult() live, since checking live would stall
28
+ // startup on the network/spawn calls checkForUpgrade() makes.
29
+ export const UPGRADE_CHECK_CACHE_KEY = "upgrade-check-result";
14
30
  function describeSpawnFailure(result) {
15
31
  return "timedOut" in result
16
32
  ? "timed out"
@@ -27,9 +43,7 @@ const OS_LABELS = {
27
43
  [SupportedOs.WINDOWS]: "Windows",
28
44
  };
29
45
  export default class DefaultUpgradeService {
30
- #spawnService;
31
- #fetchService;
32
- #printerService;
46
+ #context;
33
47
  #upgradeCheckPromise;
34
48
  #config;
35
49
  #cliConfig;
@@ -37,10 +51,28 @@ export default class DefaultUpgradeService {
37
51
  this.#config = config;
38
52
  this.#cliConfig = cliConfig;
39
53
  }
40
- setDependencies(spawnService, fetchService, printerService) {
41
- this.#spawnService = spawnService;
42
- this.#fetchService = fetchService;
43
- this.#printerService = printerService;
54
+ setContext(context) {
55
+ this.#context = context;
56
+ }
57
+ get #spawnService() {
58
+ return this.#context?.doesServiceExist(SPAWN_SERVICE_ID)
59
+ ? this.#context.getServiceById(SPAWN_SERVICE_ID)
60
+ : undefined;
61
+ }
62
+ get #fetchService() {
63
+ return this.#context?.doesServiceExist(FETCH_SERVICE_ID)
64
+ ? this.#context.getServiceById(FETCH_SERVICE_ID)
65
+ : undefined;
66
+ }
67
+ get #printerService() {
68
+ return this.#context?.doesServiceExist(PRINTER_SERVICE_ID)
69
+ ? this.#context.getServiceById(PRINTER_SERVICE_ID)
70
+ : undefined;
71
+ }
72
+ get #keyValueService() {
73
+ return this.#context?.doesServiceExist(KEY_VALUE_SERVICE_ID)
74
+ ? this.#context.getServiceById(KEY_VALUE_SERVICE_ID)
75
+ : undefined;
44
76
  }
45
77
  // Mirrors SpawnInterfaceAdapter's plugin:add/plugin:remove pattern: wrap a spawned command's
46
78
  // output in a quoted, marked block that's cleared on success (so a clean install stays quiet)
@@ -72,15 +104,12 @@ export default class DefaultUpgradeService {
72
104
  }
73
105
  return result;
74
106
  }
75
- getUpgradeCheckResult(waitForResult = false) {
107
+ getUpgradeCheckResult() {
76
108
  if (!this.#upgradeCheckPromise) {
77
109
  logger.debug(() => "Starting upgrade check");
78
110
  // Only cache a non-"failed" result. A "failed" result can come from a transient issue
79
- // (e.g. a network blip, or the opportunistic startup check racing past
80
- // VERSION_CHECK_TIMEOUT_MS before checkForUpgrade() itself resolves) - caching that would
81
- // permanently deny a later, deliberate caller (e.g. the `upgrade` command explicitly
82
- // waiting via waitForResult=true) any chance of a fresh attempt for the rest of this
83
- // process's lifetime.
111
+ // (e.g. a network blip) - caching that would permanently deny a later caller any chance of
112
+ // a fresh attempt for the rest of this process's lifetime.
84
113
  this.#upgradeCheckPromise = this.checkForUpgrade().then((result) => {
85
114
  logger.debug(() => describeUpgradeCheckResult(result));
86
115
  if (result.status === "failed") {
@@ -89,13 +118,20 @@ export default class DefaultUpgradeService {
89
118
  return result;
90
119
  });
91
120
  }
92
- if (waitForResult) {
93
- return this.#upgradeCheckPromise;
121
+ return this.#upgradeCheckPromise;
122
+ }
123
+ /**
124
+ * Run (or reuse an in-flight/cached) {@link getUpgradeCheckResult}, then persist a "checked" or
125
+ * "unsupported" result to {@link UPGRADE_CHECK_CACHE_KEY} so the banner task's cheap KV read can
126
+ * pick it up on a later invocation.
127
+ */
128
+ async refreshUpgradeCheckCache() {
129
+ const result = await this.getUpgradeCheckResult();
130
+ if (this.#keyValueService && (result.status === "checked" || result.status === "unsupported")) {
131
+ const keyValueService = this.#keyValueService;
132
+ await this.#safeKeyValueCall(() => keyValueService.set(UPGRADE_CHECK_CACHE_KEY, result));
94
133
  }
95
- return Promise.race([
96
- this.#upgradeCheckPromise,
97
- new Promise((resolve) => setTimeout(() => resolve({ status: "pending" }), VERSION_CHECK_TIMEOUT_MS)),
98
- ]);
134
+ return result;
99
135
  }
100
136
  detectOs() {
101
137
  switch (process.platform) {
@@ -120,19 +156,73 @@ export default class DefaultUpgradeService {
120
156
  }
121
157
  }
122
158
  async detectInstallMethod(os) {
123
- if (os === SupportedOs.MACOS && this.#config.homebrew && (await this.#isHomebrewInstalled())) {
159
+ // Cheap, no-spawn signals are checked first and always win over the cache below, so a fresh
160
+ // install is picked up immediately rather than waiting on a stale cached method.
161
+ if (os === SupportedOs.MACOS &&
162
+ this.#config.homebrew &&
163
+ this.#isRunningFromHomebrewCellar(this.#config.homebrew.formula)) {
124
164
  return InstallMethod.HOMEBREW;
125
165
  }
126
- if (os === SupportedOs.WINDOWS && this.#config.winget && (await this.#isWingetInstalled())) {
127
- return InstallMethod.WINGET;
128
- }
129
166
  if (os === SupportedOs.LINUX && this.#config.linuxScript && this.#isLinuxScriptInstall()) {
130
167
  return InstallMethod.LINUX_SCRIPT;
131
168
  }
132
- if (this.#config.githubRelease) {
133
- return InstallMethod.GITHUB_RELEASE;
169
+ // Everything else needs an external process spawn (`brew list`, `winget list`) to confirm -
170
+ // cache the resolved method so that spawn only ever happens once per keystore.
171
+ const cached = await this.#getCachedInstallMethod();
172
+ if (cached !== undefined) {
173
+ return cached;
174
+ }
175
+ let detected;
176
+ if (os === SupportedOs.MACOS && this.#config.homebrew && (await this.#isHomebrewInstalled())) {
177
+ detected = InstallMethod.HOMEBREW;
178
+ }
179
+ else if (os === SupportedOs.WINDOWS &&
180
+ this.#config.winget &&
181
+ (await this.#isWingetInstalled())) {
182
+ detected = InstallMethod.WINGET;
183
+ }
184
+ else if (this.#config.githubRelease) {
185
+ detected = InstallMethod.GITHUB_RELEASE;
186
+ }
187
+ if (detected !== undefined) {
188
+ await this.#cacheInstallMethod(detected);
189
+ }
190
+ return detected;
191
+ }
192
+ // The opportunistic startup check (see getUpgradeCheckResult()) runs detached from
193
+ // UpgradeServiceProvider.initService()'s own await chain, so it can still be running after that
194
+ // scoped KeyValueService access window has closed - a KeyValueService call landing after that
195
+ // point throws (or could land under a different service's scope entirely). Treat any such
196
+ // failure as "caching unavailable right now" rather than letting it fail the whole check.
197
+ async #safeKeyValueCall(op) {
198
+ try {
199
+ return await op();
200
+ }
201
+ catch (error) {
202
+ logger.debug(() => `KeyValueService access failed, skipping cache: ${error}`);
203
+ return undefined;
204
+ }
205
+ }
206
+ async #getCachedInstallMethod() {
207
+ if (!this.#keyValueService) {
208
+ return undefined;
209
+ }
210
+ const keyValueService = this.#keyValueService;
211
+ const has = await this.#safeKeyValueCall(() => keyValueService.has(INSTALL_METHOD_CACHE_KEY));
212
+ if (!has) {
213
+ return undefined;
214
+ }
215
+ const cached = await this.#safeKeyValueCall(() => keyValueService.get(INSTALL_METHOD_CACHE_KEY));
216
+ if (!cached || Date.now() - cached.checkedAt >= CACHE_TTL_MILLIS) {
217
+ return undefined;
218
+ }
219
+ return cached.method;
220
+ }
221
+ async #cacheInstallMethod(method) {
222
+ if (this.#keyValueService) {
223
+ const keyValueService = this.#keyValueService;
224
+ await this.#safeKeyValueCall(() => keyValueService.set(INSTALL_METHOD_CACHE_KEY, { method, checkedAt: Date.now() }));
134
225
  }
135
- return undefined;
136
226
  }
137
227
  async checkForUpgrade(osOverride, archOverride, installMethodOverride) {
138
228
  const os = osOverride ?? this.detectOs();
@@ -172,7 +262,7 @@ export default class DefaultUpgradeService {
172
262
  const hasOverride = osOverride !== undefined || archOverride !== undefined || installMethodOverride !== undefined;
173
263
  const checkResult = hasOverride
174
264
  ? await this.checkForUpgrade(osOverride, archOverride, installMethodOverride)
175
- : await this.getUpgradeCheckResult(true);
265
+ : await this.getUpgradeCheckResult();
176
266
  if (checkResult.status === "unsupported") {
177
267
  return {
178
268
  ok: false,
@@ -183,10 +273,6 @@ export default class DefaultUpgradeService {
183
273
  if (checkResult.status === "failed") {
184
274
  return { ok: false, oldVersion, error: checkResult.error };
185
275
  }
186
- if (checkResult.status === "pending") {
187
- // Unreachable: checkForUpgrade() and getUpgradeCheckResult(true) always run to completion.
188
- return { ok: false, oldVersion, error: new Error("Upgrade check did not complete") };
189
- }
190
276
  if (!this.#spawnService) {
191
277
  return { ok: false, oldVersion, error: new Error("SpawnService is not available") };
192
278
  }
@@ -223,6 +309,21 @@ export default class DefaultUpgradeService {
223
309
  #isPlatformSupported(os, arch) {
224
310
  return this.#config.supportedPlatforms.some((platform) => platform.os === os && platform.arch === arch);
225
311
  }
312
+ // Homebrew relinks a formula's installed binary from its Cellar directory into a `bin/` symlink,
313
+ // so resolving the running executable's real path confirms a homebrew install without spawning
314
+ // `brew`, which has a slow cold start - avoiding it keeps the background upgrade-check
315
+ // StartupTask (see UpgradeServiceProvider) fast even though it now runs to completion.
316
+ #isRunningFromHomebrewCellar(formula) {
317
+ let realExecutable = process.execPath;
318
+ try {
319
+ realExecutable = realpathSync(process.execPath);
320
+ }
321
+ catch {
322
+ // process.execPath may not resolve on disk (e.g. a fabricated path in tests) - fall back to
323
+ // the unresolved path rather than treating that as "not installed".
324
+ }
325
+ return realExecutable.includes(`/Cellar/${formula}/`);
326
+ }
226
327
  async #isHomebrewInstalled() {
227
328
  if (!this.#spawnService || !this.#config.homebrew) {
228
329
  return false;
@@ -241,6 +342,24 @@ export default class DefaultUpgradeService {
241
342
  return process.execPath.startsWith("/usr/local/bin/");
242
343
  }
243
344
  async #getLatestVersion(installMethod) {
345
+ const cacheKey = `${LATEST_VERSION_CACHE_KEY_PREFIX}${installMethod}`;
346
+ const keyValueService = this.#keyValueService;
347
+ if (keyValueService) {
348
+ const has = await this.#safeKeyValueCall(() => keyValueService.has(cacheKey));
349
+ if (has) {
350
+ const cached = await this.#safeKeyValueCall(() => keyValueService.get(cacheKey));
351
+ if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MILLIS) {
352
+ return { ok: true, version: cached.version };
353
+ }
354
+ }
355
+ }
356
+ const result = await this.#lookupLatestVersion(installMethod);
357
+ if (result.ok && keyValueService) {
358
+ await this.#safeKeyValueCall(() => keyValueService.set(cacheKey, { version: result.version, checkedAt: Date.now() }));
359
+ }
360
+ return result;
361
+ }
362
+ async #lookupLatestVersion(installMethod) {
244
363
  switch (installMethod) {
245
364
  case InstallMethod.GITHUB_RELEASE:
246
365
  case InstallMethod.LINUX_SCRIPT:
@@ -1,12 +1,22 @@
1
1
  import type { ServiceInfo, ServiceProvider } from "@flowscripter/dynamic-cli-framework-api";
2
2
  import type { Context } from "@flowscripter/dynamic-cli-framework-api";
3
3
  import type { CLIConfig } from "@flowscripter/dynamic-cli-framework-api";
4
+ import DefaultUpgradeService from "./DefaultUpgradeService.ts";
4
5
  import type { UpgradeLocationsConfig } from "./UpgradeLocationsConfig.ts";
6
+ import type { StartupTask } from "@flowscripter/dynamic-cli-framework-api";
7
+ /**
8
+ * Build the background {@link StartupTask} which opportunistically refreshes the upgrade-check
9
+ * cache (see {@link DefaultUpgradeService.refreshUpgradeCheckCache}) that the banner task reads
10
+ * from. Registered separately from {@link UpgradeServiceProvider} itself (which still runs as a
11
+ * regular blocking init task) so it can run in `"background"` mode.
12
+ */
13
+ export declare function createUpgradeCheckStartupTask(upgradeService: DefaultUpgradeService, priority: number): StartupTask;
5
14
  export default class UpgradeServiceProvider implements ServiceProvider {
6
15
  #private;
7
16
  readonly serviceId: string;
8
17
  readonly servicePriority: number;
9
18
  constructor(servicePriority: number, config: UpgradeLocationsConfig);
19
+ get upgradeService(): DefaultUpgradeService | undefined;
10
20
  getServiceInfo(cliConfig: CLIConfig): Promise<ServiceInfo>;
11
21
  initService(context: Context): Promise<void>;
12
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"UpgradeServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/UpgradeServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAczE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAK1E,MAAM,CAAC,OAAO,OAAO,sBAAuB,YAAW,eAAe;;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAsB;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAKjC,YAAmB,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,EAGzE;IAEM,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAOhE;IAEY,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAuFxD;CAoCF"}
1
+ {"version":3,"file":"UpgradeServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/UpgradeServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYzE,OAAO,qBAAqB,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAI3E;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,qBAAqB,EACrC,QAAQ,EAAE,MAAM,GACf,WAAW,CASb;AAED,MAAM,CAAC,OAAO,OAAO,sBAAuB,YAAW,eAAe;;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAsB;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAKjC,YAAmB,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,EAGzE;IAED,IAAW,cAAc,IAAI,qBAAqB,GAAG,SAAS,CAE7D;IAEM,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAOhE;IAEY,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFxD;CAoCF"}
@@ -8,6 +8,22 @@ import DefaultUpgradeService from "./DefaultUpgradeService.js";
8
8
  import { UpgradeSubCommand } from "./command/UpgradeSubCommand.js";
9
9
  import getLogger from "../../util/logger.js";
10
10
  const logger = getLogger("UpgradeServiceProvider");
11
+ /**
12
+ * Build the background {@link StartupTask} which opportunistically refreshes the upgrade-check
13
+ * cache (see {@link DefaultUpgradeService.refreshUpgradeCheckCache}) that the banner task reads
14
+ * from. Registered separately from {@link UpgradeServiceProvider} itself (which still runs as a
15
+ * regular blocking init task) so it can run in `"background"` mode.
16
+ */
17
+ export function createUpgradeCheckStartupTask(upgradeService, priority) {
18
+ return {
19
+ id: `${UPGRADE_SERVICE_ID}-check`,
20
+ priority,
21
+ mode: "background",
22
+ run: async () => {
23
+ await upgradeService.refreshUpgradeCheckCache();
24
+ },
25
+ };
26
+ }
11
27
  export default class UpgradeServiceProvider {
12
28
  serviceId = UPGRADE_SERVICE_ID;
13
29
  servicePriority;
@@ -18,12 +34,15 @@ export default class UpgradeServiceProvider {
18
34
  this.servicePriority = servicePriority;
19
35
  this.#config = config;
20
36
  }
37
+ get upgradeService() {
38
+ return this.#upgradeService;
39
+ }
21
40
  getServiceInfo(cliConfig) {
22
41
  this.#cliConfig = cliConfig;
23
42
  this.#upgradeService = new DefaultUpgradeService(this.#config, cliConfig);
24
43
  return Promise.resolve({
25
44
  service: this.#upgradeService,
26
- commands: [new UpgradeSubCommand(this.#upgradeService)],
45
+ commands: [new UpgradeSubCommand()],
27
46
  });
28
47
  }
29
48
  async initService(context) {
@@ -41,18 +60,18 @@ export default class UpgradeServiceProvider {
41
60
  if (fetchService === undefined) {
42
61
  logger.debug(() => "FetchService not available, upgrade version checks will be unavailable");
43
62
  }
44
- const printerService = context.getServiceById(PRINTER_SERVICE_ID);
45
- upgradeService.setDependencies(spawnService, fetchService, printerService);
46
- void upgradeService.getUpgradeCheckResult();
63
+ const keyValueService = context.doesServiceExist(KEY_VALUE_SERVICE_ID)
64
+ ? context.getServiceById(KEY_VALUE_SERVICE_ID)
65
+ : undefined;
66
+ upgradeService.setContext(context);
47
67
  if (!context.doesServiceExist(PROMPTER_SERVICE_ID)) {
48
68
  logger.debug(() => "PrompterService not available, skipping auto-upgrade");
49
69
  return;
50
70
  }
51
- if (!context.doesServiceExist(KEY_VALUE_SERVICE_ID)) {
71
+ if (!keyValueService) {
52
72
  logger.debug(() => "KeyValueService not available, skipping auto-upgrade");
53
73
  return;
54
74
  }
55
- const keyValueService = context.getServiceById(KEY_VALUE_SERVICE_ID);
56
75
  if (await keyValueService.has("upgrade-status")) {
57
76
  const status = await keyValueService.get("upgrade-status");
58
77
  if (status === "declined") {
@@ -2,15 +2,12 @@ import type { SubCommand } from "@flowscripter/dynamic-cli-framework-api";
2
2
  import type { Option } from "@flowscripter/dynamic-cli-framework-api";
3
3
  import type { Context } from "@flowscripter/dynamic-cli-framework-api";
4
4
  import { type Values } from "@flowscripter/dynamic-cli-framework-api";
5
- import type DefaultUpgradeService from "../DefaultUpgradeService.ts";
6
5
  export declare class UpgradeSubCommand implements SubCommand {
7
- #private;
8
6
  readonly name = "upgrade";
9
7
  readonly description = "Upgrade to the latest available version";
10
8
  readonly enableConfiguration = false;
11
9
  readonly positionals: never[];
12
10
  readonly options: ReadonlyArray<Option>;
13
- constructor(upgradeService: DefaultUpgradeService);
14
11
  execute(context: Context, argumentValues: Values): Promise<void>;
15
12
  }
16
13
  //# sourceMappingURL=UpgradeSubCommand.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UpgradeSubCommand.d.ts","sourceRoot":"","sources":["../../../../../src/service/upgrade/command/UpgradeSubCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,yCAAyC,CAAC;AASrF,OAAO,KAAK,qBAAqB,MAAM,6BAA6B,CAAC;AAErE,qBAAa,iBAAkB,YAAW,UAAU;;IAClD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,WAAW,6CAA6C;IACjE,QAAQ,CAAC,mBAAmB,SAAS;IACrC,QAAQ,CAAC,WAAW,UAAM;IAE1B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAerC;IAIF,YAAmB,cAAc,EAAE,qBAAqB,EAEvD;IAEY,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4D5E;CACF"}
1
+ {"version":3,"file":"UpgradeSubCommand.d.ts","sourceRoot":"","sources":["../../../../../src/service/upgrade/command/UpgradeSubCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,yCAAyC,CAAC;AAYrF,qBAAa,iBAAkB,YAAW,UAAU;IAClD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,WAAW,6CAA6C;IACjE,QAAQ,CAAC,mBAAmB,SAAS;IACrC,QAAQ,CAAC,WAAW,UAAM;IAE1B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAerC;IAEW,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E;CACF"}
@@ -1,5 +1,5 @@
1
1
  import { ValueTypeName } from "@flowscripter/dynamic-cli-framework-api";
2
- import { Icon, InstallMethod, PRINTER_SERVICE_ID, SupportedArch, SupportedOs, } from "@flowscripter/dynamic-cli-framework-api";
2
+ import { Icon, InstallMethod, PRINTER_SERVICE_ID, SupportedArch, SupportedOs, UPGRADE_SERVICE_ID, } from "@flowscripter/dynamic-cli-framework-api";
3
3
  export class UpgradeSubCommand {
4
4
  name = "upgrade";
5
5
  description = "Upgrade to the latest available version";
@@ -21,12 +21,9 @@ export class UpgradeSubCommand {
21
21
  description: "Override the detected install method",
22
22
  },
23
23
  ];
24
- #upgradeService;
25
- constructor(upgradeService) {
26
- this.#upgradeService = upgradeService;
27
- }
28
24
  async execute(context, argumentValues) {
29
25
  const printerService = context.getServiceById(PRINTER_SERVICE_ID);
26
+ const upgradeService = context.getServiceById(UPGRADE_SERVICE_ID);
30
27
  const cliName = context.cliConfig.name;
31
28
  const currentVersion = context.cliConfig.version;
32
29
  const os = argumentValues.os;
@@ -34,8 +31,8 @@ export class UpgradeSubCommand {
34
31
  const installMethod = argumentValues["install-method"];
35
32
  await printerService.showSpinner(`Looking for version newer than ${currentVersion}`);
36
33
  const checkResult = os === undefined && installMethod === undefined
37
- ? await this.#upgradeService.getUpgradeCheckResult(true)
38
- : await this.#upgradeService.checkForUpgrade(os, arch, installMethod);
34
+ ? await upgradeService.refreshUpgradeCheckCache()
35
+ : await upgradeService.checkForUpgrade(os, arch, installMethod);
39
36
  await printerService.hideSpinner();
40
37
  if (checkResult.status === "unsupported") {
41
38
  await printerService.error(`No upgrade location is configured for the detected or requested platform.\n`, Icon.FAILURE);
@@ -45,16 +42,11 @@ export class UpgradeSubCommand {
45
42
  await printerService.error(`Failed to check for updates: ${checkResult.error.message}. Please try again later.\n`, Icon.FAILURE);
46
43
  return;
47
44
  }
48
- if (checkResult.status === "pending") {
49
- // Unreachable: getUpgradeCheckResult(true) and checkForUpgrade() always run to completion.
50
- await printerService.error(`Failed to check for updates.\n`, Icon.FAILURE);
51
- return;
52
- }
53
45
  if (!checkResult.updateAvailable) {
54
46
  await printerService.print(`${cliName} is already up to date: ${checkResult.currentVersion}\n`, Icon.INFORMATION);
55
47
  return;
56
48
  }
57
- const upgradeResult = await this.#upgradeService.upgrade(os, arch, installMethod);
49
+ const upgradeResult = await upgradeService.upgrade(os, arch, installMethod);
58
50
  if (!upgradeResult.ok) {
59
51
  await printerService.error(`Failed to upgrade ${cliName}: ${upgradeResult.error?.message ?? "unknown error"}\n`, Icon.FAILURE);
60
52
  return;
@@ -0,0 +1,17 @@
1
+ import type { StartupTask } from "@flowscripter/dynamic-cli-framework-api";
2
+ export declare const BANNER_STARTUP_TASK_ID = "@flowscripter/dynamic-cli-framework/banner-startup-task";
3
+ /**
4
+ * Build the ascii banner {@link StartupTask}, replacing the old `BannerServiceProvider`.
5
+ *
6
+ * A consumer opts in by passing the result of this function in the `serviceProviders` array
7
+ * argument of {@link launchMultiCommandCLI}/{@link launchSingleCommandCLI} (or via
8
+ * `BaseCLI.addStartupTask`), the same way `new BannerServiceProvider(priority)` used to be passed
9
+ * in that array - there's no default/automatic banner registration.
10
+ *
11
+ * @param priority the priority of the task (higher runs earlier - the same semantics
12
+ * `BannerServiceProvider.servicePriority` had).
13
+ * @param fontName an optional [FIGlet](http://www.figlet.org) font name to use for the banner,
14
+ * defaults to "standard".
15
+ */
16
+ export default function createBannerStartupTask(priority: number, fontName?: string): StartupTask;
17
+ //# sourceMappingURL=bannerStartupTask.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bannerStartupTask.d.ts","sourceRoot":"","sources":["../../../../src/startup/banner/bannerStartupTask.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,WAAW,EAEZ,MAAM,yCAAyC,CAAC;AAYjD,eAAO,MAAM,sBAAsB,4DAA4D,CAAC;AAEhG;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAC7C,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAa,GACpB,WAAW,CA2Db"}
@@ -0,0 +1,66 @@
1
+ import { ASCII_BANNER_GENERATOR_SERVICE_ID, CONFIGURATION_SERVICE_ID, KEY_VALUE_SERVICE_ID, PRINTER_SERVICE_ID, } from "@flowscripter/dynamic-cli-framework-api";
2
+ import { UPGRADE_CHECK_CACHE_KEY } from "../../service/upgrade/DefaultUpgradeService.js";
3
+ import NoBannerCommand, {} from "./command/NoBannerCommand.js";
4
+ export const BANNER_STARTUP_TASK_ID = "@flowscripter/dynamic-cli-framework/banner-startup-task";
5
+ /**
6
+ * Build the ascii banner {@link StartupTask}, replacing the old `BannerServiceProvider`.
7
+ *
8
+ * A consumer opts in by passing the result of this function in the `serviceProviders` array
9
+ * argument of {@link launchMultiCommandCLI}/{@link launchSingleCommandCLI} (or via
10
+ * `BaseCLI.addStartupTask`), the same way `new BannerServiceProvider(priority)` used to be passed
11
+ * in that array - there's no default/automatic banner registration.
12
+ *
13
+ * @param priority the priority of the task (higher runs earlier - the same semantics
14
+ * `BannerServiceProvider.servicePriority` had).
15
+ * @param fontName an optional [FIGlet](http://www.figlet.org) font name to use for the banner,
16
+ * defaults to "standard".
17
+ */
18
+ export default function createBannerStartupTask(priority, fontName = "standard") {
19
+ const state = { printBanner: true };
20
+ return {
21
+ id: BANNER_STARTUP_TASK_ID,
22
+ priority,
23
+ mode: "blocking",
24
+ modifierCommands: [new NoBannerCommand(state, priority)],
25
+ run: async (context) => {
26
+ if (!state.printBanner) {
27
+ return;
28
+ }
29
+ const printerService = context.getServiceById(PRINTER_SERVICE_ID);
30
+ const asciiBannerGeneratorService = context.getServiceById(ASCII_BANNER_GENERATOR_SERVICE_ID);
31
+ const { cliConfig } = context;
32
+ const bannerText = await asciiBannerGeneratorService.generate(cliConfig.name.toUpperCase(), {
33
+ fontName,
34
+ subMessage: cliConfig.subMessage,
35
+ });
36
+ await printerService.info(printerService.blue(bannerText));
37
+ if (cliConfig.description !== undefined) {
38
+ await printerService.info(` ${printerService.primary(cliConfig.description)}\n`);
39
+ }
40
+ if (cliConfig.version.length > 0) {
41
+ let versionLine = "version: " + cliConfig.version;
42
+ // Cheap cache read only - never calls checkForUpgrade()/getUpgradeCheckResult() live,
43
+ // since that could stall startup on a network/spawn call. A first-ever run (no cached
44
+ // key yet) simply shows no upgrade-available suffix - that's expected, not a bug.
45
+ if (context.doesServiceExist(KEY_VALUE_SERVICE_ID)) {
46
+ const keyValueService = context.getServiceById(KEY_VALUE_SERVICE_ID);
47
+ if (await keyValueService.has(UPGRADE_CHECK_CACHE_KEY)) {
48
+ const result = (await keyValueService.get(UPGRADE_CHECK_CACHE_KEY));
49
+ if (result.status === "checked" && result.updateAvailable) {
50
+ versionLine += ` (${result.latestVersion} available, run '${cliConfig.name} upgrade')`;
51
+ }
52
+ }
53
+ }
54
+ await printerService.info(` ${printerService.secondary(versionLine)}\n`);
55
+ }
56
+ if (context.doesServiceExist(CONFIGURATION_SERVICE_ID)) {
57
+ const configurationService = context.getServiceById(CONFIGURATION_SERVICE_ID);
58
+ const configLocation = configurationService.configLocation;
59
+ if (configLocation && configLocation.length > 0) {
60
+ await printerService.info(` ${printerService.secondary("config: " + configLocation)}\n`);
61
+ }
62
+ }
63
+ await printerService.info("\n");
64
+ },
65
+ };
66
+ }
@@ -2,7 +2,13 @@ import type { GlobalModifierCommand } from "@flowscripter/dynamic-cli-framework-
2
2
  import { type SingleValueType } from "@flowscripter/dynamic-cli-framework-api";
3
3
  import type { Context } from "@flowscripter/dynamic-cli-framework-api";
4
4
  import type { GlobalCommandArgument } from "@flowscripter/dynamic-cli-framework-api";
5
- import type BannerServiceProvider from "../BannerServiceProvider.ts";
5
+ /**
6
+ * Task-local, mutable state shared between {@link NoBannerCommand} and the banner
7
+ * {@link StartupTask}'s `run()`, in place of a held `BannerServiceProvider` reference.
8
+ */
9
+ export interface BannerState {
10
+ printBanner: boolean;
11
+ }
6
12
  /**
7
13
  * Command to disable banner output for the CLI application.
8
14
  */
@@ -13,7 +19,7 @@ export default class NoBannerCommand implements GlobalModifierCommand {
13
19
  readonly enableConfiguration = true;
14
20
  readonly argument: GlobalCommandArgument;
15
21
  readonly executePriority: number;
16
- constructor(bannerServiceProvider: BannerServiceProvider, executePriority: number);
22
+ constructor(state: BannerState, executePriority: number);
17
23
  execute(_context: Context, argumentValue: SingleValueType): Promise<void>;
18
24
  }
19
25
  //# sourceMappingURL=NoBannerCommand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoBannerCommand.d.ts","sourceRoot":"","sources":["../../../../../src/startup/banner/command/NoBannerCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,EAAE,KAAK,eAAe,EAAiB,MAAM,yCAAyC,CAAC;AAC9F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAErF;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,eAAgB,YAAW,qBAAqB;;IACnE,QAAQ,CAAC,IAAI,eAAe;IAC5B,QAAQ,CAAC,WAAW,8BAA8B;IAClD,QAAQ,CAAC,mBAAmB,QAAQ;IACpC,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAItC;IACF,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAIjC,YAAmB,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,EAG7D;IAEM,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/E;CACF"}
@@ -12,13 +12,13 @@ export default class NoBannerCommand {
12
12
  configurationKey: "NO_BANNER",
13
13
  };
14
14
  executePriority;
15
- #bannerServiceProvider;
16
- constructor(bannerServiceProvider, executePriority) {
17
- this.#bannerServiceProvider = bannerServiceProvider;
15
+ #state;
16
+ constructor(state, executePriority) {
17
+ this.#state = state;
18
18
  this.executePriority = executePriority;
19
19
  }
20
20
  execute(_context, argumentValue) {
21
- this.#bannerServiceProvider.printBanner = !argumentValue;
21
+ this.#state.printBanner = !argumentValue;
22
22
  return Promise.resolve();
23
23
  }
24
24
  }
package/index.ts CHANGED
@@ -52,8 +52,10 @@ export type {
52
52
  ChiselBannerGenerateOptions,
53
53
  } from "./src/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.ts";
54
54
 
55
- export { default as BannerServiceProvider } from "./src/service/banner/BannerServiceProvider.ts";
55
+ export { default as createBannerStartupTask } from "./src/startup/banner/bannerStartupTask.ts";
56
56
 
57
+ export { CONFIGURATION_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
58
+ export type { ConfigurationService } from "@flowscripter/dynamic-cli-framework-api";
57
59
  export { KEY_VALUE_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
58
60
  export { SECRET_SENTINEL_PREFIX } from "@flowscripter/dynamic-cli-framework-api";
59
61
  export type { KeyValueService } from "@flowscripter/dynamic-cli-framework-api";
@@ -62,7 +64,11 @@ export { default as DefaultKeyValueService } from "./src/service/configuration/D
62
64
  export { default as DefaultSecretService } from "./src/service/configuration/DefaultSecretService.ts";
63
65
  export type { SecretsApi } from "./src/service/configuration/DefaultSecretService.ts";
64
66
  export { default as ConfigCommand } from "./src/service/configuration/command/ConfigCommand.ts";
65
- export { default as ConfigurationServiceProvider } from "./src/service/configuration/ConfigurationServiceProvider.ts";
67
+ export {
68
+ default as ConfigurationServiceProvider,
69
+ type KeyValueServiceScopeType,
70
+ } from "./src/service/configuration/ConfigurationServiceProvider.ts";
71
+ export { default as KeyValueServiceProvider } from "./src/service/configuration/KeyValueServiceProvider.ts";
66
72
 
67
73
  export { PRINTER_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
68
74
  export type { PrinterService } from "@flowscripter/dynamic-cli-framework-api";
@@ -71,10 +77,20 @@ export { default as DefaultPrinterService } from "./src/service/printer/DefaultP
71
77
  export { default as PrinterServiceProvider } from "./src/service/printer/PrinterServiceProvider.ts";
72
78
 
73
79
  export { SHUTDOWN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
74
- export type { ShutdownService } from "@flowscripter/dynamic-cli-framework-api";
80
+ export type { ShutdownService, ShutdownTask } from "@flowscripter/dynamic-cli-framework-api";
75
81
  export { default as DefaultShutdownService } from "./src/service/shutdown/DefaultShutdownService.ts";
76
82
  export { default as ShutdownServiceProvider } from "./src/service/shutdown/ShutdownServiceProvider.ts";
77
83
 
84
+ export { STARTUP_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
85
+ export type {
86
+ StartupService,
87
+ StartupTask,
88
+ StartupTaskContext,
89
+ StartupTaskMode,
90
+ } from "@flowscripter/dynamic-cli-framework-api";
91
+ export { default as DefaultStartupService } from "./src/service/startup/DefaultStartupService.ts";
92
+ export { default as StartupServiceProvider } from "./src/service/startup/StartupServiceProvider.ts";
93
+
78
94
  export { FETCH_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
79
95
  export type { FetchOptions, FetchService } from "@flowscripter/dynamic-cli-framework-api";
80
96
  export { default as DefaultFetchService } from "./src/service/fetch/DefaultFetchService.ts";