@flowscripter/dynamic-cli-framework 2.2.0 → 2.3.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.
- package/dist/src/cli/BaseCLI.d.ts +2 -0
- package/dist/src/cli/BaseCLI.d.ts.map +1 -1
- package/dist/src/cli/BaseCLI.js +8 -0
- package/dist/src/cli/BaseCLIFeatureOptions.d.ts +3 -0
- package/dist/src/cli/BaseCLIFeatureOptions.d.ts.map +1 -1
- package/dist/src/command/VersionCommand.d.ts.map +1 -1
- package/dist/src/command/VersionCommand.js +10 -1
- package/dist/src/service/banner/BannerServiceProvider.d.ts.map +1 -1
- package/dist/src/service/banner/BannerServiceProvider.js +8 -0
- package/dist/src/service/spawn/DefaultSpawnService.d.ts +6 -0
- package/dist/src/service/spawn/DefaultSpawnService.d.ts.map +1 -1
- package/dist/src/service/spawn/DefaultSpawnService.js +22 -1
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts +14 -0
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -0
- package/dist/src/service/upgrade/DefaultUpgradeService.js +312 -0
- package/dist/src/service/upgrade/UpgradeLocationsConfig.d.ts +51 -0
- package/dist/src/service/upgrade/UpgradeLocationsConfig.d.ts.map +1 -0
- package/dist/src/service/upgrade/UpgradeLocationsConfig.js +0 -0
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts +13 -0
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts.map +1 -0
- package/dist/src/service/upgrade/UpgradeServiceProvider.js +120 -0
- package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts +16 -0
- package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts.map +1 -0
- package/dist/src/service/upgrade/command/UpgradeSubCommand.js +50 -0
- package/package.json +5 -3
- package/src/cli/BaseCLI.ts +9 -0
- package/src/cli/BaseCLIFeatureOptions.ts +4 -0
- package/src/command/VersionCommand.ts +13 -1
- package/src/service/banner/BannerServiceProvider.ts +13 -0
- package/src/service/spawn/DefaultSpawnService.ts +23 -1
- package/src/service/upgrade/DefaultUpgradeService.ts +379 -0
- package/src/service/upgrade/UpgradeLocationsConfig.ts +53 -0
- package/src/service/upgrade/UpgradeServiceProvider.ts +150 -0
- package/src/service/upgrade/command/UpgradeSubCommand.ts +85 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { UPGRADE_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
+
import { PROMPTER_SERVICE_ID, PromptType } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
|
+
import { KEY_VALUE_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
4
|
+
import { SPAWN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
5
|
+
import { Icon, PRINTER_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
6
|
+
import DefaultUpgradeService from "./DefaultUpgradeService.js";
|
|
7
|
+
import { UpgradeSubCommand } from "./command/UpgradeSubCommand.js";
|
|
8
|
+
import getLogger from "../../util/logger.js";
|
|
9
|
+
const logger = getLogger("UpgradeServiceProvider");
|
|
10
|
+
export default class UpgradeServiceProvider {
|
|
11
|
+
serviceId = UPGRADE_SERVICE_ID;
|
|
12
|
+
servicePriority;
|
|
13
|
+
#config;
|
|
14
|
+
#upgradeService;
|
|
15
|
+
#cliConfig;
|
|
16
|
+
constructor(servicePriority, config) {
|
|
17
|
+
this.servicePriority = servicePriority;
|
|
18
|
+
this.#config = config;
|
|
19
|
+
}
|
|
20
|
+
getServiceInfo(cliConfig) {
|
|
21
|
+
this.#cliConfig = cliConfig;
|
|
22
|
+
this.#upgradeService = new DefaultUpgradeService(this.#config, cliConfig);
|
|
23
|
+
return Promise.resolve({
|
|
24
|
+
service: this.#upgradeService,
|
|
25
|
+
commands: [new UpgradeSubCommand(this.#upgradeService)],
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async initService(context) {
|
|
29
|
+
const upgradeService = this.#upgradeService;
|
|
30
|
+
const cliConfig = this.#cliConfig;
|
|
31
|
+
if (context.doesServiceExist(SPAWN_SERVICE_ID)) {
|
|
32
|
+
const spawnService = context.getServiceById(SPAWN_SERVICE_ID);
|
|
33
|
+
upgradeService.setDependencies(spawnService);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
logger.debug(() => "SpawnService not available, upgrade install methods will be unavailable");
|
|
37
|
+
}
|
|
38
|
+
if (!context.doesServiceExist(PROMPTER_SERVICE_ID)) {
|
|
39
|
+
logger.debug(() => "PrompterService not available, skipping auto-upgrade");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!context.doesServiceExist(KEY_VALUE_SERVICE_ID)) {
|
|
43
|
+
logger.debug(() => "KeyValueService not available, skipping auto-upgrade");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const keyValueService = context.getServiceById(KEY_VALUE_SERVICE_ID);
|
|
47
|
+
if (await keyValueService.hasKey("upgrade-status")) {
|
|
48
|
+
const status = await keyValueService.getKey("upgrade-status");
|
|
49
|
+
if (status === "declined") {
|
|
50
|
+
logger.debug(() => "Auto-upgrade previously declined, skipping");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (status === "enabled") {
|
|
54
|
+
await this.#checkAndUpgrade(context, upgradeService, cliConfig);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const prompterService = context.getServiceById(PROMPTER_SERVICE_ID);
|
|
59
|
+
if (!prompterService.promptEnabled) {
|
|
60
|
+
logger.debug(() => "Prompting is disabled, skipping auto-upgrade prompt");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const os = upgradeService.detectOs();
|
|
64
|
+
const arch = upgradeService.detectArch();
|
|
65
|
+
if (!os || !arch) {
|
|
66
|
+
logger.debug(() => "Unsupported OS/arch, skipping auto-upgrade prompt");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const installMethod = await upgradeService.detectInstallMethod(os);
|
|
70
|
+
if (!installMethod) {
|
|
71
|
+
logger.debug(() => "No supported install method detected, skipping auto-upgrade prompt");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
let enableResult;
|
|
75
|
+
try {
|
|
76
|
+
enableResult = await prompterService.prompt({
|
|
77
|
+
name: "enable-upgrade",
|
|
78
|
+
promptText: "Would you like to enable automatic upgrades on startup?",
|
|
79
|
+
description: "This will check for and install newer versions of " +
|
|
80
|
+
`${cliConfig.name} before it runs each time.`,
|
|
81
|
+
type: PromptType.TOGGLE,
|
|
82
|
+
options: [
|
|
83
|
+
{ displayValue: "Yes", returnedValue: true },
|
|
84
|
+
{ displayValue: "No", returnedValue: false },
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (enableResult.value !== true) {
|
|
92
|
+
await keyValueService.setKey("upgrade-status", "declined");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
await keyValueService.setKey("upgrade-status", "enabled");
|
|
96
|
+
await this.#checkAndUpgrade(context, upgradeService, cliConfig);
|
|
97
|
+
}
|
|
98
|
+
async #checkAndUpgrade(context, upgradeService, cliConfig) {
|
|
99
|
+
try {
|
|
100
|
+
const checkResult = await upgradeService.checkForUpgrade();
|
|
101
|
+
if (!checkResult?.updateAvailable) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const upgradeResult = await upgradeService.upgrade();
|
|
105
|
+
if (!context.doesServiceExist(PRINTER_SERVICE_ID)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const printerService = context.getServiceById(PRINTER_SERVICE_ID);
|
|
109
|
+
if (upgradeResult.ok) {
|
|
110
|
+
await printerService.info(`${cliConfig.name} upgraded (${upgradeResult.oldVersion} -> ${upgradeResult.newVersion})\n`, Icon.SUCCESS);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await printerService.error(`Auto-upgrade failed: ${upgradeResult.error?.message ?? "unknown error"}\n`, Icon.FAILURE);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
logger.debug(() => `Auto-upgrade check failed: ${error}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SubCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
+
import type { Option } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
|
+
import type { Context } from "@flowscripter/dynamic-cli-framework-api";
|
|
4
|
+
import { type ArgumentValues } from "@flowscripter/dynamic-cli-framework-api";
|
|
5
|
+
import type DefaultUpgradeService from "../DefaultUpgradeService.ts";
|
|
6
|
+
export declare class UpgradeSubCommand implements SubCommand {
|
|
7
|
+
#private;
|
|
8
|
+
readonly name = "upgrade";
|
|
9
|
+
readonly description = "Upgrade to the latest available version";
|
|
10
|
+
readonly enableConfiguration = false;
|
|
11
|
+
readonly positionals: never[];
|
|
12
|
+
readonly options: ReadonlyArray<Option>;
|
|
13
|
+
constructor(upgradeService: DefaultUpgradeService);
|
|
14
|
+
execute(context: Context, argumentValues: ArgumentValues): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=UpgradeSubCommand.d.ts.map
|
|
@@ -0,0 +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,EACL,KAAK,cAAc,EAEpB,MAAM,yCAAyC,CAAC;AASjD,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;gBAIiB,cAAc,EAAE,qBAAqB;IAI3C,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAsCtF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ArgumentValueTypeName, } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
+
import { Icon, InstallMethod, PRINTER_SERVICE_ID, SupportedArch, SupportedOs, } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
|
+
export class UpgradeSubCommand {
|
|
4
|
+
name = "upgrade";
|
|
5
|
+
description = "Upgrade to the latest available version";
|
|
6
|
+
enableConfiguration = false;
|
|
7
|
+
positionals = [];
|
|
8
|
+
options = [
|
|
9
|
+
{
|
|
10
|
+
name: "os",
|
|
11
|
+
type: ArgumentValueTypeName.STRING,
|
|
12
|
+
isOptional: true,
|
|
13
|
+
allowableValues: Object.values(SupportedOs),
|
|
14
|
+
description: "Override the detected operating system",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "install-method",
|
|
18
|
+
type: ArgumentValueTypeName.STRING,
|
|
19
|
+
isOptional: true,
|
|
20
|
+
allowableValues: Object.values(InstallMethod),
|
|
21
|
+
description: "Override the detected install method",
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
#upgradeService;
|
|
25
|
+
constructor(upgradeService) {
|
|
26
|
+
this.#upgradeService = upgradeService;
|
|
27
|
+
}
|
|
28
|
+
async execute(context, argumentValues) {
|
|
29
|
+
const printerService = context.getServiceById(PRINTER_SERVICE_ID);
|
|
30
|
+
const cliName = context.cliConfig.name;
|
|
31
|
+
const os = argumentValues.os;
|
|
32
|
+
const arch = undefined;
|
|
33
|
+
const installMethod = argumentValues["install-method"];
|
|
34
|
+
const checkResult = await this.#upgradeService.checkForUpgrade(os, arch, installMethod);
|
|
35
|
+
if (!checkResult) {
|
|
36
|
+
await printerService.error(`No upgrade location is configured for the detected or requested platform.\n`, Icon.FAILURE);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!checkResult.updateAvailable) {
|
|
40
|
+
await printerService.print(`${cliName} is already up to date (${checkResult.currentVersion}).\n`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const upgradeResult = await this.#upgradeService.upgrade(os, arch, installMethod);
|
|
44
|
+
if (!upgradeResult.ok) {
|
|
45
|
+
await printerService.error(`Failed to upgrade ${cliName}: ${upgradeResult.error?.message ?? "unknown error"}\n`, Icon.FAILURE);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
await printerService.print(`${cliName} upgraded (${upgradeResult.oldVersion} -> ${upgradeResult.newVersion})\n`, Icon.SUCCESS);
|
|
49
|
+
}
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowscripter/dynamic-cli-framework",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "A framework for developing CLI applications which supports dynamic discovery and installation of new commands",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -48,19 +48,21 @@
|
|
|
48
48
|
"build": "tsc -p tsconfig.build.json"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@flowscripter/dynamic-cli-framework-api": "^1.
|
|
52
|
-
"@flowscripter/dynamic-plugin-framework": "1.11.
|
|
51
|
+
"@flowscripter/dynamic-cli-framework-api": "^1.2.0",
|
|
52
|
+
"@flowscripter/dynamic-plugin-framework": "1.11.1",
|
|
53
53
|
"emphasize": "^7.0.0",
|
|
54
54
|
"fastest-levenshtein": "^1.0.16",
|
|
55
55
|
"figlet": "^1.11.0",
|
|
56
56
|
"highlight.js": "^11.11.1",
|
|
57
57
|
"prettier": "^3.9.4",
|
|
58
|
+
"semver": "^7.7.3",
|
|
58
59
|
"supports-color": "^10.2.2",
|
|
59
60
|
"supports-terminal-graphics": "^0.1.0"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@types/bun": "^1.3.14",
|
|
63
64
|
"@types/node": "^26.1.0",
|
|
65
|
+
"@types/semver": "^7.7.1",
|
|
64
66
|
"oxfmt": "0.57.0",
|
|
65
67
|
"oxlint": "1.72.0"
|
|
66
68
|
},
|
package/src/cli/BaseCLI.ts
CHANGED
|
@@ -52,6 +52,7 @@ import DefaultCompletionService from "../service/completion/DefaultCompletionSer
|
|
|
52
52
|
import CompletionServiceProvider from "../service/completion/CompletionServiceProvider.ts";
|
|
53
53
|
import ImagePrinterServiceProvider from "../service/imagePrinter/ImagePrinterServiceProvider.ts";
|
|
54
54
|
import SpawnServiceProvider from "../service/spawn/SpawnServiceProvider.ts";
|
|
55
|
+
import UpgradeServiceProvider from "../service/upgrade/UpgradeServiceProvider.ts";
|
|
55
56
|
const logger = getLogger("BaseCLI");
|
|
56
57
|
|
|
57
58
|
/**
|
|
@@ -71,6 +72,8 @@ const logger = getLogger("BaseCLI");
|
|
|
71
72
|
*
|
|
72
73
|
* * {@link ImagePrinterServiceProvider}
|
|
73
74
|
* * {@link SpawnServiceProvider}
|
|
75
|
+
* * {@link CompletionServiceProvider}
|
|
76
|
+
* * {@link UpgradeServiceProvider}
|
|
74
77
|
*
|
|
75
78
|
* `keyReader` is optional. If omitted, or if the stderr {@link Terminal} is not a TTY, prompting is
|
|
76
79
|
* unavailable: {@link PrompterServiceProvider} (and {@link ArgumentPrompterServiceProvider}, if
|
|
@@ -133,6 +136,8 @@ export default class BaseCLI implements CLI {
|
|
|
133
136
|
completionServiceEnabled: false,
|
|
134
137
|
imagePrinterServiceEnabled: false,
|
|
135
138
|
spawnServiceEnabled: false,
|
|
139
|
+
upgradeServiceEnabled: false,
|
|
140
|
+
upgradeLocationsConfig: { supportedPlatforms: [] },
|
|
136
141
|
validateAllCommands: false,
|
|
137
142
|
promptingEnabled: true,
|
|
138
143
|
...options,
|
|
@@ -276,6 +281,10 @@ export default class BaseCLI implements CLI {
|
|
|
276
281
|
);
|
|
277
282
|
}
|
|
278
283
|
|
|
284
|
+
if (this.#options.upgradeServiceEnabled) {
|
|
285
|
+
this.addServiceProvider(new UpgradeServiceProvider(6, this.#options.upgradeLocationsConfig));
|
|
286
|
+
}
|
|
287
|
+
|
|
279
288
|
const configurationServiceProvider = new ConfigurationServiceProvider(
|
|
280
289
|
90,
|
|
281
290
|
this.#options.envVarsSupportEnabled,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { UpgradeLocationsConfig } from "../service/upgrade/UpgradeLocationsConfig.ts";
|
|
2
|
+
|
|
1
3
|
export default interface BaseCLIFeatureOptions {
|
|
2
4
|
readonly configFileSupportEnabled?: boolean;
|
|
3
5
|
readonly envVarsSupportEnabled?: boolean;
|
|
@@ -7,6 +9,8 @@ export default interface BaseCLIFeatureOptions {
|
|
|
7
9
|
readonly completionServiceEnabled?: boolean;
|
|
8
10
|
readonly imagePrinterServiceEnabled?: boolean;
|
|
9
11
|
readonly spawnServiceEnabled?: boolean;
|
|
12
|
+
readonly upgradeServiceEnabled?: boolean;
|
|
13
|
+
readonly upgradeLocationsConfig?: UpgradeLocationsConfig;
|
|
10
14
|
readonly validateAllCommands?: boolean;
|
|
11
15
|
readonly promptingEnabled?: boolean;
|
|
12
16
|
}
|
|
@@ -2,6 +2,8 @@ import type { Context } from "@flowscripter/dynamic-cli-framework-api";
|
|
|
2
2
|
import type { GlobalCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
3
|
import type { PrinterService } from "@flowscripter/dynamic-cli-framework-api";
|
|
4
4
|
import { PRINTER_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
5
|
+
import { UPGRADE_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
6
|
+
import type { UpgradeService } from "@flowscripter/dynamic-cli-framework-api";
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Implementation of a {@link GlobalCommand} which outputs the version of the CLI.
|
|
@@ -13,6 +15,16 @@ export default class VersionCommand implements GlobalCommand {
|
|
|
13
15
|
|
|
14
16
|
public async execute(context: Context): Promise<void> {
|
|
15
17
|
const printerService = context.getServiceById(PRINTER_SERVICE_ID) as PrinterService;
|
|
16
|
-
|
|
18
|
+
|
|
19
|
+
let line = context.cliConfig.version;
|
|
20
|
+
if (context.doesServiceExist(UPGRADE_SERVICE_ID)) {
|
|
21
|
+
const upgradeService = context.getServiceById(UPGRADE_SERVICE_ID) as UpgradeService;
|
|
22
|
+
const result = await upgradeService.checkForUpgrade().catch(() => undefined);
|
|
23
|
+
if (result?.updateAvailable) {
|
|
24
|
+
line += ` (${result.latestVersion} available, run '${context.cliConfig.name} upgrade')`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await printerService.print(`${line}\n`);
|
|
17
29
|
}
|
|
18
30
|
}
|
|
@@ -7,6 +7,8 @@ import type ConfigurationServiceProvider from "../configuration/ConfigurationSer
|
|
|
7
7
|
import type { AsciiBannerGeneratorService } from "@flowscripter/dynamic-cli-framework-api";
|
|
8
8
|
import { ASCII_BANNER_GENERATOR_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
9
9
|
import type { CLIConfig } from "@flowscripter/dynamic-cli-framework-api";
|
|
10
|
+
import { UPGRADE_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
11
|
+
import type { UpgradeService } from "@flowscripter/dynamic-cli-framework-api";
|
|
10
12
|
export const BANNER_SERVICE_ID = "@flowscripter/dynamic-cli-framework/banner-service";
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -68,6 +70,17 @@ export default class BannerServiceProvider implements ServiceProvider {
|
|
|
68
70
|
if (cliConfig.version.length > 0) {
|
|
69
71
|
await printerService.info(` ${printerService.secondary("version: " + cliConfig.version)}\n`);
|
|
70
72
|
}
|
|
73
|
+
if (context.doesServiceExist(UPGRADE_SERVICE_ID)) {
|
|
74
|
+
const upgradeService = context.getServiceById(UPGRADE_SERVICE_ID) as UpgradeService;
|
|
75
|
+
const result = await upgradeService.checkForUpgrade().catch(() => undefined);
|
|
76
|
+
if (result?.updateAvailable) {
|
|
77
|
+
await printerService.info(
|
|
78
|
+
` ${printerService.secondary(
|
|
79
|
+
`(${result.latestVersion} available, run '${cliConfig.name} upgrade')`,
|
|
80
|
+
)}\n`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
71
84
|
if (this.#configurationServiceProvider) {
|
|
72
85
|
const configLocation = this.#configurationServiceProvider.configLocation;
|
|
73
86
|
if (configLocation && configLocation.length > 0) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import process from "node:process";
|
|
1
2
|
import type {
|
|
2
3
|
PrinterService,
|
|
3
4
|
ShutdownService,
|
|
@@ -11,6 +12,26 @@ const logger = getLogger("DefaultSpawnService");
|
|
|
11
12
|
|
|
12
13
|
const SHUTDOWN_GRACE_PERIOD_MS = 5000;
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* On Windows, batch-file shims (e.g. `npm.cmd`, `bun.cmd`) cannot be launched directly via
|
|
17
|
+
* `CreateProcess` - they must be run through `cmd.exe /c`. Resolve the command through the shell
|
|
18
|
+
* in that case; leave it untouched on other platforms.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveForPlatform(command: ReadonlyArray<string>): string[] {
|
|
21
|
+
if (process.platform !== "win32") {
|
|
22
|
+
return [...command];
|
|
23
|
+
}
|
|
24
|
+
const [bin, ...args] = command;
|
|
25
|
+
if (bin === undefined) {
|
|
26
|
+
return [...command];
|
|
27
|
+
}
|
|
28
|
+
const resolved = Bun.which(bin) ?? bin;
|
|
29
|
+
if (!/\.(cmd|bat)$/i.test(resolved)) {
|
|
30
|
+
return [resolved, ...args];
|
|
31
|
+
}
|
|
32
|
+
return ["cmd.exe", "/d", "/s", "/c", resolved, ...args];
|
|
33
|
+
}
|
|
34
|
+
|
|
14
35
|
export default class DefaultSpawnService implements SpawnService {
|
|
15
36
|
#printerService: PrinterService | undefined;
|
|
16
37
|
#shutdownService: ShutdownService | undefined;
|
|
@@ -68,8 +89,9 @@ export default class DefaultSpawnService implements SpawnService {
|
|
|
68
89
|
|
|
69
90
|
let proc;
|
|
70
91
|
try {
|
|
71
|
-
proc = Bun.spawn(
|
|
92
|
+
proc = Bun.spawn(resolveForPlatform(command), {
|
|
72
93
|
cwd: options.cwd,
|
|
94
|
+
stdin: stdio === "wrapped" ? "ignore" : "inherit",
|
|
73
95
|
stdout: stdio === "wrapped" ? "pipe" : "inherit",
|
|
74
96
|
stderr: stdio === "wrapped" ? "pipe" : "inherit",
|
|
75
97
|
});
|