@flowscripter/dynamic-cli-framework 6.0.1 → 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.
- package/README.md +2 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/src/cli/BaseCLI.d.ts +8 -0
- package/dist/src/cli/BaseCLI.d.ts.map +1 -1
- package/dist/src/cli/BaseCLI.js +37 -4
- package/dist/src/launcher.d.ts +3 -3
- package/dist/src/launcher.d.ts.map +1 -1
- package/dist/src/launcher.js +6 -4
- package/dist/src/runtime/lifecycle/PriorityTaskList.d.ts +22 -0
- package/dist/src/runtime/lifecycle/PriorityTaskList.d.ts.map +1 -0
- package/dist/src/runtime/lifecycle/PriorityTaskList.js +23 -0
- package/dist/src/runtime/runner.d.ts +15 -7
- package/dist/src/runtime/runner.d.ts.map +1 -1
- package/dist/src/runtime/runner.js +82 -53
- package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts +26 -82
- package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts.map +1 -1
- package/dist/src/service/configuration/ConfigurationServiceProvider.js +68 -163
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts +6 -3
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts.map +1 -1
- package/dist/src/service/configuration/DefaultKeyValueService.js +7 -24
- package/dist/src/service/configuration/DefaultSecretService.d.ts +1 -3
- package/dist/src/service/configuration/DefaultSecretService.d.ts.map +1 -1
- package/dist/src/service/configuration/DefaultSecretService.js +2 -10
- package/dist/src/service/configuration/KeyValueServiceProvider.d.ts +105 -0
- package/dist/src/service/configuration/KeyValueServiceProvider.d.ts.map +1 -0
- package/dist/src/service/configuration/KeyValueServiceProvider.js +201 -0
- package/dist/src/service/configuration/command/ConfigCommand.d.ts +4 -5
- package/dist/src/service/configuration/command/ConfigCommand.d.ts.map +1 -1
- package/dist/src/service/configuration/command/ConfigCommand.js +7 -6
- package/dist/src/service/configuration/command/DumpConfigCommand.d.ts +7 -3
- package/dist/src/service/configuration/command/DumpConfigCommand.d.ts.map +1 -1
- package/dist/src/service/configuration/command/DumpConfigCommand.js +10 -5
- package/dist/src/service/fetch/DefaultFetchService.d.ts +2 -2
- package/dist/src/service/fetch/DefaultFetchService.d.ts.map +1 -1
- package/dist/src/service/fetch/DefaultFetchService.js +17 -12
- package/dist/src/service/fetch/FetchServiceProvider.d.ts.map +1 -1
- package/dist/src/service/fetch/FetchServiceProvider.js +2 -3
- package/dist/src/service/printer/PrinterServiceProvider.d.ts.map +1 -1
- package/dist/src/service/printer/PrinterServiceProvider.js +11 -7
- package/dist/src/service/printer/command/DarkModeCommand.d.ts +3 -5
- package/dist/src/service/printer/command/DarkModeCommand.d.ts.map +1 -1
- package/dist/src/service/printer/command/DarkModeCommand.js +6 -6
- package/dist/src/service/printer/command/LogLevelCommand.d.ts +3 -5
- package/dist/src/service/printer/command/LogLevelCommand.d.ts.map +1 -1
- package/dist/src/service/printer/command/LogLevelCommand.js +9 -9
- package/dist/src/service/printer/command/NoColorCommand.d.ts +3 -5
- package/dist/src/service/printer/command/NoColorCommand.d.ts.map +1 -1
- package/dist/src/service/printer/command/NoColorCommand.js +6 -6
- package/dist/src/service/printer/command/NoHyperlinksCommand.d.ts +2 -4
- package/dist/src/service/printer/command/NoHyperlinksCommand.d.ts.map +1 -1
- package/dist/src/service/printer/command/NoHyperlinksCommand.js +5 -5
- package/dist/src/service/prompter/PrompterServiceProvider.js +1 -1
- package/dist/src/service/prompter/command/NoPromptCommand.d.ts +2 -4
- package/dist/src/service/prompter/command/NoPromptCommand.d.ts.map +1 -1
- package/dist/src/service/prompter/command/NoPromptCommand.js +5 -5
- package/dist/src/service/shutdown/DefaultShutdownService.d.ts +4 -3
- package/dist/src/service/shutdown/DefaultShutdownService.d.ts.map +1 -1
- package/dist/src/service/shutdown/DefaultShutdownService.js +4 -3
- package/dist/src/service/shutdown/ShutdownServiceProvider.js +2 -2
- package/dist/src/service/spawn/DefaultSpawnService.d.ts +2 -2
- package/dist/src/service/spawn/DefaultSpawnService.d.ts.map +1 -1
- package/dist/src/service/spawn/DefaultSpawnService.js +17 -14
- package/dist/src/service/spawn/SpawnServiceProvider.d.ts.map +1 -1
- package/dist/src/service/spawn/SpawnServiceProvider.js +1 -4
- package/dist/src/service/startup/DefaultStartupService.d.ts +10 -0
- package/dist/src/service/startup/DefaultStartupService.d.ts.map +1 -0
- package/dist/src/service/startup/DefaultStartupService.js +13 -0
- package/dist/src/service/startup/StartupServiceProvider.d.ts +17 -0
- package/dist/src/service/startup/StartupServiceProvider.d.ts.map +1 -0
- package/dist/src/service/startup/StartupServiceProvider.js +26 -0
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts +10 -4
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -1
- package/dist/src/service/upgrade/DefaultUpgradeService.js +156 -37
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts +10 -0
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts.map +1 -1
- package/dist/src/service/upgrade/UpgradeServiceProvider.js +25 -6
- package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts +0 -3
- package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts.map +1 -1
- package/dist/src/service/upgrade/command/UpgradeSubCommand.js +5 -13
- package/dist/src/startup/banner/bannerStartupTask.d.ts +17 -0
- package/dist/src/startup/banner/bannerStartupTask.d.ts.map +1 -0
- package/dist/src/startup/banner/bannerStartupTask.js +66 -0
- package/dist/src/{service → startup}/banner/command/NoBannerCommand.d.ts +8 -2
- package/dist/src/startup/banner/command/NoBannerCommand.d.ts.map +1 -0
- package/dist/src/{service → startup}/banner/command/NoBannerCommand.js +4 -4
- package/index.ts +19 -3
- package/package.json +2 -2
- package/src/cli/BaseCLI.ts +49 -3
- package/src/launcher.ts +7 -2
- package/src/runtime/lifecycle/PriorityTaskList.ts +34 -0
- package/src/runtime/runner.ts +106 -67
- package/src/service/configuration/ConfigurationServiceProvider.ts +80 -180
- package/src/service/configuration/DefaultKeyValueService.ts +8 -27
- package/src/service/configuration/DefaultSecretService.ts +3 -13
- package/src/service/configuration/KeyValueServiceProvider.ts +246 -0
- package/src/service/configuration/command/ConfigCommand.ts +12 -11
- package/src/service/configuration/command/DumpConfigCommand.ts +10 -9
- package/src/service/fetch/DefaultFetchService.ts +18 -12
- package/src/service/fetch/FetchServiceProvider.ts +2 -4
- package/src/service/printer/PrinterServiceProvider.ts +11 -7
- package/src/service/printer/command/DarkModeCommand.ts +7 -8
- package/src/service/printer/command/LogLevelCommand.ts +10 -11
- package/src/service/printer/command/NoColorCommand.ts +7 -8
- package/src/service/printer/command/NoHyperlinksCommand.ts +6 -7
- package/src/service/prompter/PrompterServiceProvider.ts +1 -1
- package/src/service/prompter/command/NoPromptCommand.ts +6 -7
- package/src/service/shutdown/DefaultShutdownService.ts +5 -4
- package/src/service/shutdown/ShutdownServiceProvider.ts +2 -2
- package/src/service/spawn/DefaultSpawnService.ts +18 -14
- package/src/service/spawn/SpawnServiceProvider.ts +1 -5
- package/src/service/startup/DefaultStartupService.ts +17 -0
- package/src/service/startup/StartupServiceProvider.ts +36 -0
- package/src/service/upgrade/DefaultUpgradeService.ts +202 -42
- package/src/service/upgrade/UpgradeServiceProvider.ts +31 -8
- package/src/service/upgrade/command/UpgradeSubCommand.ts +5 -15
- package/src/startup/banner/bannerStartupTask.ts +96 -0
- package/src/{service → startup}/banner/command/NoBannerCommand.ts +12 -5
- package/dist/src/service/banner/BannerServiceProvider.d.ts +0 -29
- package/dist/src/service/banner/BannerServiceProvider.d.ts.map +0 -1
- package/dist/src/service/banner/BannerServiceProvider.js +0 -69
- package/dist/src/service/banner/command/NoBannerCommand.d.ts.map +0 -1
- 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
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
41
|
-
this.#
|
|
42
|
-
|
|
43
|
-
|
|
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(
|
|
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
|
|
80
|
-
//
|
|
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
|
-
|
|
93
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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(
|
|
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;
|
|
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(
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
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 (!
|
|
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;
|
|
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
|
|
38
|
-
: await
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
#
|
|
16
|
-
constructor(
|
|
17
|
-
this.#
|
|
15
|
+
#state;
|
|
16
|
+
constructor(state, executePriority) {
|
|
17
|
+
this.#state = state;
|
|
18
18
|
this.executePriority = executePriority;
|
|
19
19
|
}
|
|
20
20
|
execute(_context, argumentValue) {
|
|
21
|
-
this.#
|
|
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
|
|
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 {
|
|
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";
|