@flowscripter/dynamic-cli-framework 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/index.d.ts +5 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +4 -1
  4. package/dist/src/cli/BaseCLI.d.ts +3 -0
  5. package/dist/src/cli/BaseCLI.d.ts.map +1 -1
  6. package/dist/src/cli/BaseCLI.js +14 -0
  7. package/dist/src/cli/BaseCLIFeatureOptions.d.ts +4 -0
  8. package/dist/src/cli/BaseCLIFeatureOptions.d.ts.map +1 -1
  9. package/dist/src/command/VersionCommand.d.ts.map +1 -1
  10. package/dist/src/command/VersionCommand.js +10 -1
  11. package/dist/src/service/banner/BannerServiceProvider.d.ts.map +1 -1
  12. package/dist/src/service/banner/BannerServiceProvider.js +8 -0
  13. package/dist/src/service/plugin/DefaultPluginService.d.ts +1 -1
  14. package/dist/src/service/plugin/DefaultPluginService.d.ts.map +1 -1
  15. package/dist/src/service/plugin/DefaultPluginServiceProvider.d.ts +3 -2
  16. package/dist/src/service/plugin/DefaultPluginServiceProvider.d.ts.map +1 -1
  17. package/dist/src/service/plugin/DefaultPluginServiceProvider.js +22 -2
  18. package/dist/src/service/plugin/SpawnInterfaceAdapter.d.ts +19 -0
  19. package/dist/src/service/plugin/SpawnInterfaceAdapter.d.ts.map +1 -0
  20. package/dist/src/service/plugin/SpawnInterfaceAdapter.js +40 -0
  21. package/dist/src/service/spawn/DefaultSpawnService.d.ts +7 -0
  22. package/dist/src/service/spawn/DefaultSpawnService.d.ts.map +1 -0
  23. package/dist/src/service/spawn/DefaultSpawnService.js +95 -0
  24. package/dist/src/service/spawn/SpawnServiceProvider.d.ts +12 -0
  25. package/dist/src/service/spawn/SpawnServiceProvider.d.ts.map +1 -0
  26. package/dist/src/service/spawn/SpawnServiceProvider.js +24 -0
  27. package/dist/src/service/upgrade/DefaultUpgradeService.d.ts +14 -0
  28. package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -0
  29. package/dist/src/service/upgrade/DefaultUpgradeService.js +312 -0
  30. package/dist/src/service/upgrade/UpgradeLocationsConfig.d.ts +51 -0
  31. package/dist/src/service/upgrade/UpgradeLocationsConfig.d.ts.map +1 -0
  32. package/dist/src/service/upgrade/UpgradeLocationsConfig.js +0 -0
  33. package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts +13 -0
  34. package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts.map +1 -0
  35. package/dist/src/service/upgrade/UpgradeServiceProvider.js +120 -0
  36. package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts +16 -0
  37. package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts.map +1 -0
  38. package/dist/src/service/upgrade/command/UpgradeSubCommand.js +50 -0
  39. package/index.ts +10 -1
  40. package/package.json +5 -3
  41. package/src/cli/BaseCLI.ts +16 -0
  42. package/src/cli/BaseCLIFeatureOptions.ts +5 -0
  43. package/src/command/VersionCommand.ts +13 -1
  44. package/src/service/banner/BannerServiceProvider.ts +13 -0
  45. package/src/service/plugin/DefaultPluginService.ts +1 -1
  46. package/src/service/plugin/DefaultPluginServiceProvider.ts +39 -3
  47. package/src/service/plugin/SpawnInterfaceAdapter.ts +62 -0
  48. package/src/service/spawn/DefaultSpawnService.ts +120 -0
  49. package/src/service/spawn/SpawnServiceProvider.ts +32 -0
  50. package/src/service/upgrade/DefaultUpgradeService.ts +379 -0
  51. package/src/service/upgrade/UpgradeLocationsConfig.ts +53 -0
  52. package/src/service/upgrade/UpgradeServiceProvider.ts +150 -0
  53. package/src/service/upgrade/command/UpgradeSubCommand.ts +85 -0
@@ -51,6 +51,8 @@ import ArgumentPrompterServiceProvider from "../service/argumentPrompter/Argumen
51
51
  import DefaultCompletionService from "../service/completion/DefaultCompletionService.ts";
52
52
  import CompletionServiceProvider from "../service/completion/CompletionServiceProvider.ts";
53
53
  import ImagePrinterServiceProvider from "../service/imagePrinter/ImagePrinterServiceProvider.ts";
54
+ import SpawnServiceProvider from "../service/spawn/SpawnServiceProvider.ts";
55
+ import UpgradeServiceProvider from "../service/upgrade/UpgradeServiceProvider.ts";
54
56
  const logger = getLogger("BaseCLI");
55
57
 
56
58
  /**
@@ -69,6 +71,9 @@ const logger = getLogger("BaseCLI");
69
71
  * Optionally enabled via {@link BaseCLIFeatureOptions}:
70
72
  *
71
73
  * * {@link ImagePrinterServiceProvider}
74
+ * * {@link SpawnServiceProvider}
75
+ * * {@link CompletionServiceProvider}
76
+ * * {@link UpgradeServiceProvider}
72
77
  *
73
78
  * `keyReader` is optional. If omitted, or if the stderr {@link Terminal} is not a TTY, prompting is
74
79
  * unavailable: {@link PrompterServiceProvider} (and {@link ArgumentPrompterServiceProvider}, if
@@ -130,6 +135,9 @@ export default class BaseCLI implements CLI {
130
135
  argumentPrompterServiceEnabled: false,
131
136
  completionServiceEnabled: false,
132
137
  imagePrinterServiceEnabled: false,
138
+ spawnServiceEnabled: false,
139
+ upgradeServiceEnabled: false,
140
+ upgradeLocationsConfig: { supportedPlatforms: [] },
133
141
  validateAllCommands: false,
134
142
  promptingEnabled: true,
135
143
  ...options,
@@ -262,6 +270,10 @@ export default class BaseCLI implements CLI {
262
270
  }
263
271
  }
264
272
 
273
+ if (this.#options.spawnServiceEnabled) {
274
+ this.addServiceProvider(new SpawnServiceProvider(58));
275
+ }
276
+
265
277
  if (this.#options.completionServiceEnabled) {
266
278
  const completionService = new DefaultCompletionService();
267
279
  this.addServiceProvider(
@@ -269,6 +281,10 @@ export default class BaseCLI implements CLI {
269
281
  );
270
282
  }
271
283
 
284
+ if (this.#options.upgradeServiceEnabled) {
285
+ this.addServiceProvider(new UpgradeServiceProvider(6, this.#options.upgradeLocationsConfig));
286
+ }
287
+
272
288
  const configurationServiceProvider = new ConfigurationServiceProvider(
273
289
  90,
274
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;
@@ -6,6 +8,9 @@ export default interface BaseCLIFeatureOptions {
6
8
  readonly argumentPrompterServiceEnabled?: boolean;
7
9
  readonly completionServiceEnabled?: boolean;
8
10
  readonly imagePrinterServiceEnabled?: boolean;
11
+ readonly spawnServiceEnabled?: boolean;
12
+ readonly upgradeServiceEnabled?: boolean;
13
+ readonly upgradeLocationsConfig?: UpgradeLocationsConfig;
9
14
  readonly validateAllCommands?: boolean;
10
15
  readonly promptingEnabled?: boolean;
11
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
- await printerService.print(`${context.cliConfig.version}\n`);
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,8 +1,8 @@
1
1
  import type { PluginService } from "@flowscripter/dynamic-cli-framework-api";
2
2
  import type {
3
3
  MarketplacePluginManager,
4
- VersionedPluginDescriptor,
5
4
  SearchQuery,
5
+ VersionedPluginDescriptor,
6
6
  } from "@flowscripter/dynamic-plugin-framework";
7
7
 
8
8
  export default class DefaultPluginService implements PluginService {
@@ -1,20 +1,37 @@
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 type { MarketplacePluginManager } from "@flowscripter/dynamic-plugin-framework";
4
+ import type { PrinterService, SpawnService } from "@flowscripter/dynamic-cli-framework-api";
5
+ import { PRINTER_SERVICE_ID, SPAWN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
6
+ import type {
7
+ MarketplacePluginManager,
8
+ SpawnCapable,
9
+ } from "@flowscripter/dynamic-plugin-framework";
5
10
  import { PLUGIN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
6
11
  import DefaultPluginService from "./DefaultPluginService.ts";
7
12
  import { PluginGroupCommand } from "./command/PluginGroupCommand.ts";
13
+ import SpawnInterfaceAdapter, {
14
+ type SpawnInterfaceAdapterOptions,
15
+ } from "./SpawnInterfaceAdapter.ts";
16
+ import getLogger from "../../util/logger.ts";
17
+
18
+ const logger = getLogger("DefaultPluginServiceProvider");
8
19
 
9
20
  export default class DefaultPluginServiceProvider implements ServiceProvider {
10
21
  readonly serviceId = PLUGIN_SERVICE_ID;
11
22
  readonly servicePriority: number;
12
23
  readonly #pluginManager: MarketplacePluginManager;
24
+ readonly #spawnInterfaceAdapterOptions: SpawnInterfaceAdapterOptions;
13
25
  #pluginService: DefaultPluginService | undefined;
14
26
 
15
- constructor(pluginManager: MarketplacePluginManager, servicePriority = 50) {
27
+ constructor(
28
+ pluginManager: MarketplacePluginManager,
29
+ servicePriority = 50,
30
+ spawnInterfaceAdapterOptions: SpawnInterfaceAdapterOptions = {},
31
+ ) {
16
32
  this.#pluginManager = pluginManager;
17
33
  this.servicePriority = servicePriority;
34
+ this.#spawnInterfaceAdapterOptions = spawnInterfaceAdapterOptions;
18
35
  }
19
36
 
20
37
  async getServiceInfo(_cliConfig: CLIConfig): Promise<ServiceInfo> {
@@ -25,5 +42,24 @@ export default class DefaultPluginServiceProvider implements ServiceProvider {
25
42
  };
26
43
  }
27
44
 
28
- async initService(_context: Context): Promise<void> {}
45
+ initService(context: Context): Promise<void> {
46
+ if (!context.doesServiceExist(SPAWN_SERVICE_ID)) {
47
+ logger.debug("SpawnService not available, plugin manager will spawn processes directly");
48
+ return Promise.resolve();
49
+ }
50
+ if (!("setSpawn" in this.#pluginManager)) {
51
+ logger.debug("Plugin manager does not support SpawnCapable, skipping spawn injection");
52
+ return Promise.resolve();
53
+ }
54
+
55
+ const spawnService = context.getServiceById(SPAWN_SERVICE_ID) as SpawnService;
56
+ const printerService = context.getServiceById(PRINTER_SERVICE_ID) as PrinterService;
57
+ const adapter = new SpawnInterfaceAdapter(
58
+ spawnService,
59
+ printerService,
60
+ this.#spawnInterfaceAdapterOptions,
61
+ );
62
+ (this.#pluginManager as unknown as SpawnCapable).setSpawn(adapter);
63
+ return Promise.resolve();
64
+ }
29
65
  }
@@ -0,0 +1,62 @@
1
+ import type { PrinterService, SpawnService } from "@flowscripter/dynamic-cli-framework-api";
2
+ import type { SpawnInterface, SpawnResult } from "@flowscripter/dynamic-plugin-framework";
3
+
4
+ export interface SpawnInterfaceAdapterOptions {
5
+ quoteColor?: string;
6
+ markMinimumDisplayTimeMs?: number;
7
+ }
8
+
9
+ /**
10
+ * Adapts a {@link SpawnService} to dynamic-plugin-framework's {@link SpawnInterface}, wrapping
11
+ * spawned process output in a quoted, marked block via {@link PrinterService} that is cleared
12
+ * again once the process exits (subject to {@link SpawnInterfaceAdapterOptions.markMinimumDisplayTimeMs}).
13
+ */
14
+ export default class SpawnInterfaceAdapter implements SpawnInterface {
15
+ readonly #spawnService: SpawnService;
16
+ readonly #printerService: PrinterService;
17
+ readonly #quoteColor: string | undefined;
18
+ readonly #markMinimumDisplayTimeMs: number;
19
+
20
+ public constructor(
21
+ spawnService: SpawnService,
22
+ printerService: PrinterService,
23
+ options: SpawnInterfaceAdapterOptions = {},
24
+ ) {
25
+ this.#spawnService = spawnService;
26
+ this.#printerService = printerService;
27
+ this.#quoteColor = options.quoteColor;
28
+ this.#markMinimumDisplayTimeMs = options.markMinimumDisplayTimeMs ?? 1000;
29
+ }
30
+
31
+ public async spawn(
32
+ command: ReadonlyArray<string>,
33
+ options: { cwd: string },
34
+ ): Promise<SpawnResult> {
35
+ this.#printerService.startQuote(this.#quoteColor);
36
+ this.#printerService.startMark();
37
+
38
+ // onOutput is synchronous and may be called concurrently for stdout/stderr lines, but
39
+ // printerService.info() is async and must not be invoked concurrently with itself - queue
40
+ // writes so they're applied one at a time, in call order.
41
+ let writeQueue: Promise<void> = Promise.resolve();
42
+ const onOutput = (line: string): void => {
43
+ writeQueue = writeQueue.then(() => this.#printerService.info(`${line}\n`));
44
+ };
45
+
46
+ const result = await this.#spawnService.spawn(command, {
47
+ cwd: options.cwd,
48
+ stdio: "wrapped",
49
+ onOutput,
50
+ });
51
+
52
+ await writeQueue;
53
+
54
+ this.#printerService.endQuote();
55
+ this.#printerService.endMark();
56
+ await this.#printerService.clearMarked(this.#markMinimumDisplayTimeMs);
57
+
58
+ return result.ok
59
+ ? { ok: true, exitCode: result.exitCode }
60
+ : { ok: false, exitCode: result.exitCode, error: result.error };
61
+ }
62
+ }
@@ -0,0 +1,120 @@
1
+ import type {
2
+ PrinterService,
3
+ ShutdownService,
4
+ SpawnOptions,
5
+ SpawnResult,
6
+ SpawnService,
7
+ } from "@flowscripter/dynamic-cli-framework-api";
8
+ import getLogger from "../../util/logger.ts";
9
+
10
+ const logger = getLogger("DefaultSpawnService");
11
+
12
+ const SHUTDOWN_GRACE_PERIOD_MS = 5000;
13
+
14
+ export default class DefaultSpawnService implements SpawnService {
15
+ #printerService: PrinterService | undefined;
16
+ #shutdownService: ShutdownService | undefined;
17
+
18
+ public setDependencies(printerService: PrinterService, shutdownService: ShutdownService): void {
19
+ this.#printerService = printerService;
20
+ this.#shutdownService = shutdownService;
21
+ }
22
+
23
+ async #pipeLines(
24
+ stream: ReadableStream<Uint8Array> | number | undefined,
25
+ streamName: "stdout" | "stderr",
26
+ onOutput: (line: string, stream: "stdout" | "stderr") => void,
27
+ ): Promise<void> {
28
+ if (typeof stream !== "object" || stream === null) {
29
+ return;
30
+ }
31
+ const decoder = new TextDecoder();
32
+ const reader = stream.getReader();
33
+ let buffer = "";
34
+ for (;;) {
35
+ const { done, value } = await reader.read();
36
+ if (done) {
37
+ break;
38
+ }
39
+ buffer += decoder.decode(value, { stream: true });
40
+ let newlineIndex = buffer.indexOf("\n");
41
+ while (newlineIndex !== -1) {
42
+ onOutput(buffer.slice(0, newlineIndex), streamName);
43
+ buffer = buffer.slice(newlineIndex + 1);
44
+ newlineIndex = buffer.indexOf("\n");
45
+ }
46
+ }
47
+ if (buffer.length > 0) {
48
+ onOutput(buffer, streamName);
49
+ }
50
+ }
51
+
52
+ public async spawn(
53
+ command: ReadonlyArray<string>,
54
+ options: SpawnOptions = {},
55
+ ): Promise<SpawnResult> {
56
+ if (this.#printerService === undefined || this.#shutdownService === undefined) {
57
+ throw new Error("DefaultSpawnService.spawn() called before setDependencies()");
58
+ }
59
+ const printerService = this.#printerService;
60
+ const shutdownService = this.#shutdownService;
61
+ const stdio = options.stdio ?? "inherit";
62
+ const longRunning = options.longRunning ?? true;
63
+
64
+ if (stdio === "inherit") {
65
+ await printerService.hideSpinner();
66
+ await printerService.hideAllProgressBars();
67
+ }
68
+
69
+ let proc;
70
+ try {
71
+ proc = Bun.spawn([...command], {
72
+ cwd: options.cwd,
73
+ stdin: stdio === "wrapped" ? "ignore" : "inherit",
74
+ stdout: stdio === "wrapped" ? "pipe" : "inherit",
75
+ stderr: stdio === "wrapped" ? "pipe" : "inherit",
76
+ });
77
+ } catch (error) {
78
+ logger.debug(() => `Failed to launch command '${command.join(" ")}': ${error}`);
79
+ return { ok: false, error: error as Error };
80
+ }
81
+
82
+ let settled = false;
83
+
84
+ if (longRunning) {
85
+ shutdownService.enterLongRunningMode();
86
+ }
87
+
88
+ shutdownService.addShutdownListener(async () => {
89
+ if (settled) {
90
+ return;
91
+ }
92
+ logger.debug(() => `Sending SIGTERM to spawned command '${command.join(" ")}'`);
93
+ proc.kill("SIGTERM");
94
+ await Promise.race([
95
+ proc.exited.then(() => {}),
96
+ new Promise<void>((resolve) => setTimeout(resolve, SHUTDOWN_GRACE_PERIOD_MS)),
97
+ ]);
98
+ if (!settled) {
99
+ logger.debug(() => `Sending SIGKILL to spawned command '${command.join(" ")}'`);
100
+ proc.kill("SIGKILL");
101
+ }
102
+ });
103
+
104
+ if (stdio === "wrapped" && options.onOutput) {
105
+ const onOutput = options.onOutput;
106
+ void this.#pipeLines(proc.stdout, "stdout", onOutput);
107
+ void this.#pipeLines(proc.stderr, "stderr", onOutput);
108
+ }
109
+
110
+ try {
111
+ const exitCode = await proc.exited;
112
+ settled = true;
113
+ return exitCode === 0 ? { ok: true, exitCode } : { ok: false, exitCode };
114
+ } finally {
115
+ if (longRunning) {
116
+ shutdownService.leaveLongRunningMode();
117
+ }
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,32 @@
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 { PrinterService, ShutdownService } from "@flowscripter/dynamic-cli-framework-api";
5
+ import { PRINTER_SERVICE_ID, SHUTDOWN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
6
+ import { SPAWN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
7
+ import DefaultSpawnService from "./DefaultSpawnService.ts";
8
+
9
+ export default class SpawnServiceProvider implements ServiceProvider {
10
+ readonly serviceId: string = SPAWN_SERVICE_ID;
11
+ readonly servicePriority: number;
12
+ #spawnService: DefaultSpawnService | undefined;
13
+
14
+ public constructor(servicePriority: number) {
15
+ this.servicePriority = servicePriority;
16
+ }
17
+
18
+ public getServiceInfo(_cliConfig: CLIConfig): Promise<ServiceInfo> {
19
+ this.#spawnService = new DefaultSpawnService();
20
+ return Promise.resolve({
21
+ service: this.#spawnService,
22
+ commands: [],
23
+ });
24
+ }
25
+
26
+ public initService(context: Context): Promise<void> {
27
+ const printerService = context.getServiceById(PRINTER_SERVICE_ID) as PrinterService;
28
+ const shutdownService = context.getServiceById(SHUTDOWN_SERVICE_ID) as ShutdownService;
29
+ this.#spawnService!.setDependencies(printerService, shutdownService);
30
+ return Promise.resolve();
31
+ }
32
+ }