@studiometa/forge-cli 0.4.5 → 0.4.6

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/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { S as isOutputFormat, _ as ApiError, a as createContext, b as ValidationError, c as colors, d as deleteToken, g as setToken, h as setOrganizationSlug, i as runCommand, l as setColorEnabled, m as getToken, n as exitWithValidationError, p as getOrganizationSlug, s as OutputFormatter, u as createConfigStore } from "./error-handler-peS_8nl4.js";
3
- import { activateCertificate, createAuditLogger, createBackupConfig, createCommand, createDatabaseUser, createMonitor, createNginxTemplate, createRedirectRule, createScheduledJob, createSecurityRule, deleteBackupConfig, deleteDatabaseUser, deleteMonitor, deleteNginxTemplate, deleteRedirectRule, deleteScheduledJob, deleteSecurityRule, deploySiteAndWait, getBackupConfig, getCertificate, getCommand, getDaemon, getDatabase, getDatabaseUser, getEnv, getFirewallRule, getMonitor, getNginxConfig, getNginxTemplate, getRecipe, getRedirectRule, getScheduledJob, getSecurityRule, getServer, getSite, getSshKey, getUser, listBackupConfigs, listCommands, listDaemons, listDatabaseUsers, listDatabases, listDeployments, listFirewallRules, listMonitors, listNginxTemplates, listRecipes, listRedirectRules, listScheduledJobs, listSecurityRules, listServers, listSites, listSshKeys, matchByName, rebootServer, restartDaemon, runRecipe, updateBackupConfig, updateDaemon, updateDatabaseUser, updateEnv, updateNginxConfig, updateNginxTemplate, updateRecipe, updateSecurityRule, updateSite } from "@studiometa/forge-core";
2
+ import { S as isOutputFormat, _ as ApiError, a as createContext, b as ValidationError, c as colors, d as deleteToken, g as setToken, h as setOrganizationSlug, i as runCommand, l as setColorEnabled, m as getToken, n as exitWithValidationError, p as getOrganizationSlug, s as OutputFormatter, u as createConfigStore } from "./error-handler-BA357jIr.js";
3
+ import { RESTARTABLE_SERVICES, activateCertificate, createAuditLogger, createBackupConfig, createCommand, createDatabaseUser, createMonitor, createNginxTemplate, createRedirectRule, createScheduledJob, createSecurityRule, deleteBackupConfig, deleteDatabaseUser, deleteMonitor, deleteNginxTemplate, deleteRedirectRule, deleteScheduledJob, deleteSecurityRule, deploySiteAndWait, getBackupConfig, getCertificate, getCommand, getDaemon, getDaemonLog, getDatabase, getDatabaseUser, getDeploymentLog, getEnv, getFirewallRule, getMonitor, getNginxConfig, getNginxTemplate, getRecipe, getRedirectRule, getScheduledJob, getSecurityRule, getServer, getSite, getSshKey, getUser, listBackupConfigs, listCommands, listDaemons, listDatabaseUsers, listDatabases, listDeployments, listFirewallRules, listMonitors, listNginxTemplates, listRecipes, listRedirectRules, listScheduledJobs, listSecurityRules, listServers, listServices, listSites, listSshKeys, matchByName, rebootServer, restartDaemon, restartService, runRecipe, updateBackupConfig, updateDaemon, updateDatabaseUser, updateEnv, updateNginxConfig, updateNginxTemplate, updateRecipe, updateSecurityRule, updateSite } from "@studiometa/forge-core";
4
4
  import { mkdirSync, writeFileSync } from "node:fs";
5
5
  import { homedir } from "node:os";
6
6
  import { join } from "node:path";
@@ -336,16 +336,16 @@ _forge_completions() {
336
336
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
337
337
 
338
338
  # Main commands
339
- commands="config servers s sites deployments d databases db database-users daemons env nginx certificates certs firewall-rules fw ssh ssh-keys backups commands scheduled-jobs user monitors nginx-templates security-rules redirect-rules recipes completion help"
339
+ commands="config servers s sites deployments d databases db database-users daemons env nginx certificates certs firewall-rules fw ssh ssh-keys backups commands scheduled-jobs services user monitors nginx-templates security-rules redirect-rules recipes completion help"
340
340
 
341
341
  # Subcommands for each command
342
342
  local config_cmds="set get delete"
343
343
  local servers_cmds="list ls get reboot"
344
344
  local sites_cmds="list ls get"
345
- local deployments_cmds="list ls deploy"
345
+ local deployments_cmds="list ls deploy logs log"
346
346
  local databases_cmds="list ls get"
347
347
  local database_users_cmds="list ls get create delete"
348
- local daemons_cmds="list ls get restart"
348
+ local daemons_cmds="list ls get restart logs log"
349
349
  local env_cmds="get update"
350
350
  local nginx_cmds="get update"
351
351
  local certificates_cmds="get activate"
@@ -354,6 +354,7 @@ _forge_completions() {
354
354
  local backups_cmds="list ls get create delete"
355
355
  local commands_cmds="list ls get create"
356
356
  local scheduled_jobs_cmds="list ls get create delete"
357
+ local services_cmds="list ls restart"
357
358
  local user_cmds="get"
358
359
  local monitors_cmds="list ls get create delete"
359
360
  local nginx_templates_cmds="list ls get create update delete"
@@ -422,6 +423,9 @@ _forge_completions() {
422
423
  scheduled-jobs)
423
424
  COMPREPLY=( $(compgen -W "\${scheduled_jobs_cmds}" -- "\${cur}") )
424
425
  ;;
426
+ services)
427
+ COMPREPLY=( $(compgen -W "\${services_cmds}" -- "\${cur}") )
428
+ ;;
425
429
  user)
426
430
  COMPREPLY=( $(compgen -W "\${user_cmds}" -- "\${cur}") )
427
431
  ;;
@@ -505,6 +509,7 @@ _forge() {
505
509
  'backups:Manage backup configurations'
506
510
  'commands:Manage site commands'
507
511
  'scheduled-jobs:Manage scheduled jobs'
512
+ 'services:List and restart server services'
508
513
  'user:Display authenticated user profile'
509
514
  'monitors:Manage server monitors'
510
515
  'nginx-templates:Manage Nginx templates'
@@ -552,6 +557,8 @@ _forge() {
552
557
  'list:List deployments'
553
558
  'ls:List deployments (alias)'
554
559
  'deploy:Trigger a deployment'
560
+ 'logs:Show deployment output'
561
+ 'log:Show deployment output (alias)'
555
562
  )
556
563
  _describe 'deployments command' deployments_cmds
557
564
  ;;
@@ -582,6 +589,8 @@ _forge() {
582
589
  'ls:List daemons (alias)'
583
590
  'get:Get daemon details'
584
591
  'restart:Restart a daemon'
592
+ 'logs:Show daemon log output'
593
+ 'log:Show daemon log output (alias)'
585
594
  )
586
595
  _describe 'daemons command' daemons_cmds
587
596
  ;;
@@ -659,6 +668,15 @@ _forge() {
659
668
  )
660
669
  _describe 'scheduled-jobs command' scheduled_jobs_cmds
661
670
  ;;
671
+ services)
672
+ local -a services_cmds
673
+ services_cmds=(
674
+ 'list:List services on a server'
675
+ 'ls:List services on a server (alias)'
676
+ 'restart:Restart a service'
677
+ )
678
+ _describe 'services command' services_cmds
679
+ ;;
662
680
  user)
663
681
  local -a user_cmds
664
682
  user_cmds=(
@@ -795,6 +813,7 @@ complete -c forge -f -n "__fish_use_subcommand" -a "ssh-keys" -d "Manage SSH key
795
813
  complete -c forge -f -n "__fish_use_subcommand" -a "backups" -d "Manage backup configurations"
796
814
  complete -c forge -f -n "__fish_use_subcommand" -a "commands" -d "Manage site commands"
797
815
  complete -c forge -f -n "__fish_use_subcommand" -a "scheduled-jobs" -d "Manage scheduled jobs"
816
+ complete -c forge -f -n "__fish_use_subcommand" -a "services" -d "List and restart server services"
798
817
  complete -c forge -f -n "__fish_use_subcommand" -a "user" -d "Display authenticated user profile"
799
818
  complete -c forge -f -n "__fish_use_subcommand" -a "monitors" -d "Manage server monitors"
800
819
  complete -c forge -f -n "__fish_use_subcommand" -a "nginx-templates" -d "Manage Nginx templates"
@@ -824,6 +843,8 @@ complete -c forge -f -n "__fish_seen_subcommand_from sites" -a "get" -d "Get sit
824
843
  complete -c forge -f -n "__fish_seen_subcommand_from deployments d" -a "list" -d "List deployments"
825
844
  complete -c forge -f -n "__fish_seen_subcommand_from deployments d" -a "ls" -d "List deployments (alias)"
826
845
  complete -c forge -f -n "__fish_seen_subcommand_from deployments d" -a "deploy" -d "Trigger a deployment"
846
+ complete -c forge -f -n "__fish_seen_subcommand_from deployments d" -a "logs" -d "Show deployment output"
847
+ complete -c forge -f -n "__fish_seen_subcommand_from deployments d" -a "log" -d "Show deployment output (alias)"
827
848
 
828
849
  # databases subcommands
829
850
  complete -c forge -f -n "__fish_seen_subcommand_from databases db" -a "list" -d "List databases"
@@ -842,6 +863,8 @@ complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "list" -d "List
842
863
  complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "ls" -d "List daemons (alias)"
843
864
  complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "get" -d "Get daemon details"
844
865
  complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "restart" -d "Restart a daemon"
866
+ complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "logs" -d "Show daemon log output"
867
+ complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "log" -d "Show daemon log output (alias)"
845
868
 
846
869
  # env subcommands
847
870
  complete -c forge -f -n "__fish_seen_subcommand_from env" -a "get" -d "Get env variables"
@@ -886,6 +909,11 @@ complete -c forge -f -n "__fish_seen_subcommand_from scheduled-jobs" -a "get" -d
886
909
  complete -c forge -f -n "__fish_seen_subcommand_from scheduled-jobs" -a "create" -d "Create a scheduled job"
887
910
  complete -c forge -f -n "__fish_seen_subcommand_from scheduled-jobs" -a "delete" -d "Delete a scheduled job"
888
911
 
912
+ # services subcommands
913
+ complete -c forge -f -n "__fish_seen_subcommand_from services" -a "list" -d "List services on a server"
914
+ complete -c forge -f -n "__fish_seen_subcommand_from services" -a "ls" -d "List services on a server (alias)"
915
+ complete -c forge -f -n "__fish_seen_subcommand_from services" -a "restart" -d "Restart a service"
916
+
889
917
  # user subcommands
890
918
  complete -c forge -f -n "__fish_seen_subcommand_from user" -a "get" -d "Get the authenticated user profile"
891
919
 
@@ -1432,6 +1460,22 @@ async function daemonsGet(args, ctx) {
1432
1460
  ]);
1433
1461
  }, ctx.formatter);
1434
1462
  }
1463
+ async function daemonsLogs(args, ctx) {
1464
+ const [id] = args;
1465
+ const server = String(ctx.options.server ?? "");
1466
+ if (!id) exitWithValidationError("daemon_id", "forge daemons logs <daemon_id> --server <server_id>", ctx.formatter);
1467
+ if (!server) exitWithValidationError("server_id", "forge daemons logs <daemon_id> --server <server_id>", ctx.formatter);
1468
+ await runCommand(async () => {
1469
+ const token = ctx.getToken();
1470
+ const execCtx = ctx.createExecutorContext(token);
1471
+ const result = await getDaemonLog({
1472
+ server_id: await resolveServerId(server, execCtx),
1473
+ id
1474
+ }, execCtx);
1475
+ if (result.data) ctx.formatter.outputText(result.data);
1476
+ else ctx.formatter.info("No log output available.");
1477
+ }, ctx.formatter);
1478
+ }
1435
1479
  async function daemonsUpdate(args, ctx) {
1436
1480
  const [id] = args;
1437
1481
  const server = String(ctx.options.server ?? "");
@@ -1483,6 +1527,8 @@ var handleDaemonsCommand = createCommandRouter({
1483
1527
  list: daemonsList,
1484
1528
  ls: daemonsList,
1485
1529
  get: [daemonsGet, "args"],
1530
+ logs: [daemonsLogs, "args"],
1531
+ log: [daemonsLogs, "args"],
1486
1532
  restart: [daemonsRestart, "args"],
1487
1533
  update: [daemonsUpdate, "args"]
1488
1534
  },
@@ -1502,6 +1548,12 @@ ${colors.bold("forge daemons get")} - Get daemon details
1502
1548
 
1503
1549
  ${colors.bold("USAGE:")}
1504
1550
  forge daemons get <daemon_id> --server <server_id>
1551
+ `);
1552
+ else if (subcommand === "logs" || subcommand === "log") console.log(`
1553
+ ${colors.bold("forge daemons logs")} - Show the log output of a daemon
1554
+
1555
+ ${colors.bold("USAGE:")}
1556
+ forge daemons logs <daemon_id> --server <server_id>
1505
1557
  `);
1506
1558
  else if (subcommand === "update") console.log(`
1507
1559
  ${colors.bold("forge daemons update")} - Update a daemon
@@ -1524,6 +1576,7 @@ ${colors.bold("USAGE:")}
1524
1576
  ${colors.bold("SUBCOMMANDS:")}
1525
1577
  list, ls List daemons
1526
1578
  get <id> Get daemon details
1579
+ logs <id> Show the log output of a daemon
1527
1580
  update <id> Update a daemon
1528
1581
  restart <id> Restart a daemon
1529
1582
 
@@ -1832,6 +1885,38 @@ async function deploymentsList(ctx) {
1832
1885
  ], "No deployments found.", (d) => `${String(d.id).padEnd(8)} ${d.status.padEnd(12)} ${(d.commit.hash ?? "—").padEnd(10)} ${d.started_at}`);
1833
1886
  }, ctx.formatter);
1834
1887
  }
1888
+ async function deploymentsLogs(args, ctx) {
1889
+ const [deploymentId] = args;
1890
+ const server = String(ctx.options.server ?? "");
1891
+ const site = String(ctx.options.site ?? "");
1892
+ if (!server) exitWithValidationError("server_id", "forge deployments logs [deployment_id] --server <server_id> --site <site_id>", ctx.formatter);
1893
+ if (!site) exitWithValidationError("site_id", "forge deployments logs [deployment_id] --server <server_id> --site <site_id>", ctx.formatter);
1894
+ await runCommand(async () => {
1895
+ const token = ctx.getToken();
1896
+ const execCtx = ctx.createExecutorContext(token);
1897
+ const server_id = await resolveServerId(server, execCtx);
1898
+ const site_id = await resolveSiteId(site, server_id, execCtx);
1899
+ let deployment_id = deploymentId;
1900
+ if (!deployment_id) {
1901
+ const deployments = await listDeployments({
1902
+ server_id,
1903
+ site_id
1904
+ }, execCtx);
1905
+ if (deployments.data.length === 0) {
1906
+ ctx.formatter.info("No deployments found.");
1907
+ return;
1908
+ }
1909
+ deployment_id = String(deployments.data[0].id);
1910
+ }
1911
+ const result = await getDeploymentLog({
1912
+ server_id,
1913
+ site_id,
1914
+ deployment_id
1915
+ }, execCtx);
1916
+ if (result.data) ctx.formatter.outputText(result.data);
1917
+ else ctx.formatter.info("No log output available.");
1918
+ }, ctx.formatter);
1919
+ }
1835
1920
  async function deploymentsDeploy(ctx) {
1836
1921
  const server = String(ctx.options.server ?? "");
1837
1922
  const site = String(ctx.options.site ?? "");
@@ -1868,7 +1953,9 @@ var handleDeploymentsCommand = createCommandRouter({
1868
1953
  handlers: {
1869
1954
  list: deploymentsList,
1870
1955
  ls: deploymentsList,
1871
- deploy: deploymentsDeploy
1956
+ deploy: deploymentsDeploy,
1957
+ logs: [deploymentsLogs, "args"],
1958
+ log: [deploymentsLogs, "args"]
1872
1959
  },
1873
1960
  writeSubcommands: ["deploy"]
1874
1961
  });
@@ -1881,6 +1968,17 @@ ${colors.bold("forge deployments list")} - List deployments for a site
1881
1968
  ${colors.bold("USAGE:")}
1882
1969
  forge deployments list --server <server_id> --site <site_id> [options]
1883
1970
 
1971
+ ${colors.bold("OPTIONS:")}
1972
+ --server <id> Server ID (required)
1973
+ --site <id> Site ID (required)
1974
+ -f, --format <fmt> Output format: json, human, table
1975
+ `);
1976
+ else if (subcommand === "logs" || subcommand === "log") console.log(`
1977
+ ${colors.bold("forge deployments logs")} - Show output of a deployment (latest if no id)
1978
+
1979
+ ${colors.bold("USAGE:")}
1980
+ forge deployments logs [deployment_id] --server <server_id> --site <site_id>
1981
+
1884
1982
  ${colors.bold("OPTIONS:")}
1885
1983
  --server <id> Server ID (required)
1886
1984
  --site <id> Site ID (required)
@@ -1910,6 +2008,7 @@ ${colors.bold("ALIASES:")}
1910
2008
  ${colors.bold("SUBCOMMANDS:")}
1911
2009
  list, ls List deployments for a site
1912
2010
  deploy Trigger a deployment
2011
+ logs Show output of a deployment (latest if no id)
1913
2012
 
1914
2013
  ${colors.bold("OPTIONS:")}
1915
2014
  --server <id> Server ID (required)
@@ -3321,6 +3420,122 @@ Run ${colors.cyan("forge servers <subcommand> --help")} for subcommand details.
3321
3420
  `);
3322
3421
  }
3323
3422
  //#endregion
3423
+ //#region src/commands/services/handlers.ts
3424
+ /**
3425
+ * List services on a server with a derived availability flag.
3426
+ */
3427
+ async function servicesList(args, ctx) {
3428
+ const [server] = args;
3429
+ if (!server) exitWithValidationError("server", "forge services list <server>", ctx.formatter);
3430
+ await runCommand(async () => {
3431
+ const token = ctx.getToken();
3432
+ const execCtx = ctx.createExecutorContext(token);
3433
+ const result = await listServices({ server_id: await resolveServerId(server, execCtx) }, execCtx);
3434
+ ctx.formatter.outputList(result.data, [
3435
+ "service",
3436
+ "available",
3437
+ "detail"
3438
+ ], "No services.", (row) => {
3439
+ const status = row.available ? "yes" : "no";
3440
+ const detail = row.detail ? ` (${row.detail})` : "";
3441
+ return `${row.service.padEnd(12)}${status}${detail}`;
3442
+ });
3443
+ }, ctx.formatter);
3444
+ }
3445
+ /**
3446
+ * Restart a service on a server.
3447
+ */
3448
+ async function servicesRestart(args, ctx) {
3449
+ const [server, service] = args;
3450
+ if (!server) exitWithValidationError("server", "forge services restart <server> <service>", ctx.formatter);
3451
+ if (!service) exitWithValidationError("service", "forge services restart <server> <service>", ctx.formatter);
3452
+ const version = ctx.options.version ? String(ctx.options.version) : void 0;
3453
+ await runCommand(async () => {
3454
+ if (!RESTARTABLE_SERVICES.includes(service)) throw new ValidationError(`Invalid service "${service}"`, "service", [`Valid services: ${RESTARTABLE_SERVICES.join(", ")}`]);
3455
+ const token = ctx.getToken();
3456
+ const execCtx = ctx.createExecutorContext(token);
3457
+ const server_id = await resolveServerId(server, execCtx);
3458
+ let resolvedVersion = version;
3459
+ if (service === "php" && !resolvedVersion) {
3460
+ const { data: srv } = await getServer({ server_id }, execCtx);
3461
+ if (!srv.php_version) throw new ValidationError("The php service requires a version", "version", ["Pass --version <php_version> (e.g. --version php83)"]);
3462
+ resolvedVersion = srv.php_version;
3463
+ }
3464
+ await restartService({
3465
+ server_id,
3466
+ service,
3467
+ version: resolvedVersion
3468
+ }, execCtx);
3469
+ ctx.formatter.success(`${service} restart initiated on server ${server_id}.`);
3470
+ }, ctx.formatter);
3471
+ }
3472
+ //#endregion
3473
+ //#region src/commands/services/command.ts
3474
+ var handleServicesCommand = createCommandRouter({
3475
+ resource: "services",
3476
+ handlers: {
3477
+ list: [servicesList, "args"],
3478
+ ls: [servicesList, "args"],
3479
+ restart: [servicesRestart, "args"]
3480
+ },
3481
+ writeSubcommands: ["restart"]
3482
+ });
3483
+ //#endregion
3484
+ //#region src/commands/services/help.ts
3485
+ function showServicesHelp(subcommand) {
3486
+ if (subcommand === "list" || subcommand === "ls") console.log(`
3487
+ ${colors.bold("forge services list")} - List services on a server
3488
+
3489
+ ${colors.bold("USAGE:")}
3490
+ forge services list <server> [options]
3491
+
3492
+ ${colors.bold("ARGUMENTS:")}
3493
+ <server> Server ID or name (required)
3494
+
3495
+ ${colors.bold("NOTES:")}
3496
+ Availability is a heuristic derived from the server object — the API has
3497
+ no services listing endpoint.
3498
+
3499
+ ${colors.bold("EXAMPLES:")}
3500
+ forge services list 123
3501
+ forge services list my-server --format json
3502
+ `);
3503
+ else if (subcommand === "restart") console.log(`
3504
+ ${colors.bold("forge services restart")} - Restart a service on a server
3505
+
3506
+ ${colors.bold("USAGE:")}
3507
+ forge services restart <server> <service> [options]
3508
+
3509
+ ${colors.bold("ARGUMENTS:")}
3510
+ <server> Server ID or name (required)
3511
+ <service> One of: nginx, php, mysql, postgres, redis, supervisor
3512
+
3513
+ ${colors.bold("OPTIONS:")}
3514
+ --version <ver> PHP version (e.g. php83) — required for the php service
3515
+ (defaults to the server's php_version when omitted)
3516
+
3517
+ ${colors.bold("EXAMPLES:")}
3518
+ forge services restart 123 nginx
3519
+ forge services restart 123 php --version php83
3520
+ `);
3521
+ else console.log(`
3522
+ ${colors.bold("forge services")} - List and restart server services
3523
+
3524
+ ${colors.bold("USAGE:")}
3525
+ forge services <subcommand> [options]
3526
+
3527
+ ${colors.bold("SUBCOMMANDS:")}
3528
+ list, ls List services on a server
3529
+ restart <service> Restart a service (nginx, php, mysql, postgres, redis, supervisor)
3530
+
3531
+ ${colors.bold("OPTIONS:")}
3532
+ --version <ver> PHP version (for the php service)
3533
+ -f, --format <fmt> Output format: json, human, table
3534
+
3535
+ Run ${colors.cyan("forge services <subcommand> --help")} for details.
3536
+ `);
3537
+ }
3538
+ //#endregion
3324
3539
  //#region src/commands/sites/handlers.ts
3325
3540
  async function sitesList(ctx) {
3326
3541
  const server = String(ctx.options.server ?? "");
@@ -3784,7 +3999,7 @@ function parseArgs(argv = process.argv.slice(2)) {
3784
3999
  }
3785
4000
  //#endregion
3786
4001
  //#region src/cli.ts
3787
- var VERSION = "0.4.5";
4002
+ var VERSION = "0.4.6";
3788
4003
  function showHelp() {
3789
4004
  console.log(`
3790
4005
  ${colors.bold("forge")} v${VERSION}
@@ -3867,6 +4082,10 @@ ${colors.bold("COMMANDS:")}
3867
4082
  create Create a scheduled job (requires --server --command)
3868
4083
  delete <id> Delete a scheduled job (requires --server)
3869
4084
 
4085
+ services List and restart server services
4086
+ list, ls List services on a server (forge services list <server>)
4087
+ restart <service> Restart a service (forge services restart <server> <service>)
4088
+
3870
4089
  user Display authenticated user profile
3871
4090
  get Get the authenticated user profile
3872
4091
 
@@ -3955,7 +4174,7 @@ async function main() {
3955
4174
  showHelp();
3956
4175
  process.exit(0);
3957
4176
  }
3958
- if (options.version || options.v) {
4177
+ if (options.version === true || options.v === true) {
3959
4178
  console.log(VERSION);
3960
4179
  process.exit(0);
3961
4180
  }
@@ -4083,6 +4302,13 @@ async function main() {
4083
4302
  }
4084
4303
  await handleScheduledJobsCommand(subcommand ?? "list", positional, options);
4085
4304
  break;
4305
+ case "services":
4306
+ if (wantsHelp) {
4307
+ showServicesHelp(subcommand);
4308
+ process.exit(0);
4309
+ }
4310
+ await handleServicesCommand(subcommand ?? "list", positional, options);
4311
+ break;
4086
4312
  case "user":
4087
4313
  if (wantsHelp) {
4088
4314
  showUserHelp(subcommand);