@flowscripter/dynamic-cli-framework 3.0.2 → 5.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/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/src/cli/BaseCLI.d.ts +1 -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 +4 -0
- package/dist/src/cli/BaseCLIFeatureOptions.d.ts.map +1 -1
- package/dist/src/command/VersionCommand.js +2 -2
- package/dist/src/launcher.d.ts +0 -2
- package/dist/src/launcher.d.ts.map +1 -1
- package/dist/src/launcher.js +0 -20
- package/dist/src/service/banner/BannerServiceProvider.js +2 -2
- package/dist/src/service/completion/CompletionServiceProvider.js +4 -4
- package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts +19 -8
- package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts.map +1 -1
- package/dist/src/service/configuration/ConfigurationServiceProvider.js +26 -33
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts +6 -6
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts.map +1 -1
- package/dist/src/service/configuration/DefaultKeyValueService.js +60 -19
- package/dist/src/service/configuration/resolveSecrets.d.ts +11 -0
- package/dist/src/service/configuration/resolveSecrets.d.ts.map +1 -0
- package/dist/src/service/configuration/resolveSecrets.js +31 -0
- package/dist/src/service/plugin/DefaultPluginService.d.ts +5 -2
- package/dist/src/service/plugin/DefaultPluginService.d.ts.map +1 -1
- package/dist/src/service/plugin/DefaultPluginService.js +21 -2
- package/dist/src/service/plugin/PluginServiceProvider.d.ts +17 -0
- package/dist/src/service/plugin/PluginServiceProvider.d.ts.map +1 -0
- package/dist/src/service/plugin/PluginServiceProvider.js +95 -0
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts +2 -2
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -1
- package/dist/src/service/upgrade/DefaultUpgradeService.js +79 -43
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts.map +1 -1
- package/dist/src/service/upgrade/UpgradeServiceProvider.js +9 -5
- package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts.map +1 -1
- package/dist/src/service/upgrade/command/UpgradeSubCommand.js +10 -1
- package/index.ts +2 -7
- package/package.json +3 -3
- package/src/cli/BaseCLI.ts +17 -0
- package/src/cli/BaseCLIFeatureOptions.ts +7 -0
- package/src/command/VersionCommand.ts +2 -2
- package/src/launcher.ts +0 -35
- package/src/service/banner/BannerServiceProvider.ts +2 -2
- package/src/service/completion/CompletionServiceProvider.ts +4 -4
- package/src/service/configuration/ConfigurationServiceProvider.ts +36 -40
- package/src/service/configuration/DefaultKeyValueService.ts +76 -22
- package/src/service/configuration/resolveSecrets.ts +35 -0
- package/src/service/plugin/DefaultPluginService.ts +41 -3
- package/src/service/plugin/PluginServiceProvider.ts +142 -0
- package/src/service/upgrade/DefaultUpgradeService.ts +98 -52
- package/src/service/upgrade/UpgradeServiceProvider.ts +9 -5
- package/src/service/upgrade/command/UpgradeSubCommand.ts +15 -1
- package/dist/src/cli/DynamicPluginRuntimeCLI.d.ts +0 -20
- package/dist/src/cli/DynamicPluginRuntimeCLI.d.ts.map +0 -1
- package/dist/src/cli/DynamicPluginRuntimeCLI.js +0 -50
- package/dist/src/service/plugin/DefaultPluginServiceProvider.d.ts +0 -15
- package/dist/src/service/plugin/DefaultPluginServiceProvider.d.ts.map +0 -1
- package/dist/src/service/plugin/DefaultPluginServiceProvider.js +0 -56
- package/src/cli/DynamicPluginRuntimeCLI.ts +0 -77
- package/src/service/plugin/DefaultPluginServiceProvider.ts +0 -82
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultPluginService.d.ts","sourceRoot":"","sources":["../../../../src/service/plugin/DefaultPluginService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EACV,wBAAwB,EACxB,WAAW,EACX,yBAAyB,EAC1B,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"DefaultPluginService.d.ts","sourceRoot":"","sources":["../../../../src/service/plugin/DefaultPluginService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EACV,wBAAwB,EACxB,WAAW,EACX,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EAC1B,MAAM,wCAAwC,CAAC;AAOhD,MAAM,CAAC,OAAO,OAAO,oBAAqB,YAAW,aAAa;;gBAKpD,YAAY,EAAE,2BAA2B,EAAE,WAAW,EAAE,yBAAyB;IAS7F,IAAI,aAAa,IAAI,wBAAwB,CAE5C;IAEK,sBAAsB,CAAC,eAAe,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB7E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC;IAIlF,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,yBAAyB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,aAAa,IAAI,aAAa,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC;IAInE,eAAe,IAAI,aAAa,CAAC;QAC/B,UAAU,EAAE,QAAQ,CAAC,yBAAyB,CAAC,CAAC;QAChD,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CAGH"}
|
|
@@ -1,7 +1,26 @@
|
|
|
1
|
+
import { NpmPluginManager, NpmjsPluginRepository, NpmPluginRepository, } from "@flowscripter/dynamic-plugin-framework";
|
|
1
2
|
export default class DefaultPluginService {
|
|
3
|
+
#defaultRemoteConfig;
|
|
4
|
+
#defaultLocalConfig;
|
|
2
5
|
#pluginManager;
|
|
3
|
-
constructor(
|
|
4
|
-
this.#
|
|
6
|
+
constructor(remoteConfig, localConfig) {
|
|
7
|
+
this.#defaultRemoteConfig = remoteConfig;
|
|
8
|
+
this.#defaultLocalConfig = localConfig;
|
|
9
|
+
this.#pluginManager = new NpmPluginManager([new NpmjsPluginRepository(remoteConfig)], new NpmPluginRepository(localConfig));
|
|
10
|
+
}
|
|
11
|
+
get pluginManager() {
|
|
12
|
+
return this.#pluginManager;
|
|
13
|
+
}
|
|
14
|
+
async applyKeyValueOverrides(keyValueService) {
|
|
15
|
+
let remotesConfig = [this.#defaultRemoteConfig];
|
|
16
|
+
if (await keyValueService.has("remotes-config")) {
|
|
17
|
+
remotesConfig = (await keyValueService.get("remotes-config"));
|
|
18
|
+
}
|
|
19
|
+
let localConfig = this.#defaultLocalConfig;
|
|
20
|
+
if (await keyValueService.has("local-config")) {
|
|
21
|
+
localConfig = (await keyValueService.get("local-config"));
|
|
22
|
+
}
|
|
23
|
+
this.#pluginManager = new NpmPluginManager(remotesConfig.map((config) => new NpmjsPluginRepository(config)), new NpmPluginRepository(localConfig));
|
|
5
24
|
}
|
|
6
25
|
search(query) {
|
|
7
26
|
return this.#pluginManager.search(query);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ServiceInfo, ServiceProvider } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
+
import type { Context } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
|
+
import type { CLIConfig } from "@flowscripter/dynamic-cli-framework-api";
|
|
4
|
+
import type { NpmjsPluginRepositoryConfig, NpmPluginRepositoryConfig } from "@flowscripter/dynamic-plugin-framework";
|
|
5
|
+
import { type SpawnInterfaceAdapterOptions } from "./SpawnInterfaceAdapter.ts";
|
|
6
|
+
import { type FetchInterfaceAdapterOptions } from "./FetchInterfaceAdapter.ts";
|
|
7
|
+
import DefaultCommandRegistry from "../../runtime/registry/DefaultCommandRegistry.ts";
|
|
8
|
+
import DefaultServiceProviderRegistry from "../../runtime/registry/DefaultServiceProviderRegistry.ts";
|
|
9
|
+
export default class PluginServiceProvider implements ServiceProvider {
|
|
10
|
+
#private;
|
|
11
|
+
readonly serviceId = "PLUGIN_SERVICE";
|
|
12
|
+
readonly servicePriority: number;
|
|
13
|
+
constructor(servicePriority: number, remoteConfig: NpmjsPluginRepositoryConfig, localConfig: NpmPluginRepositoryConfig, commandRegistry: DefaultCommandRegistry, serviceProviderRegistry: DefaultServiceProviderRegistry, spawnInterfaceAdapterOptions?: SpawnInterfaceAdapterOptions, fetchInterfaceAdapterOptions?: FetchInterfaceAdapterOptions);
|
|
14
|
+
getServiceInfo(cliConfig: CLIConfig): Promise<ServiceInfo>;
|
|
15
|
+
initService(context: Context): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=PluginServiceProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/plugin/PluginServiceProvider.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;AAWzE,OAAO,KAAK,EAEV,2BAA2B,EAC3B,yBAAyB,EAE1B,MAAM,wCAAwC,CAAC;AAIhD,OAA8B,EAC5B,KAAK,4BAA4B,EAClC,MAAM,4BAA4B,CAAC;AACpC,OAA8B,EAC5B,KAAK,4BAA4B,EAClC,MAAM,4BAA4B,CAAC;AACpC,OAAO,sBAAsB,MAAM,kDAAkD,CAAC;AACtF,OAAO,8BAA8B,MAAM,0DAA0D,CAAC;AAMtG,MAAM,CAAC,OAAO,OAAO,qBAAsB,YAAW,eAAe;;IACnE,QAAQ,CAAC,SAAS,oBAAqB;IACvC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;gBAW/B,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,2BAA2B,EACzC,WAAW,EAAE,yBAAyB,EACtC,eAAe,EAAE,sBAAsB,EACvC,uBAAuB,EAAE,8BAA8B,EACvD,4BAA4B,GAAE,4BAAiC,EAC/D,4BAA4B,GAAE,4BAAiC;IAW3D,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAS1D,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAmEnD"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { PRINTER_SERVICE_ID, SPAWN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
+
import { FETCH_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
|
+
import { KEY_VALUE_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
4
|
+
import { DYNAMIC_CLI_FRAMEWORK_COMMAND_FACTORY_EXTENSION_POINT } from "@flowscripter/dynamic-cli-framework-api";
|
|
5
|
+
import { DYNAMIC_CLI_FRAMEWORK_SERVICE_PROVIDER_FACTORY_EXTENSION_POINT } from "@flowscripter/dynamic-cli-framework-api";
|
|
6
|
+
import { PLUGIN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
7
|
+
import DefaultPluginService from "./DefaultPluginService.js";
|
|
8
|
+
import { PluginGroupCommand } from "./command/PluginGroupCommand.js";
|
|
9
|
+
import SpawnInterfaceAdapter, {} from "./SpawnInterfaceAdapter.js";
|
|
10
|
+
import FetchInterfaceAdapter, {} from "./FetchInterfaceAdapter.js";
|
|
11
|
+
import DefaultCommandRegistry from "../../runtime/registry/DefaultCommandRegistry.js";
|
|
12
|
+
import DefaultServiceProviderRegistry from "../../runtime/registry/DefaultServiceProviderRegistry.js";
|
|
13
|
+
import getLogger from "../../util/logger.js";
|
|
14
|
+
const logger = getLogger("PluginServiceProvider");
|
|
15
|
+
export default class PluginServiceProvider {
|
|
16
|
+
serviceId = PLUGIN_SERVICE_ID;
|
|
17
|
+
servicePriority;
|
|
18
|
+
#remoteConfig;
|
|
19
|
+
#localConfig;
|
|
20
|
+
#commandRegistry;
|
|
21
|
+
#serviceProviderRegistry;
|
|
22
|
+
#spawnInterfaceAdapterOptions;
|
|
23
|
+
#fetchInterfaceAdapterOptions;
|
|
24
|
+
#pluginService;
|
|
25
|
+
#cliConfig;
|
|
26
|
+
constructor(servicePriority, remoteConfig, localConfig, commandRegistry, serviceProviderRegistry, spawnInterfaceAdapterOptions = {}, fetchInterfaceAdapterOptions = {}) {
|
|
27
|
+
this.servicePriority = servicePriority;
|
|
28
|
+
this.#remoteConfig = remoteConfig;
|
|
29
|
+
this.#localConfig = localConfig;
|
|
30
|
+
this.#commandRegistry = commandRegistry;
|
|
31
|
+
this.#serviceProviderRegistry = serviceProviderRegistry;
|
|
32
|
+
this.#spawnInterfaceAdapterOptions = spawnInterfaceAdapterOptions;
|
|
33
|
+
this.#fetchInterfaceAdapterOptions = fetchInterfaceAdapterOptions;
|
|
34
|
+
}
|
|
35
|
+
async getServiceInfo(cliConfig) {
|
|
36
|
+
this.#cliConfig = cliConfig;
|
|
37
|
+
this.#pluginService = new DefaultPluginService(this.#remoteConfig, this.#localConfig);
|
|
38
|
+
return {
|
|
39
|
+
service: this.#pluginService,
|
|
40
|
+
commands: [new PluginGroupCommand()],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async initService(context) {
|
|
44
|
+
const pluginService = this.#pluginService;
|
|
45
|
+
if (context.doesServiceExist(KEY_VALUE_SERVICE_ID)) {
|
|
46
|
+
const keyValueService = context.getServiceById(KEY_VALUE_SERVICE_ID);
|
|
47
|
+
await pluginService.applyKeyValueOverrides(keyValueService);
|
|
48
|
+
}
|
|
49
|
+
const pluginManager = pluginService.pluginManager;
|
|
50
|
+
if (!context.doesServiceExist(SPAWN_SERVICE_ID)) {
|
|
51
|
+
logger.debug("SpawnService not available, plugin manager will spawn processes directly");
|
|
52
|
+
}
|
|
53
|
+
else if (!("setSpawn" in pluginManager)) {
|
|
54
|
+
logger.debug("Plugin manager does not support SpawnCapable, skipping spawn injection");
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const spawnService = context.getServiceById(SPAWN_SERVICE_ID);
|
|
58
|
+
const printerService = context.getServiceById(PRINTER_SERVICE_ID);
|
|
59
|
+
const adapter = new SpawnInterfaceAdapter(spawnService, printerService, this.#spawnInterfaceAdapterOptions);
|
|
60
|
+
pluginManager.setSpawn(adapter);
|
|
61
|
+
}
|
|
62
|
+
if (!context.doesServiceExist(FETCH_SERVICE_ID)) {
|
|
63
|
+
logger.debug("FetchService not available, plugin manager will fetch directly");
|
|
64
|
+
}
|
|
65
|
+
else if (!("setFetch" in pluginManager)) {
|
|
66
|
+
logger.debug("Plugin manager does not support FetchCapable, skipping fetch injection");
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const fetchService = context.getServiceById(FETCH_SERVICE_ID);
|
|
70
|
+
const adapter = new FetchInterfaceAdapter(fetchService, this.#fetchInterfaceAdapterOptions);
|
|
71
|
+
pluginManager.setFetch(adapter);
|
|
72
|
+
}
|
|
73
|
+
await pluginManager.registerExtensions(DYNAMIC_CLI_FRAMEWORK_COMMAND_FACTORY_EXTENSION_POINT);
|
|
74
|
+
await pluginManager.registerExtensions(DYNAMIC_CLI_FRAMEWORK_SERVICE_PROVIDER_FACTORY_EXTENSION_POINT);
|
|
75
|
+
const commandFactoryExtensions = await pluginManager.getRegisteredExtensions(DYNAMIC_CLI_FRAMEWORK_COMMAND_FACTORY_EXTENSION_POINT);
|
|
76
|
+
for (const ext of commandFactoryExtensions) {
|
|
77
|
+
const factory = (await pluginManager.instantiate(ext.extensionHandle));
|
|
78
|
+
for (const command of factory.getCommands()) {
|
|
79
|
+
this.#commandRegistry.addCommand(command);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const spFactoryExtensions = await pluginManager.getRegisteredExtensions(DYNAMIC_CLI_FRAMEWORK_SERVICE_PROVIDER_FACTORY_EXTENSION_POINT);
|
|
83
|
+
for (const ext of spFactoryExtensions) {
|
|
84
|
+
const factory = (await pluginManager.instantiate(ext.extensionHandle));
|
|
85
|
+
for (const sp of factory.getServiceProviders()) {
|
|
86
|
+
const info = await sp.getServiceInfo(this.#cliConfig);
|
|
87
|
+
if (info.service) {
|
|
88
|
+
context.addServiceInstance(sp.serviceId, info.service);
|
|
89
|
+
}
|
|
90
|
+
info.commands.forEach((cmd) => this.#commandRegistry.addCommand(cmd, sp.serviceId));
|
|
91
|
+
this.#serviceProviderRegistry.addServiceProvider(sp);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -6,11 +6,11 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
6
6
|
#private;
|
|
7
7
|
constructor(config: UpgradeLocationsConfig, cliConfig: CLIConfig);
|
|
8
8
|
setDependencies(spawnService: SpawnService | undefined, fetchService: FetchService | undefined): void;
|
|
9
|
-
getUpgradeCheckResult(waitForResult?: boolean): Promise<UpgradeCheckResult
|
|
9
|
+
getUpgradeCheckResult(waitForResult?: boolean): Promise<UpgradeCheckResult>;
|
|
10
10
|
detectOs(): SupportedOs | undefined;
|
|
11
11
|
detectArch(): SupportedArch | undefined;
|
|
12
12
|
detectInstallMethod(os: SupportedOs): Promise<InstallMethod | undefined>;
|
|
13
|
-
checkForUpgrade(osOverride?: SupportedOs, archOverride?: SupportedArch, installMethodOverride?: InstallMethod): Promise<UpgradeCheckResult
|
|
13
|
+
checkForUpgrade(osOverride?: SupportedOs, archOverride?: SupportedArch, installMethodOverride?: InstallMethod): Promise<UpgradeCheckResult>;
|
|
14
14
|
upgrade(osOverride?: SupportedOs, archOverride?: SupportedArch, installMethodOverride?: InstallMethod): Promise<UpgradeResult>;
|
|
15
15
|
}
|
|
16
16
|
//# sourceMappingURL=DefaultUpgradeService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultUpgradeService.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/DefaultUpgradeService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EAEZ,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACd,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,KAAK,cAAc,EACpB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"DefaultUpgradeService.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/DefaultUpgradeService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EAEZ,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACd,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,KAAK,cAAc,EACpB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAM1E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAkB5C,MAAM,CAAC,OAAO,OAAO,qBAAsB,YAAW,cAAc;;gBAO/C,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE,SAAS;IAKhE,eAAe,CACpB,YAAY,EAAE,YAAY,GAAG,SAAS,EACtC,YAAY,EAAE,YAAY,GAAG,SAAS,GACrC,IAAI;IAKA,qBAAqB,CAAC,aAAa,UAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA0BzE,QAAQ,IAAI,WAAW,GAAG,SAAS;IAanC,UAAU,IAAI,aAAa,GAAG,SAAS;IAWjC,mBAAmB,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAgBxE,eAAe,CAC1B,UAAU,CAAC,EAAE,WAAW,EACxB,YAAY,CAAC,EAAE,aAAa,EAC5B,qBAAqB,CAAC,EAAE,aAAa,GACpC,OAAO,CAAC,kBAAkB,CAAC;IAwCjB,OAAO,CAClB,UAAU,CAAC,EAAE,WAAW,EACxB,YAAY,CAAC,EAAE,aAAa,EAC5B,qBAAqB,CAAC,EAAE,aAAa,GACpC,OAAO,CAAC,aAAa,CAAC;CA4S1B"}
|
|
@@ -4,8 +4,6 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { InstallMethod, SupportedArch, SupportedOs, } from "@flowscripter/dynamic-cli-framework-api";
|
|
6
6
|
import semver from "semver";
|
|
7
|
-
import getLogger from "../../util/logger.js";
|
|
8
|
-
const logger = getLogger("DefaultUpgradeService");
|
|
9
7
|
// checkForUpgrade() runs opportunistically on every CLI invocation (via BannerServiceProvider),
|
|
10
8
|
// so that opportunistic call is raced against this timeout so it never stalls CLI startup.
|
|
11
9
|
// A deliberate caller (waitForResult=true, e.g. the `upgrade` command) awaits checkForUpgrade()
|
|
@@ -37,14 +35,14 @@ export default class DefaultUpgradeService {
|
|
|
37
35
|
}
|
|
38
36
|
getUpgradeCheckResult(waitForResult = false) {
|
|
39
37
|
if (!this.#upgradeCheckPromise) {
|
|
40
|
-
// Only cache a
|
|
41
|
-
//
|
|
42
|
-
// before checkForUpgrade() itself resolves) - caching that would
|
|
43
|
-
// later, deliberate caller (e.g. the `upgrade` command explicitly
|
|
44
|
-
// waitForResult=true) any chance of a fresh attempt for the rest of this
|
|
45
|
-
// lifetime.
|
|
38
|
+
// Only cache a non-"failed" result. A "failed" result can come from a transient issue
|
|
39
|
+
// (e.g. a network blip, or the opportunistic startup check racing past
|
|
40
|
+
// VERSION_CHECK_TIMEOUT_MS before checkForUpgrade() itself resolves) - caching that would
|
|
41
|
+
// permanently deny a later, deliberate caller (e.g. the `upgrade` command explicitly
|
|
42
|
+
// waiting via waitForResult=true) any chance of a fresh attempt for the rest of this
|
|
43
|
+
// process's lifetime.
|
|
46
44
|
this.#upgradeCheckPromise = this.checkForUpgrade().then((result) => {
|
|
47
|
-
if (result ===
|
|
45
|
+
if (result.status === "failed") {
|
|
48
46
|
this.#upgradeCheckPromise = undefined;
|
|
49
47
|
}
|
|
50
48
|
return result;
|
|
@@ -55,7 +53,7 @@ export default class DefaultUpgradeService {
|
|
|
55
53
|
}
|
|
56
54
|
return Promise.race([
|
|
57
55
|
this.#upgradeCheckPromise,
|
|
58
|
-
new Promise((resolve) => setTimeout(() => resolve(
|
|
56
|
+
new Promise((resolve) => setTimeout(() => resolve({ status: "pending" }), VERSION_CHECK_TIMEOUT_MS)),
|
|
59
57
|
]);
|
|
60
58
|
}
|
|
61
59
|
detectOs() {
|
|
@@ -99,26 +97,29 @@ export default class DefaultUpgradeService {
|
|
|
99
97
|
const os = osOverride ?? this.detectOs();
|
|
100
98
|
const arch = archOverride ?? this.detectArch();
|
|
101
99
|
if (!os || !arch || !this.#isPlatformSupported(os, arch)) {
|
|
102
|
-
return
|
|
100
|
+
return { status: "unsupported" };
|
|
103
101
|
}
|
|
104
102
|
const installMethod = installMethodOverride ?? (await this.detectInstallMethod(os));
|
|
105
103
|
if (!installMethod) {
|
|
106
|
-
return
|
|
104
|
+
return { status: "unsupported" };
|
|
107
105
|
}
|
|
108
|
-
const
|
|
109
|
-
if (!
|
|
110
|
-
return
|
|
106
|
+
const versionLookup = await this.#getLatestVersion(installMethod);
|
|
107
|
+
if (!versionLookup.ok) {
|
|
108
|
+
return { status: "failed", error: versionLookup.error };
|
|
111
109
|
}
|
|
112
110
|
const currentVersion = this.#cliConfig.version;
|
|
113
111
|
const coercedCurrent = semver.coerce(currentVersion);
|
|
114
|
-
const coercedLatest = semver.coerce(
|
|
112
|
+
const coercedLatest = semver.coerce(versionLookup.version);
|
|
115
113
|
if (!coercedCurrent || !coercedLatest) {
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
return {
|
|
115
|
+
status: "failed",
|
|
116
|
+
error: new Error(`Unable to compare versions '${currentVersion}' and '${versionLookup.version}'`),
|
|
117
|
+
};
|
|
118
118
|
}
|
|
119
119
|
return {
|
|
120
|
+
status: "checked",
|
|
120
121
|
currentVersion,
|
|
121
|
-
latestVersion,
|
|
122
|
+
latestVersion: versionLookup.version,
|
|
122
123
|
updateAvailable: semver.gt(coercedLatest, coercedCurrent),
|
|
123
124
|
os,
|
|
124
125
|
arch,
|
|
@@ -131,13 +132,20 @@ export default class DefaultUpgradeService {
|
|
|
131
132
|
const checkResult = hasOverride
|
|
132
133
|
? await this.checkForUpgrade(osOverride, archOverride, installMethodOverride)
|
|
133
134
|
: await this.getUpgradeCheckResult(true);
|
|
134
|
-
if (
|
|
135
|
+
if (checkResult.status === "unsupported") {
|
|
135
136
|
return {
|
|
136
137
|
ok: false,
|
|
137
138
|
oldVersion,
|
|
138
139
|
error: new Error("No upgrade location configured for the detected or requested platform"),
|
|
139
140
|
};
|
|
140
141
|
}
|
|
142
|
+
if (checkResult.status === "failed") {
|
|
143
|
+
return { ok: false, oldVersion, error: checkResult.error };
|
|
144
|
+
}
|
|
145
|
+
if (checkResult.status === "pending") {
|
|
146
|
+
// Unreachable: checkForUpgrade() and getUpgradeCheckResult(true) always run to completion.
|
|
147
|
+
return { ok: false, oldVersion, error: new Error("Upgrade check did not complete") };
|
|
148
|
+
}
|
|
141
149
|
if (!this.#spawnService) {
|
|
142
150
|
return { ok: false, oldVersion, error: new Error("SpawnService is not available") };
|
|
143
151
|
}
|
|
@@ -194,53 +202,81 @@ export default class DefaultUpgradeService {
|
|
|
194
202
|
return this.#getLatestHomebrewVersion();
|
|
195
203
|
case InstallMethod.WINGET:
|
|
196
204
|
return this.#getLatestWingetVersion();
|
|
197
|
-
default:
|
|
198
|
-
return undefined;
|
|
199
205
|
}
|
|
200
206
|
}
|
|
201
207
|
async #getLatestGithubReleaseVersion() {
|
|
202
|
-
if (!this.#config.githubRelease
|
|
203
|
-
return
|
|
208
|
+
if (!this.#config.githubRelease) {
|
|
209
|
+
return { ok: false, error: new Error("No githubRelease location configured") };
|
|
210
|
+
}
|
|
211
|
+
if (!this.#fetchService) {
|
|
212
|
+
return { ok: false, error: new Error("FetchService is not available") };
|
|
204
213
|
}
|
|
205
214
|
const { owner, repo } = this.#config.githubRelease;
|
|
206
215
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
216
|
+
// Uses the plain web redirect rather than the api.github.com REST endpoint, since the
|
|
217
|
+
// latter's unauthenticated rate limit (60 requests/hour/IP) is easily exhausted, e.g. by
|
|
218
|
+
// CI runners sharing an IP pool.
|
|
219
|
+
const response = await this.#fetchService.fetch(`https://github.com/${owner}/${repo}/releases/latest`, { redirect: "manual" });
|
|
220
|
+
const location = response.headers.get("location");
|
|
221
|
+
const version = location ? /\/releases\/tag\/v?([^/]+)$/.exec(location)?.[1] : undefined;
|
|
222
|
+
if (!version) {
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
error: new Error(`Unexpected response resolving latest release for ${owner}/${repo}: HTTP ${response.status}`),
|
|
226
|
+
};
|
|
210
227
|
}
|
|
211
|
-
|
|
212
|
-
return data.tag_name?.replace(/^v/, "");
|
|
228
|
+
return { ok: true, version };
|
|
213
229
|
}
|
|
214
230
|
catch (error) {
|
|
215
|
-
|
|
216
|
-
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
error: new Error(`Failed to fetch latest GitHub release for ${owner}/${repo}: ${error}`),
|
|
234
|
+
};
|
|
217
235
|
}
|
|
218
236
|
}
|
|
219
237
|
async #getLatestHomebrewVersion() {
|
|
220
|
-
if (!this.#config.homebrew
|
|
221
|
-
return
|
|
238
|
+
if (!this.#config.homebrew) {
|
|
239
|
+
return { ok: false, error: new Error("No homebrew location configured") };
|
|
240
|
+
}
|
|
241
|
+
if (!this.#fetchService) {
|
|
242
|
+
return { ok: false, error: new Error("FetchService is not available") };
|
|
222
243
|
}
|
|
223
244
|
const { tap, formula } = this.#config.homebrew;
|
|
224
245
|
const [tapOwner, tapName] = tap.split("/");
|
|
225
246
|
if (!tapOwner || !tapName) {
|
|
226
|
-
return
|
|
247
|
+
return { ok: false, error: new Error(`Invalid homebrew tap '${tap}'`) };
|
|
227
248
|
}
|
|
228
249
|
try {
|
|
229
250
|
const response = await this.#fetchService.fetch(`https://raw.githubusercontent.com/${tapOwner}/homebrew-${tapName}/main/${formula}.rb`);
|
|
230
251
|
if (!response.ok) {
|
|
231
|
-
return
|
|
252
|
+
return {
|
|
253
|
+
ok: false,
|
|
254
|
+
error: new Error(`Failed to fetch homebrew formula for ${tap}/${formula}: HTTP ${response.status}`),
|
|
255
|
+
};
|
|
232
256
|
}
|
|
233
257
|
const text = await response.text();
|
|
234
|
-
|
|
258
|
+
const version = /version\s+"v?([^"]+)"/.exec(text)?.[1];
|
|
259
|
+
if (!version) {
|
|
260
|
+
return {
|
|
261
|
+
ok: false,
|
|
262
|
+
error: new Error(`Could not parse version from homebrew formula ${tap}/${formula}`),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return { ok: true, version };
|
|
235
266
|
}
|
|
236
267
|
catch (error) {
|
|
237
|
-
|
|
238
|
-
|
|
268
|
+
return {
|
|
269
|
+
ok: false,
|
|
270
|
+
error: new Error(`Failed to fetch homebrew formula for ${tap}/${formula}: ${error}`),
|
|
271
|
+
};
|
|
239
272
|
}
|
|
240
273
|
}
|
|
241
274
|
async #getLatestWingetVersion() {
|
|
242
|
-
if (!this.#
|
|
243
|
-
return
|
|
275
|
+
if (!this.#config.winget) {
|
|
276
|
+
return { ok: false, error: new Error("No winget location configured") };
|
|
277
|
+
}
|
|
278
|
+
if (!this.#spawnService) {
|
|
279
|
+
return { ok: false, error: new Error("SpawnService is not available") };
|
|
244
280
|
}
|
|
245
281
|
const lines = [];
|
|
246
282
|
const result = await this.#spawnService.spawn(["winget", "show", "--id", this.#config.winget.packageId], {
|
|
@@ -249,15 +285,15 @@ export default class DefaultUpgradeService {
|
|
|
249
285
|
onOutput: (line) => lines.push(line),
|
|
250
286
|
});
|
|
251
287
|
if (!result.ok) {
|
|
252
|
-
return
|
|
288
|
+
return { ok: false, error: new Error(`winget show failed: ${describeSpawnFailure(result)}`) };
|
|
253
289
|
}
|
|
254
290
|
for (const line of lines) {
|
|
255
291
|
const match = /Version:\s*(\S+)/.exec(line);
|
|
256
292
|
if (match?.[1]) {
|
|
257
|
-
return match[1];
|
|
293
|
+
return { ok: true, version: match[1] };
|
|
258
294
|
}
|
|
259
295
|
}
|
|
260
|
-
return
|
|
296
|
+
return { ok: false, error: new Error("Could not parse version from winget output") };
|
|
261
297
|
}
|
|
262
298
|
async #upgradeViaLinuxScript() {
|
|
263
299
|
const { scriptUrl } = this.#config.linuxScript;
|
|
@@ -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;gBAKd,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB;IAKnE,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IASpD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,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;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;gBAKd,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB;IAKnE,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IASpD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CA0H1D"}
|
|
@@ -52,8 +52,8 @@ export default class UpgradeServiceProvider {
|
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
const keyValueService = context.getServiceById(KEY_VALUE_SERVICE_ID);
|
|
55
|
-
if (await keyValueService.
|
|
56
|
-
const status = await keyValueService.
|
|
55
|
+
if (await keyValueService.has("upgrade-status")) {
|
|
56
|
+
const status = await keyValueService.get("upgrade-status");
|
|
57
57
|
if (status === "declined") {
|
|
58
58
|
logger.debug(() => "Auto-upgrade previously declined, skipping");
|
|
59
59
|
return;
|
|
@@ -97,16 +97,20 @@ export default class UpgradeServiceProvider {
|
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
if (enableResult.value !== true) {
|
|
100
|
-
await keyValueService.
|
|
100
|
+
await keyValueService.set("upgrade-status", "declined");
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
|
-
await keyValueService.
|
|
103
|
+
await keyValueService.set("upgrade-status", "enabled");
|
|
104
104
|
await this.#checkAndUpgrade(context, upgradeService, cliConfig);
|
|
105
105
|
}
|
|
106
106
|
async #checkAndUpgrade(context, upgradeService, cliConfig) {
|
|
107
107
|
try {
|
|
108
108
|
const checkResult = await upgradeService.getUpgradeCheckResult();
|
|
109
|
-
if (
|
|
109
|
+
if (checkResult.status === "failed") {
|
|
110
|
+
logger.debug(() => `Auto-upgrade check failed: ${checkResult.error.message}`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (checkResult.status !== "checked" || !checkResult.updateAvailable) {
|
|
110
114
|
return;
|
|
111
115
|
}
|
|
112
116
|
const upgradeResult = await upgradeService.upgrade();
|
|
@@ -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;gBAIiB,cAAc,EAAE,qBAAqB;IAI3C,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,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;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;gBAIiB,cAAc,EAAE,qBAAqB;IAI3C,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAuD9E"}
|
|
@@ -34,10 +34,19 @@ export class UpgradeSubCommand {
|
|
|
34
34
|
const checkResult = os === undefined && installMethod === undefined
|
|
35
35
|
? await this.#upgradeService.getUpgradeCheckResult(true)
|
|
36
36
|
: await this.#upgradeService.checkForUpgrade(os, arch, installMethod);
|
|
37
|
-
if (
|
|
37
|
+
if (checkResult.status === "unsupported") {
|
|
38
38
|
await printerService.error(`No upgrade location is configured for the detected or requested platform.\n`, Icon.FAILURE);
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
+
if (checkResult.status === "failed") {
|
|
42
|
+
await printerService.error(`Failed to check for updates: ${checkResult.error.message}. Please try again later.\n`, Icon.FAILURE);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (checkResult.status === "pending") {
|
|
46
|
+
// Unreachable: getUpgradeCheckResult(true) and checkForUpgrade() always run to completion.
|
|
47
|
+
await printerService.error(`Failed to check for updates.\n`, Icon.FAILURE);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
41
50
|
if (!checkResult.updateAvailable) {
|
|
42
51
|
await printerService.print(`${cliName} is already up to date (${checkResult.currentVersion}).\n`);
|
|
43
52
|
return;
|
package/index.ts
CHANGED
|
@@ -187,12 +187,11 @@ export type { ServiceProviderFactory } from "@flowscripter/dynamic-cli-framework
|
|
|
187
187
|
export { DYNAMIC_CLI_FRAMEWORK_SERVICE_PROVIDER_FACTORY_EXTENSION_POINT } from "@flowscripter/dynamic-cli-framework-api";
|
|
188
188
|
export { PLUGIN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
189
189
|
export type { PluginService } from "@flowscripter/dynamic-cli-framework-api";
|
|
190
|
-
export { default as
|
|
190
|
+
export { default as PluginServiceProvider } from "./src/service/plugin/PluginServiceProvider.ts";
|
|
191
191
|
|
|
192
192
|
// Core CLI
|
|
193
193
|
export { default as BaseCLI } from "./src/cli/BaseCLI.ts";
|
|
194
194
|
export { default as DefaultRuntimeCLI } from "./src/cli/DefaultRuntimeCLI.ts";
|
|
195
|
-
export { default as DynamicPluginRuntimeCLI } from "./src/cli/DynamicPluginRuntimeCLI.ts";
|
|
196
195
|
|
|
197
196
|
// Terminal API
|
|
198
197
|
export type { default as Terminal } from "./src/terminal/Terminal.ts";
|
|
@@ -202,8 +201,4 @@ export { SpecialKey } from "./src/terminal/KeyReader.ts";
|
|
|
202
201
|
export { default as TtyKeyReader } from "./src/terminal/TtyKeyReader.ts";
|
|
203
202
|
|
|
204
203
|
// Convenience functions
|
|
205
|
-
export {
|
|
206
|
-
launchDynamicPluginMultiCommandCLI,
|
|
207
|
-
launchMultiCommandCLI,
|
|
208
|
-
launchSingleCommandCLI,
|
|
209
|
-
} from "./src/launcher.ts";
|
|
204
|
+
export { launchMultiCommandCLI, launchSingleCommandCLI } from "./src/launcher.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowscripter/dynamic-cli-framework",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
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,8 +48,8 @@
|
|
|
48
48
|
"build": "tsc -p tsconfig.build.json"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@flowscripter/dynamic-cli-framework-api": "^
|
|
52
|
-
"@flowscripter/dynamic-plugin-framework": "
|
|
51
|
+
"@flowscripter/dynamic-cli-framework-api": "^2.0.0",
|
|
52
|
+
"@flowscripter/dynamic-plugin-framework": "2.0.0",
|
|
53
53
|
"emphasize": "^7.0.0",
|
|
54
54
|
"fastest-levenshtein": "^1.0.16",
|
|
55
55
|
"figlet": "^1.11.0",
|
package/src/cli/BaseCLI.ts
CHANGED
|
@@ -54,6 +54,7 @@ import ImagePrinterServiceProvider from "../service/imagePrinter/ImagePrinterSer
|
|
|
54
54
|
import SpawnServiceProvider from "../service/spawn/SpawnServiceProvider.ts";
|
|
55
55
|
import FetchServiceProvider from "../service/fetch/FetchServiceProvider.ts";
|
|
56
56
|
import UpgradeServiceProvider from "../service/upgrade/UpgradeServiceProvider.ts";
|
|
57
|
+
import PluginServiceProvider from "../service/plugin/PluginServiceProvider.ts";
|
|
57
58
|
const logger = getLogger("BaseCLI");
|
|
58
59
|
|
|
59
60
|
/**
|
|
@@ -76,6 +77,7 @@ const logger = getLogger("BaseCLI");
|
|
|
76
77
|
* * {@link FetchServiceProvider}
|
|
77
78
|
* * {@link CompletionServiceProvider}
|
|
78
79
|
* * {@link UpgradeServiceProvider}
|
|
80
|
+
* * {@link PluginServiceProvider}
|
|
79
81
|
*
|
|
80
82
|
* `keyReader` is optional. If omitted, or if the stderr {@link Terminal} is not a TTY, prompting is
|
|
81
83
|
* unavailable: {@link PrompterServiceProvider} (and {@link ArgumentPrompterServiceProvider}, if
|
|
@@ -141,6 +143,9 @@ export default class BaseCLI implements CLI {
|
|
|
141
143
|
fetchServiceEnabled: false,
|
|
142
144
|
upgradeServiceEnabled: false,
|
|
143
145
|
upgradeLocationsConfig: { supportedPlatforms: [] },
|
|
146
|
+
pluginServiceEnabled: false,
|
|
147
|
+
pluginServiceRemoteConfig: { name: "", registryUrl: "", packageJsonNamespace: "" },
|
|
148
|
+
pluginServiceLocalConfig: { nodeModulesPath: "", packageJsonNamespace: "" },
|
|
144
149
|
validateAllCommands: false,
|
|
145
150
|
promptingEnabled: true,
|
|
146
151
|
...options,
|
|
@@ -292,6 +297,18 @@ export default class BaseCLI implements CLI {
|
|
|
292
297
|
this.addServiceProvider(new UpgradeServiceProvider(6, this.#options.upgradeLocationsConfig));
|
|
293
298
|
}
|
|
294
299
|
|
|
300
|
+
if (this.#options.pluginServiceEnabled) {
|
|
301
|
+
this.addServiceProvider(
|
|
302
|
+
new PluginServiceProvider(
|
|
303
|
+
50,
|
|
304
|
+
this.#options.pluginServiceRemoteConfig,
|
|
305
|
+
this.#options.pluginServiceLocalConfig,
|
|
306
|
+
this.#commandRegistry,
|
|
307
|
+
this.#serviceProviderRegistry,
|
|
308
|
+
),
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
295
312
|
const configurationServiceProvider = new ConfigurationServiceProvider(
|
|
296
313
|
90,
|
|
297
314
|
this.#options.envVarsSupportEnabled,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { UpgradeLocationsConfig } from "../service/upgrade/UpgradeLocationsConfig.ts";
|
|
2
|
+
import type {
|
|
3
|
+
NpmjsPluginRepositoryConfig,
|
|
4
|
+
NpmPluginRepositoryConfig,
|
|
5
|
+
} from "@flowscripter/dynamic-plugin-framework";
|
|
2
6
|
|
|
3
7
|
export default interface BaseCLIFeatureOptions {
|
|
4
8
|
readonly configFileSupportEnabled?: boolean;
|
|
@@ -12,6 +16,9 @@ export default interface BaseCLIFeatureOptions {
|
|
|
12
16
|
readonly fetchServiceEnabled?: boolean;
|
|
13
17
|
readonly upgradeServiceEnabled?: boolean;
|
|
14
18
|
readonly upgradeLocationsConfig?: UpgradeLocationsConfig;
|
|
19
|
+
readonly pluginServiceEnabled?: boolean;
|
|
20
|
+
readonly pluginServiceRemoteConfig?: NpmjsPluginRepositoryConfig;
|
|
21
|
+
readonly pluginServiceLocalConfig?: NpmPluginRepositoryConfig;
|
|
15
22
|
readonly validateAllCommands?: boolean;
|
|
16
23
|
readonly promptingEnabled?: boolean;
|
|
17
24
|
}
|
|
@@ -19,8 +19,8 @@ export default class VersionCommand implements GlobalCommand {
|
|
|
19
19
|
let line = context.cliConfig.version;
|
|
20
20
|
if (context.doesServiceExist(UPGRADE_SERVICE_ID)) {
|
|
21
21
|
const upgradeService = context.getServiceById(UPGRADE_SERVICE_ID) as UpgradeService;
|
|
22
|
-
const result = await upgradeService.getUpgradeCheckResult()
|
|
23
|
-
if (result
|
|
22
|
+
const result = await upgradeService.getUpgradeCheckResult();
|
|
23
|
+
if (result.status === "checked" && result.updateAvailable) {
|
|
24
24
|
line += ` (${result.latestVersion} available, run '${context.cliConfig.name} upgrade')`;
|
|
25
25
|
}
|
|
26
26
|
}
|
package/src/launcher.ts
CHANGED
|
@@ -9,8 +9,6 @@ import type {
|
|
|
9
9
|
} from "@flowscripter/dynamic-cli-framework-api";
|
|
10
10
|
import type BaseCLIFeatureOptions from "./cli/BaseCLIFeatureOptions.ts";
|
|
11
11
|
import DefaultRuntimeCLI from "./cli/DefaultRuntimeCLI.ts";
|
|
12
|
-
import DynamicPluginRuntimeCLI from "./cli/DynamicPluginRuntimeCLI.ts";
|
|
13
|
-
import type { MarketplacePluginManager } from "@flowscripter/dynamic-plugin-framework";
|
|
14
12
|
|
|
15
13
|
export async function launchSingleCommandCLI(
|
|
16
14
|
command: SubCommand,
|
|
@@ -75,36 +73,3 @@ export async function launchMultiCommandCLI(
|
|
|
75
73
|
|
|
76
74
|
return await cli.run();
|
|
77
75
|
}
|
|
78
|
-
|
|
79
|
-
export async function launchDynamicPluginMultiCommandCLI(
|
|
80
|
-
pluginManager: MarketplacePluginManager,
|
|
81
|
-
commands: ReadonlyArray<Command>,
|
|
82
|
-
description?: string,
|
|
83
|
-
name?: string,
|
|
84
|
-
version?: string,
|
|
85
|
-
serviceProviders?: ReadonlyArray<ServiceProvider>,
|
|
86
|
-
options?: BaseCLIFeatureOptions,
|
|
87
|
-
): Promise<RunResult> {
|
|
88
|
-
if (!name) {
|
|
89
|
-
name = path.basename(process.execPath);
|
|
90
|
-
}
|
|
91
|
-
const cliConfig: CLIConfig = {
|
|
92
|
-
description,
|
|
93
|
-
name,
|
|
94
|
-
version: version || "N/A",
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const mergedOptions: BaseCLIFeatureOptions = {
|
|
98
|
-
...options,
|
|
99
|
-
validateAllCommands:
|
|
100
|
-
(options?.validateAllCommands ?? false) ||
|
|
101
|
-
process.env.DYNAMIC_CLI_FRAMEWORK_VALIDATE_ALL !== undefined,
|
|
102
|
-
};
|
|
103
|
-
const cli = new DynamicPluginRuntimeCLI(cliConfig, pluginManager, mergedOptions);
|
|
104
|
-
|
|
105
|
-
commands.forEach((command) => cli.addCommand(command));
|
|
106
|
-
|
|
107
|
-
serviceProviders?.forEach((service) => cli.addServiceProvider(service));
|
|
108
|
-
|
|
109
|
-
return await cli.run();
|
|
110
|
-
}
|