@studiometa/forge-cli 0.4.4 → 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.
Files changed (37) hide show
  1. package/README.md +14 -0
  2. package/dist/cli.js +576 -34
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/completion.d.ts.map +1 -1
  5. package/dist/commands/daemons/command.d.ts.map +1 -1
  6. package/dist/commands/daemons/handlers.d.ts +1 -0
  7. package/dist/commands/daemons/handlers.d.ts.map +1 -1
  8. package/dist/commands/daemons/help.d.ts.map +1 -1
  9. package/dist/commands/deployments/command.d.ts.map +1 -1
  10. package/dist/commands/deployments/handlers.d.ts +1 -0
  11. package/dist/commands/deployments/handlers.d.ts.map +1 -1
  12. package/dist/commands/deployments/help.d.ts.map +1 -1
  13. package/dist/commands/services/command.d.ts +2 -0
  14. package/dist/commands/services/command.d.ts.map +1 -0
  15. package/dist/commands/services/handlers.d.ts +10 -0
  16. package/dist/commands/services/handlers.d.ts.map +1 -0
  17. package/dist/commands/services/help.d.ts +2 -0
  18. package/dist/commands/services/help.d.ts.map +1 -0
  19. package/dist/commands/services/index.d.ts +3 -0
  20. package/dist/commands/services/index.d.ts.map +1 -0
  21. package/dist/commands/ssh/command.d.ts +9 -0
  22. package/dist/commands/ssh/command.d.ts.map +1 -0
  23. package/dist/commands/ssh/handlers.d.ts +16 -0
  24. package/dist/commands/ssh/handlers.d.ts.map +1 -0
  25. package/dist/commands/ssh/help.d.ts +2 -0
  26. package/dist/commands/ssh/help.d.ts.map +1 -0
  27. package/dist/commands/ssh/index.d.ts +3 -0
  28. package/dist/commands/ssh/index.d.ts.map +1 -0
  29. package/dist/commands/ssh/ssh-target.d.ts +36 -0
  30. package/dist/commands/ssh/ssh-target.d.ts.map +1 -0
  31. package/dist/{error-handler-ByMitiOr.js → error-handler-BA357jIr.js} +34 -3
  32. package/dist/error-handler-BA357jIr.js.map +1 -0
  33. package/dist/index.js +1 -1
  34. package/dist/output.d.ts +5 -4
  35. package/dist/output.d.ts.map +1 -1
  36. package/package.json +3 -3
  37. package/dist/error-handler-ByMitiOr.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,9 +1,11 @@
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-ByMitiOr.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";
7
+ import { spawnSync } from "node:child_process";
8
+ //#region src/audit.ts
7
9
  /**
8
10
  * Shared audit logger for CLI write operations.
9
11
  * Lazily initialized on first use.
@@ -13,6 +15,8 @@ function getCliAuditLogger() {
13
15
  if (!_auditLogger) _auditLogger = createAuditLogger("cli");
14
16
  return _auditLogger;
15
17
  }
18
+ //#endregion
19
+ //#region src/utils/command-router.ts
16
20
  /**
17
21
  * Creates a command router function that dispatches to the appropriate handler.
18
22
  */
@@ -55,6 +59,8 @@ function createCommandRouter(config) {
55
59
  else await handler(ctx);
56
60
  };
57
61
  }
62
+ //#endregion
63
+ //#region src/utils/resolve.ts
58
64
  /**
59
65
  * Smart resolution utilities for --server and --site options.
60
66
  *
@@ -106,6 +112,8 @@ async function resolveSiteId(value, serverId, execCtx) {
106
112
  }
107
113
  return String(partial[0].id);
108
114
  }
115
+ //#endregion
116
+ //#region src/commands/backups/handlers.ts
109
117
  async function backupsList(ctx) {
110
118
  const server = String(ctx.options.server ?? "");
111
119
  if (!server) exitWithValidationError("server_id", "forge backups list --server <server_id>", ctx.formatter);
@@ -216,7 +224,9 @@ async function backupsDelete(args, ctx) {
216
224
  ctx.formatter.success(`Backup configuration ${id} deleted.`);
217
225
  }, ctx.formatter);
218
226
  }
219
- const handleBackupsCommand = createCommandRouter({
227
+ //#endregion
228
+ //#region src/commands/backups/command.ts
229
+ var handleBackupsCommand = createCommandRouter({
220
230
  resource: "backups",
221
231
  handlers: {
222
232
  list: backupsList,
@@ -232,6 +242,8 @@ const handleBackupsCommand = createCommandRouter({
232
242
  "delete"
233
243
  ]
234
244
  });
245
+ //#endregion
246
+ //#region src/commands/backups/help.ts
235
247
  function showBackupsHelp(subcommand) {
236
248
  if (subcommand === "list" || subcommand === "ls") console.log(`
237
249
  ${colors.bold("forge backups list")} - List backup configurations on a server
@@ -309,6 +321,8 @@ ${colors.bold("OPTIONS:")}
309
321
  Run ${colors.cyan("forge backups <subcommand> --help")} for details.
310
322
  `);
311
323
  }
324
+ //#endregion
325
+ //#region src/commands/completion.ts
312
326
  /**
313
327
  * Shell completion installation command
314
328
  */
@@ -322,16 +336,16 @@ _forge_completions() {
322
336
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
323
337
 
324
338
  # Main commands
325
- commands="config servers s sites deployments d databases db database-users daemons env nginx certificates certs firewall-rules fw 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"
326
340
 
327
341
  # Subcommands for each command
328
342
  local config_cmds="set get delete"
329
343
  local servers_cmds="list ls get reboot"
330
344
  local sites_cmds="list ls get"
331
- local deployments_cmds="list ls deploy"
345
+ local deployments_cmds="list ls deploy logs log"
332
346
  local databases_cmds="list ls get"
333
347
  local database_users_cmds="list ls get create delete"
334
- local daemons_cmds="list ls get restart"
348
+ local daemons_cmds="list ls get restart logs log"
335
349
  local env_cmds="get update"
336
350
  local nginx_cmds="get update"
337
351
  local certificates_cmds="get activate"
@@ -340,6 +354,7 @@ _forge_completions() {
340
354
  local backups_cmds="list ls get create delete"
341
355
  local commands_cmds="list ls get create"
342
356
  local scheduled_jobs_cmds="list ls get create delete"
357
+ local services_cmds="list ls restart"
343
358
  local user_cmds="get"
344
359
  local monitors_cmds="list ls get create delete"
345
360
  local nginx_templates_cmds="list ls get create update delete"
@@ -349,7 +364,7 @@ _forge_completions() {
349
364
  local completion_cmds="bash zsh fish"
350
365
 
351
366
  # Global options
352
- options="--token --server --site -f --format --no-color -h --help -v --version --name --command --content --from --to --provider --frequency --type --operator --threshold --minutes --key --port --ip-address --user --script --servers --domain --password --credential-id --region --size --project-type --directory"
367
+ options="--token --server --site -f --format --no-color -h --help -v --version --name --command --content --from --to --provider --frequency --type --operator --threshold --minutes --key --port --ip-address --user --private --dry-run --script --servers --domain --password --credential-id --region --size --project-type --directory"
353
368
 
354
369
  # Format options
355
370
  local formats="json human table"
@@ -408,6 +423,9 @@ _forge_completions() {
408
423
  scheduled-jobs)
409
424
  COMPREPLY=( $(compgen -W "\${scheduled_jobs_cmds}" -- "\${cur}") )
410
425
  ;;
426
+ services)
427
+ COMPREPLY=( $(compgen -W "\${services_cmds}" -- "\${cur}") )
428
+ ;;
411
429
  user)
412
430
  COMPREPLY=( $(compgen -W "\${user_cmds}" -- "\${cur}") )
413
431
  ;;
@@ -486,10 +504,12 @@ _forge() {
486
504
  'certs:Manage SSL certificates (alias)'
487
505
  'firewall-rules:Manage firewall rules'
488
506
  'fw:Manage firewall rules (alias)'
507
+ 'ssh:Open an SSH session to a server'
489
508
  'ssh-keys:Manage SSH keys'
490
509
  'backups:Manage backup configurations'
491
510
  'commands:Manage site commands'
492
511
  'scheduled-jobs:Manage scheduled jobs'
512
+ 'services:List and restart server services'
493
513
  'user:Display authenticated user profile'
494
514
  'monitors:Manage server monitors'
495
515
  'nginx-templates:Manage Nginx templates'
@@ -537,6 +557,8 @@ _forge() {
537
557
  'list:List deployments'
538
558
  'ls:List deployments (alias)'
539
559
  'deploy:Trigger a deployment'
560
+ 'logs:Show deployment output'
561
+ 'log:Show deployment output (alias)'
540
562
  )
541
563
  _describe 'deployments command' deployments_cmds
542
564
  ;;
@@ -567,6 +589,8 @@ _forge() {
567
589
  'ls:List daemons (alias)'
568
590
  'get:Get daemon details'
569
591
  'restart:Restart a daemon'
592
+ 'logs:Show daemon log output'
593
+ 'log:Show daemon log output (alias)'
570
594
  )
571
595
  _describe 'daemons command' daemons_cmds
572
596
  ;;
@@ -644,6 +668,15 @@ _forge() {
644
668
  )
645
669
  _describe 'scheduled-jobs command' scheduled_jobs_cmds
646
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
+ ;;
647
680
  user)
648
681
  local -a user_cmds
649
682
  user_cmds=(
@@ -775,10 +808,12 @@ complete -c forge -f -n "__fish_use_subcommand" -a "certificates" -d "Manage SSL
775
808
  complete -c forge -f -n "__fish_use_subcommand" -a "certs" -d "Manage SSL certificates (alias)"
776
809
  complete -c forge -f -n "__fish_use_subcommand" -a "firewall-rules" -d "Manage firewall rules"
777
810
  complete -c forge -f -n "__fish_use_subcommand" -a "fw" -d "Manage firewall rules (alias)"
811
+ complete -c forge -f -n "__fish_use_subcommand" -a "ssh" -d "Open an SSH session to a server"
778
812
  complete -c forge -f -n "__fish_use_subcommand" -a "ssh-keys" -d "Manage SSH keys"
779
813
  complete -c forge -f -n "__fish_use_subcommand" -a "backups" -d "Manage backup configurations"
780
814
  complete -c forge -f -n "__fish_use_subcommand" -a "commands" -d "Manage site commands"
781
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"
782
817
  complete -c forge -f -n "__fish_use_subcommand" -a "user" -d "Display authenticated user profile"
783
818
  complete -c forge -f -n "__fish_use_subcommand" -a "monitors" -d "Manage server monitors"
784
819
  complete -c forge -f -n "__fish_use_subcommand" -a "nginx-templates" -d "Manage Nginx templates"
@@ -808,6 +843,8 @@ complete -c forge -f -n "__fish_seen_subcommand_from sites" -a "get" -d "Get sit
808
843
  complete -c forge -f -n "__fish_seen_subcommand_from deployments d" -a "list" -d "List deployments"
809
844
  complete -c forge -f -n "__fish_seen_subcommand_from deployments d" -a "ls" -d "List deployments (alias)"
810
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)"
811
848
 
812
849
  # databases subcommands
813
850
  complete -c forge -f -n "__fish_seen_subcommand_from databases db" -a "list" -d "List databases"
@@ -826,6 +863,8 @@ complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "list" -d "List
826
863
  complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "ls" -d "List daemons (alias)"
827
864
  complete -c forge -f -n "__fish_seen_subcommand_from daemons" -a "get" -d "Get daemon details"
828
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)"
829
868
 
830
869
  # env subcommands
831
870
  complete -c forge -f -n "__fish_seen_subcommand_from env" -a "get" -d "Get env variables"
@@ -870,6 +909,11 @@ complete -c forge -f -n "__fish_seen_subcommand_from scheduled-jobs" -a "get" -d
870
909
  complete -c forge -f -n "__fish_seen_subcommand_from scheduled-jobs" -a "create" -d "Create a scheduled job"
871
910
  complete -c forge -f -n "__fish_seen_subcommand_from scheduled-jobs" -a "delete" -d "Delete a scheduled job"
872
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
+
873
917
  # user subcommands
874
918
  complete -c forge -f -n "__fish_seen_subcommand_from user" -a "get" -d "Get the authenticated user profile"
875
919
 
@@ -1053,6 +1097,8 @@ function handleCompletionCommand(shell, options = {}) {
1053
1097
  process.exit(1);
1054
1098
  }
1055
1099
  }
1100
+ //#endregion
1101
+ //#region src/commands/certificates/handlers.ts
1056
1102
  function requireServerSiteDomainRaw(ctx, usage) {
1057
1103
  const server = String(ctx.options.server ?? "");
1058
1104
  const site = String(ctx.options.site ?? "");
@@ -1104,7 +1150,9 @@ async function certificatesActivate(args, ctx) {
1104
1150
  ctx.formatter.success(`Certificate for domain ${domainId} activated.`);
1105
1151
  }, ctx.formatter);
1106
1152
  }
1107
- const handleCertificatesCommand = createCommandRouter({
1153
+ //#endregion
1154
+ //#region src/commands/certificates/command.ts
1155
+ var handleCertificatesCommand = createCommandRouter({
1108
1156
  resource: "certificates",
1109
1157
  handlers: {
1110
1158
  get: [certificatesGet, "args"],
@@ -1112,6 +1160,8 @@ const handleCertificatesCommand = createCommandRouter({
1112
1160
  },
1113
1161
  writeSubcommands: ["activate"]
1114
1162
  });
1163
+ //#endregion
1164
+ //#region src/commands/certificates/help.ts
1115
1165
  function showCertificatesHelp(subcommand) {
1116
1166
  if (subcommand === "get") console.log(`
1117
1167
  ${colors.bold("forge certificates get")} - Get certificate for a domain
@@ -1150,6 +1200,8 @@ ${colors.bold("NOTE:")}
1150
1200
  Run ${colors.cyan("forge certificates <subcommand> --help")} for details.
1151
1201
  `);
1152
1202
  }
1203
+ //#endregion
1204
+ //#region src/commands/commands/handlers.ts
1153
1205
  function requireServerAndSiteRaw$2(ctx, usage) {
1154
1206
  const server = String(ctx.options.server ?? "");
1155
1207
  const site = String(ctx.options.site ?? "");
@@ -1219,7 +1271,9 @@ async function commandsCreate(ctx) {
1219
1271
  ctx.formatter.success("Command queued for execution.");
1220
1272
  }, ctx.formatter);
1221
1273
  }
1222
- const handleCommandsCommand = createCommandRouter({
1274
+ //#endregion
1275
+ //#region src/commands/commands/command.ts
1276
+ var handleCommandsCommand = createCommandRouter({
1223
1277
  resource: "commands",
1224
1278
  handlers: {
1225
1279
  list: commandsList,
@@ -1229,6 +1283,8 @@ const handleCommandsCommand = createCommandRouter({
1229
1283
  },
1230
1284
  writeSubcommands: ["create"]
1231
1285
  });
1286
+ //#endregion
1287
+ //#region src/commands/commands/help.ts
1232
1288
  function showCommandsHelp(subcommand) {
1233
1289
  if (subcommand === "list" || subcommand === "ls") console.log(`
1234
1290
  ${colors.bold("forge commands list")} - List commands executed on a site
@@ -1278,6 +1334,8 @@ ${colors.bold("OPTIONS:")}
1278
1334
  Run ${colors.cyan("forge commands <subcommand> --help")} for details.
1279
1335
  `);
1280
1336
  }
1337
+ //#endregion
1338
+ //#region src/commands/config.ts
1281
1339
  /**
1282
1340
  * Config command — manage forge configuration
1283
1341
  */
@@ -1362,6 +1420,8 @@ function handleConfigCommand(subcommand, args, options) {
1362
1420
  function maskToken(token) {
1363
1421
  return token.length > 8 ? `${token.slice(0, 4)}${"*".repeat(token.length - 8)}${token.slice(-4)}` : "****";
1364
1422
  }
1423
+ //#endregion
1424
+ //#region src/commands/daemons/handlers.ts
1365
1425
  async function daemonsList(ctx) {
1366
1426
  const server = String(ctx.options.server ?? "");
1367
1427
  if (!server) exitWithValidationError("server_id", "forge daemons list --server <server_id>", ctx.formatter);
@@ -1400,6 +1460,22 @@ async function daemonsGet(args, ctx) {
1400
1460
  ]);
1401
1461
  }, ctx.formatter);
1402
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
+ }
1403
1479
  async function daemonsUpdate(args, ctx) {
1404
1480
  const [id] = args;
1405
1481
  const server = String(ctx.options.server ?? "");
@@ -1443,17 +1519,23 @@ async function daemonsRestart(args, ctx) {
1443
1519
  ctx.formatter.success(`Daemon ${id} restarted.`);
1444
1520
  }, ctx.formatter);
1445
1521
  }
1446
- const handleDaemonsCommand = createCommandRouter({
1522
+ //#endregion
1523
+ //#region src/commands/daemons/command.ts
1524
+ var handleDaemonsCommand = createCommandRouter({
1447
1525
  resource: "daemons",
1448
1526
  handlers: {
1449
1527
  list: daemonsList,
1450
1528
  ls: daemonsList,
1451
1529
  get: [daemonsGet, "args"],
1530
+ logs: [daemonsLogs, "args"],
1531
+ log: [daemonsLogs, "args"],
1452
1532
  restart: [daemonsRestart, "args"],
1453
1533
  update: [daemonsUpdate, "args"]
1454
1534
  },
1455
1535
  writeSubcommands: ["restart", "update"]
1456
1536
  });
1537
+ //#endregion
1538
+ //#region src/commands/daemons/help.ts
1457
1539
  function showDaemonsHelp(subcommand) {
1458
1540
  if (subcommand === "list" || subcommand === "ls") console.log(`
1459
1541
  ${colors.bold("forge daemons list")} - List daemons on a server
@@ -1466,6 +1548,12 @@ ${colors.bold("forge daemons get")} - Get daemon details
1466
1548
 
1467
1549
  ${colors.bold("USAGE:")}
1468
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>
1469
1557
  `);
1470
1558
  else if (subcommand === "update") console.log(`
1471
1559
  ${colors.bold("forge daemons update")} - Update a daemon
@@ -1488,6 +1576,7 @@ ${colors.bold("USAGE:")}
1488
1576
  ${colors.bold("SUBCOMMANDS:")}
1489
1577
  list, ls List daemons
1490
1578
  get <id> Get daemon details
1579
+ logs <id> Show the log output of a daemon
1491
1580
  update <id> Update a daemon
1492
1581
  restart <id> Restart a daemon
1493
1582
 
@@ -1498,6 +1587,8 @@ ${colors.bold("OPTIONS:")}
1498
1587
  Run ${colors.cyan("forge daemons <subcommand> --help")} for details.
1499
1588
  `);
1500
1589
  }
1590
+ //#endregion
1591
+ //#region src/commands/databases/handlers.ts
1501
1592
  async function databasesList(ctx) {
1502
1593
  const server = String(ctx.options.server ?? "");
1503
1594
  if (!server) exitWithValidationError("server_id", "forge databases list --server <server_id>", ctx.formatter);
@@ -1532,7 +1623,9 @@ async function databasesGet(args, ctx) {
1532
1623
  ]);
1533
1624
  }, ctx.formatter);
1534
1625
  }
1535
- const handleDatabasesCommand = createCommandRouter({
1626
+ //#endregion
1627
+ //#region src/commands/databases/command.ts
1628
+ var handleDatabasesCommand = createCommandRouter({
1536
1629
  resource: "databases",
1537
1630
  handlers: {
1538
1631
  list: databasesList,
@@ -1540,6 +1633,8 @@ const handleDatabasesCommand = createCommandRouter({
1540
1633
  get: [databasesGet, "args"]
1541
1634
  }
1542
1635
  });
1636
+ //#endregion
1637
+ //#region src/commands/databases/help.ts
1543
1638
  function showDatabasesHelp(subcommand) {
1544
1639
  if (subcommand === "list" || subcommand === "ls") console.log(`
1545
1640
  ${colors.bold("forge databases list")} - List databases on a server
@@ -1577,6 +1672,8 @@ ${colors.bold("OPTIONS:")}
1577
1672
  Run ${colors.cyan("forge databases <subcommand> --help")} for details.
1578
1673
  `);
1579
1674
  }
1675
+ //#endregion
1676
+ //#region src/commands/database-users/handlers.ts
1580
1677
  async function databaseUsersList(ctx) {
1581
1678
  const server = String(ctx.options.server ?? "");
1582
1679
  if (!server) exitWithValidationError("server_id", "forge database-users list --server <server_id>", ctx.formatter);
@@ -1677,7 +1774,9 @@ async function databaseUsersDelete(args, ctx) {
1677
1774
  ctx.formatter.success(`Database user ${id} deleted.`);
1678
1775
  }, ctx.formatter);
1679
1776
  }
1680
- const handleDatabaseUsersCommand = createCommandRouter({
1777
+ //#endregion
1778
+ //#region src/commands/database-users/command.ts
1779
+ var handleDatabaseUsersCommand = createCommandRouter({
1681
1780
  resource: "database-users",
1682
1781
  handlers: {
1683
1782
  list: databaseUsersList,
@@ -1693,6 +1792,8 @@ const handleDatabaseUsersCommand = createCommandRouter({
1693
1792
  "delete"
1694
1793
  ]
1695
1794
  });
1795
+ //#endregion
1796
+ //#region src/commands/database-users/help.ts
1696
1797
  function showDatabaseUsersHelp(subcommand) {
1697
1798
  if (subcommand === "list" || subcommand === "ls") console.log(`
1698
1799
  ${colors.bold("forge database-users list")} - List database users on a server
@@ -1761,6 +1862,8 @@ ${colors.bold("OPTIONS:")}
1761
1862
  Run ${colors.cyan("forge database-users <subcommand> --help")} for details.
1762
1863
  `);
1763
1864
  }
1865
+ //#endregion
1866
+ //#region src/commands/deployments/handlers.ts
1764
1867
  async function deploymentsList(ctx) {
1765
1868
  const server = String(ctx.options.server ?? "");
1766
1869
  const site = String(ctx.options.site ?? "");
@@ -1782,6 +1885,38 @@ async function deploymentsList(ctx) {
1782
1885
  ], "No deployments found.", (d) => `${String(d.id).padEnd(8)} ${d.status.padEnd(12)} ${(d.commit.hash ?? "—").padEnd(10)} ${d.started_at}`);
1783
1886
  }, ctx.formatter);
1784
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
+ }
1785
1920
  async function deploymentsDeploy(ctx) {
1786
1921
  const server = String(ctx.options.server ?? "");
1787
1922
  const site = String(ctx.options.site ?? "");
@@ -1811,15 +1946,21 @@ async function deploymentsDeploy(ctx) {
1811
1946
  else throw new ApiError(`Deployment failed for site ${site_id} (${elapsedSec}s).`);
1812
1947
  }, ctx.formatter);
1813
1948
  }
1814
- const handleDeploymentsCommand = createCommandRouter({
1949
+ //#endregion
1950
+ //#region src/commands/deployments/command.ts
1951
+ var handleDeploymentsCommand = createCommandRouter({
1815
1952
  resource: "deployments",
1816
1953
  handlers: {
1817
1954
  list: deploymentsList,
1818
1955
  ls: deploymentsList,
1819
- deploy: deploymentsDeploy
1956
+ deploy: deploymentsDeploy,
1957
+ logs: [deploymentsLogs, "args"],
1958
+ log: [deploymentsLogs, "args"]
1820
1959
  },
1821
1960
  writeSubcommands: ["deploy"]
1822
1961
  });
1962
+ //#endregion
1963
+ //#region src/commands/deployments/help.ts
1823
1964
  function showDeploymentsHelp(subcommand) {
1824
1965
  if (subcommand === "list" || subcommand === "ls") console.log(`
1825
1966
  ${colors.bold("forge deployments list")} - List deployments for a site
@@ -1827,6 +1968,17 @@ ${colors.bold("forge deployments list")} - List deployments for a site
1827
1968
  ${colors.bold("USAGE:")}
1828
1969
  forge deployments list --server <server_id> --site <site_id> [options]
1829
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
+
1830
1982
  ${colors.bold("OPTIONS:")}
1831
1983
  --server <id> Server ID (required)
1832
1984
  --site <id> Site ID (required)
@@ -1856,6 +2008,7 @@ ${colors.bold("ALIASES:")}
1856
2008
  ${colors.bold("SUBCOMMANDS:")}
1857
2009
  list, ls List deployments for a site
1858
2010
  deploy Trigger a deployment
2011
+ logs Show output of a deployment (latest if no id)
1859
2012
 
1860
2013
  ${colors.bold("OPTIONS:")}
1861
2014
  --server <id> Server ID (required)
@@ -1865,6 +2018,8 @@ ${colors.bold("OPTIONS:")}
1865
2018
  Run ${colors.cyan("forge deployments <subcommand> --help")} for details.
1866
2019
  `);
1867
2020
  }
2021
+ //#endregion
2022
+ //#region src/commands/env/handlers.ts
1868
2023
  async function envGet(ctx) {
1869
2024
  const server = String(ctx.options.server ?? "");
1870
2025
  const site = String(ctx.options.site ?? "");
@@ -1901,7 +2056,9 @@ async function envUpdate(ctx) {
1901
2056
  ctx.formatter.success("Environment variables updated.");
1902
2057
  }, ctx.formatter);
1903
2058
  }
1904
- const handleEnvCommand = createCommandRouter({
2059
+ //#endregion
2060
+ //#region src/commands/env/command.ts
2061
+ var handleEnvCommand = createCommandRouter({
1905
2062
  resource: "env",
1906
2063
  handlers: {
1907
2064
  get: envGet,
@@ -1909,6 +2066,8 @@ const handleEnvCommand = createCommandRouter({
1909
2066
  },
1910
2067
  writeSubcommands: ["update"]
1911
2068
  });
2069
+ //#endregion
2070
+ //#region src/commands/env/help.ts
1912
2071
  function showEnvHelp(subcommand) {
1913
2072
  if (subcommand === "get") console.log(`
1914
2073
  ${colors.bold("forge env get")} - Get environment variables
@@ -1941,6 +2100,8 @@ ${colors.bold("OPTIONS:")}
1941
2100
  Run ${colors.cyan("forge env <subcommand> --help")} for details.
1942
2101
  `);
1943
2102
  }
2103
+ //#endregion
2104
+ //#region src/commands/firewall-rules/handlers.ts
1944
2105
  async function firewallRulesList(ctx) {
1945
2106
  const server = String(ctx.options.server ?? "");
1946
2107
  if (!server) exitWithValidationError("server_id", "forge firewall-rules list --server <server_id>", ctx.formatter);
@@ -1981,7 +2142,9 @@ async function firewallRulesGet(args, ctx) {
1981
2142
  ]);
1982
2143
  }, ctx.formatter);
1983
2144
  }
1984
- const handleFirewallRulesCommand = createCommandRouter({
2145
+ //#endregion
2146
+ //#region src/commands/firewall-rules/command.ts
2147
+ var handleFirewallRulesCommand = createCommandRouter({
1985
2148
  resource: "firewall-rules",
1986
2149
  handlers: {
1987
2150
  list: firewallRulesList,
@@ -1989,6 +2152,8 @@ const handleFirewallRulesCommand = createCommandRouter({
1989
2152
  get: [firewallRulesGet, "args"]
1990
2153
  }
1991
2154
  });
2155
+ //#endregion
2156
+ //#region src/commands/firewall-rules/help.ts
1992
2157
  function showFirewallRulesHelp(subcommand) {
1993
2158
  if (subcommand === "list" || subcommand === "ls") console.log(`
1994
2159
  ${colors.bold("forge firewall-rules list")} - List firewall rules on a server
@@ -2022,6 +2187,8 @@ ${colors.bold("OPTIONS:")}
2022
2187
  Run ${colors.cyan("forge firewall-rules <subcommand> --help")} for details.
2023
2188
  `);
2024
2189
  }
2190
+ //#endregion
2191
+ //#region src/commands/monitors/handlers.ts
2025
2192
  async function monitorsList(ctx) {
2026
2193
  const server = String(ctx.options.server ?? "");
2027
2194
  if (!server) exitWithValidationError("server_id", "forge monitors list --server <server_id>", ctx.formatter);
@@ -2107,7 +2274,9 @@ async function monitorsDelete(args, ctx) {
2107
2274
  ctx.formatter.success(`Monitor ${id} deleted.`);
2108
2275
  }, ctx.formatter);
2109
2276
  }
2110
- const handleMonitorsCommand = createCommandRouter({
2277
+ //#endregion
2278
+ //#region src/commands/monitors/command.ts
2279
+ var handleMonitorsCommand = createCommandRouter({
2111
2280
  resource: "monitors",
2112
2281
  handlers: {
2113
2282
  list: monitorsList,
@@ -2118,6 +2287,8 @@ const handleMonitorsCommand = createCommandRouter({
2118
2287
  },
2119
2288
  writeSubcommands: ["create", "delete"]
2120
2289
  });
2290
+ //#endregion
2291
+ //#region src/commands/monitors/help.ts
2121
2292
  function showMonitorsHelp(subcommand) {
2122
2293
  if (subcommand === "list" || subcommand === "ls") console.log(`
2123
2294
  ${colors.bold("forge monitors list")} - List monitors on a server
@@ -2174,6 +2345,8 @@ ${colors.bold("OPTIONS:")}
2174
2345
  Run ${colors.cyan("forge monitors <subcommand> --help")} for details.
2175
2346
  `);
2176
2347
  }
2348
+ //#endregion
2349
+ //#region src/commands/nginx/handlers.ts
2177
2350
  async function nginxGet(ctx) {
2178
2351
  const server = String(ctx.options.server ?? "");
2179
2352
  const site = String(ctx.options.site ?? "");
@@ -2210,7 +2383,9 @@ async function nginxUpdate(ctx) {
2210
2383
  ctx.formatter.success("Nginx configuration updated.");
2211
2384
  }, ctx.formatter);
2212
2385
  }
2213
- const handleNginxCommand = createCommandRouter({
2386
+ //#endregion
2387
+ //#region src/commands/nginx/command.ts
2388
+ var handleNginxCommand = createCommandRouter({
2214
2389
  resource: "nginx",
2215
2390
  handlers: {
2216
2391
  get: nginxGet,
@@ -2218,6 +2393,8 @@ const handleNginxCommand = createCommandRouter({
2218
2393
  },
2219
2394
  writeSubcommands: ["update"]
2220
2395
  });
2396
+ //#endregion
2397
+ //#region src/commands/nginx/help.ts
2221
2398
  function showNginxHelp(subcommand) {
2222
2399
  if (subcommand === "get") console.log(`
2223
2400
  ${colors.bold("forge nginx get")} - Get nginx configuration
@@ -2249,6 +2426,8 @@ ${colors.bold("OPTIONS:")}
2249
2426
  Run ${colors.cyan("forge nginx <subcommand> --help")} for details.
2250
2427
  `);
2251
2428
  }
2429
+ //#endregion
2430
+ //#region src/commands/nginx-templates/handlers.ts
2252
2431
  async function nginxTemplatesList(ctx) {
2253
2432
  const server = String(ctx.options.server ?? "");
2254
2433
  if (!server) exitWithValidationError("server_id", "forge nginx-templates list --server <server_id>", ctx.formatter);
@@ -2313,11 +2492,14 @@ async function nginxTemplatesUpdate(args, ctx) {
2313
2492
  await runCommand(async () => {
2314
2493
  const token = ctx.getToken();
2315
2494
  const execCtx = ctx.createExecutorContext(token);
2495
+ const server_id = await resolveServerId(server, execCtx);
2496
+ const name = ctx.options.name ? String(ctx.options.name) : void 0;
2497
+ const content = ctx.options.content ? String(ctx.options.content) : void 0;
2316
2498
  const result = await updateNginxTemplate({
2317
- server_id: await resolveServerId(server, execCtx),
2499
+ server_id,
2318
2500
  id,
2319
- name: ctx.options.name ? String(ctx.options.name) : void 0,
2320
- content: ctx.options.content ? String(ctx.options.content) : void 0
2501
+ name,
2502
+ content
2321
2503
  }, execCtx);
2322
2504
  ctx.formatter.outputOne(result.data, [
2323
2505
  "id",
@@ -2341,7 +2523,9 @@ async function nginxTemplatesDelete(args, ctx) {
2341
2523
  ctx.formatter.success(`Nginx template ${id} deleted.`);
2342
2524
  }, ctx.formatter);
2343
2525
  }
2344
- const handleNginxTemplatesCommand = createCommandRouter({
2526
+ //#endregion
2527
+ //#region src/commands/nginx-templates/command.ts
2528
+ var handleNginxTemplatesCommand = createCommandRouter({
2345
2529
  resource: "nginx-templates",
2346
2530
  handlers: {
2347
2531
  list: nginxTemplatesList,
@@ -2357,6 +2541,8 @@ const handleNginxTemplatesCommand = createCommandRouter({
2357
2541
  "delete"
2358
2542
  ]
2359
2543
  });
2544
+ //#endregion
2545
+ //#region src/commands/nginx-templates/help.ts
2360
2546
  function showNginxTemplatesHelp(subcommand) {
2361
2547
  if (subcommand === "list" || subcommand === "ls") console.log(`
2362
2548
  ${colors.bold("forge nginx-templates list")} - List Nginx templates on a server
@@ -2424,6 +2610,8 @@ ${colors.bold("OPTIONS:")}
2424
2610
  Run ${colors.cyan("forge nginx-templates <subcommand> --help")} for details.
2425
2611
  `);
2426
2612
  }
2613
+ //#endregion
2614
+ //#region src/commands/recipes/handlers.ts
2427
2615
  async function recipesList(ctx) {
2428
2616
  await runCommand(async () => {
2429
2617
  const token = ctx.getToken();
@@ -2492,7 +2680,9 @@ async function recipesRun(args, ctx) {
2492
2680
  ctx.formatter.success(`Recipe ${id} dispatched to ${servers.length} server(s).`);
2493
2681
  }, ctx.formatter);
2494
2682
  }
2495
- const handleRecipesCommand = createCommandRouter({
2683
+ //#endregion
2684
+ //#region src/commands/recipes/command.ts
2685
+ var handleRecipesCommand = createCommandRouter({
2496
2686
  resource: "recipes",
2497
2687
  handlers: {
2498
2688
  list: recipesList,
@@ -2503,6 +2693,8 @@ const handleRecipesCommand = createCommandRouter({
2503
2693
  },
2504
2694
  writeSubcommands: ["update", "run"]
2505
2695
  });
2696
+ //#endregion
2697
+ //#region src/commands/recipes/help.ts
2506
2698
  function showRecipesHelp(subcommand) {
2507
2699
  if (subcommand === "list" || subcommand === "ls") console.log(`
2508
2700
  ${colors.bold("forge recipes list")} - List all recipes
@@ -2554,6 +2746,8 @@ ${colors.bold("OPTIONS:")}
2554
2746
  Run ${colors.cyan("forge recipes <subcommand> --help")} for details.
2555
2747
  `);
2556
2748
  }
2749
+ //#endregion
2750
+ //#region src/commands/redirect-rules/handlers.ts
2557
2751
  function requireServerAndSiteRaw$1(ctx, usage) {
2558
2752
  const server = String(ctx.options.server ?? "");
2559
2753
  const site = String(ctx.options.site ?? "");
@@ -2644,7 +2838,9 @@ async function redirectRulesDelete(args, ctx) {
2644
2838
  ctx.formatter.success(`Redirect rule ${id} deleted.`);
2645
2839
  }, ctx.formatter);
2646
2840
  }
2647
- const handleRedirectRulesCommand = createCommandRouter({
2841
+ //#endregion
2842
+ //#region src/commands/redirect-rules/command.ts
2843
+ var handleRedirectRulesCommand = createCommandRouter({
2648
2844
  resource: "redirect-rules",
2649
2845
  handlers: {
2650
2846
  list: redirectRulesList,
@@ -2655,6 +2851,8 @@ const handleRedirectRulesCommand = createCommandRouter({
2655
2851
  },
2656
2852
  writeSubcommands: ["create", "delete"]
2657
2853
  });
2854
+ //#endregion
2855
+ //#region src/commands/redirect-rules/help.ts
2658
2856
  function showRedirectRulesHelp(subcommand) {
2659
2857
  if (subcommand === "list" || subcommand === "ls") console.log(`
2660
2858
  ${colors.bold("forge redirect-rules list")} - List redirect rules for a site
@@ -2712,6 +2910,8 @@ ${colors.bold("OPTIONS:")}
2712
2910
  Run ${colors.cyan("forge redirect-rules <subcommand> --help")} for details.
2713
2911
  `);
2714
2912
  }
2913
+ //#endregion
2914
+ //#region src/commands/scheduled-jobs/handlers.ts
2715
2915
  async function scheduledJobsList(ctx) {
2716
2916
  const server = String(ctx.options.server ?? "");
2717
2917
  const site = ctx.options.site ? String(ctx.options.site) : void 0;
@@ -2742,10 +2942,11 @@ async function scheduledJobsGet(args, ctx) {
2742
2942
  const token = ctx.getToken();
2743
2943
  const execCtx = ctx.createExecutorContext(token);
2744
2944
  const server_id = await resolveServerId(server, execCtx);
2945
+ const site_id = site ? await resolveSiteId(site, server_id, execCtx) : void 0;
2745
2946
  const result = await getScheduledJob({
2746
2947
  server_id,
2747
2948
  id,
2748
- site_id: site ? await resolveSiteId(site, server_id, execCtx) : void 0
2949
+ site_id
2749
2950
  }, execCtx);
2750
2951
  ctx.formatter.outputOne(result.data, [
2751
2952
  "id",
@@ -2800,15 +3001,18 @@ async function scheduledJobsDelete(args, ctx) {
2800
3001
  const token = ctx.getToken();
2801
3002
  const execCtx = ctx.createExecutorContext(token);
2802
3003
  const server_id = await resolveServerId(server, execCtx);
3004
+ const site_id = site ? await resolveSiteId(site, server_id, execCtx) : void 0;
2803
3005
  await deleteScheduledJob({
2804
3006
  server_id,
2805
3007
  id,
2806
- site_id: site ? await resolveSiteId(site, server_id, execCtx) : void 0
3008
+ site_id
2807
3009
  }, execCtx);
2808
3010
  ctx.formatter.success(`Scheduled job ${id} deleted.`);
2809
3011
  }, ctx.formatter);
2810
3012
  }
2811
- const handleScheduledJobsCommand = createCommandRouter({
3013
+ //#endregion
3014
+ //#region src/commands/scheduled-jobs/command.ts
3015
+ var handleScheduledJobsCommand = createCommandRouter({
2812
3016
  resource: "scheduled-jobs",
2813
3017
  handlers: {
2814
3018
  list: scheduledJobsList,
@@ -2819,6 +3023,8 @@ const handleScheduledJobsCommand = createCommandRouter({
2819
3023
  },
2820
3024
  writeSubcommands: ["create", "delete"]
2821
3025
  });
3026
+ //#endregion
3027
+ //#region src/commands/scheduled-jobs/help.ts
2822
3028
  function showScheduledJobsHelp(subcommand) {
2823
3029
  if (subcommand === "list" || subcommand === "ls") console.log(`
2824
3030
  ${colors.bold("forge scheduled-jobs list")} - List scheduled jobs on a server
@@ -2872,6 +3078,8 @@ ${colors.bold("OPTIONS:")}
2872
3078
  Run ${colors.cyan("forge scheduled-jobs <subcommand> --help")} for details.
2873
3079
  `);
2874
3080
  }
3081
+ //#endregion
3082
+ //#region src/commands/security-rules/handlers.ts
2875
3083
  function requireServerAndSiteRaw(ctx, usage) {
2876
3084
  const server = String(ctx.options.server ?? "");
2877
3085
  const site = String(ctx.options.site ?? "");
@@ -2987,7 +3195,9 @@ async function securityRulesDelete(args, ctx) {
2987
3195
  ctx.formatter.success(`Security rule ${id} deleted.`);
2988
3196
  }, ctx.formatter);
2989
3197
  }
2990
- const handleSecurityRulesCommand = createCommandRouter({
3198
+ //#endregion
3199
+ //#region src/commands/security-rules/command.ts
3200
+ var handleSecurityRulesCommand = createCommandRouter({
2991
3201
  resource: "security-rules",
2992
3202
  handlers: {
2993
3203
  list: securityRulesList,
@@ -3003,6 +3213,8 @@ const handleSecurityRulesCommand = createCommandRouter({
3003
3213
  "delete"
3004
3214
  ]
3005
3215
  });
3216
+ //#endregion
3217
+ //#region src/commands/security-rules/help.ts
3006
3218
  function showSecurityRulesHelp(subcommand) {
3007
3219
  if (subcommand === "list" || subcommand === "ls") console.log(`
3008
3220
  ${colors.bold("forge security-rules list")} - List security rules for a site
@@ -3072,6 +3284,8 @@ ${colors.bold("OPTIONS:")}
3072
3284
  Run ${colors.cyan("forge security-rules <subcommand> --help")} for details.
3073
3285
  `);
3074
3286
  }
3287
+ //#endregion
3288
+ //#region src/commands/servers/handlers.ts
3075
3289
  /**
3076
3290
  * CLI handlers for servers commands.
3077
3291
  * Delegates all HTTP logic to forge-core executors.
@@ -3121,7 +3335,9 @@ async function serversReboot(args, ctx) {
3121
3335
  ctx.formatter.success(`Server ${server_id} reboot initiated.`);
3122
3336
  }, ctx.formatter);
3123
3337
  }
3124
- const handleServersCommand = createCommandRouter({
3338
+ //#endregion
3339
+ //#region src/commands/servers/command.ts
3340
+ var handleServersCommand = createCommandRouter({
3125
3341
  resource: "servers",
3126
3342
  handlers: {
3127
3343
  list: serversList,
@@ -3131,6 +3347,8 @@ const handleServersCommand = createCommandRouter({
3131
3347
  },
3132
3348
  writeSubcommands: ["reboot"]
3133
3349
  });
3350
+ //#endregion
3351
+ //#region src/commands/servers/help.ts
3134
3352
  function showServersHelp(subcommand) {
3135
3353
  if (subcommand === "list" || subcommand === "ls") console.log(`
3136
3354
  ${colors.bold("forge servers list")} - List all servers
@@ -3201,6 +3419,124 @@ ${colors.bold("EXAMPLES:")}
3201
3419
  Run ${colors.cyan("forge servers <subcommand> --help")} for subcommand details.
3202
3420
  `);
3203
3421
  }
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
3539
+ //#region src/commands/sites/handlers.ts
3204
3540
  async function sitesList(ctx) {
3205
3541
  const server = String(ctx.options.server ?? "");
3206
3542
  if (!server) exitWithValidationError("server_id", "forge sites list --server <server_id>", ctx.formatter);
@@ -3276,7 +3612,9 @@ async function sitesUpdate(args, ctx) {
3276
3612
  ]);
3277
3613
  }, ctx.formatter);
3278
3614
  }
3279
- const handleSitesCommand = createCommandRouter({
3615
+ //#endregion
3616
+ //#region src/commands/sites/command.ts
3617
+ var handleSitesCommand = createCommandRouter({
3280
3618
  resource: "sites",
3281
3619
  handlers: {
3282
3620
  list: sitesList,
@@ -3285,6 +3623,8 @@ const handleSitesCommand = createCommandRouter({
3285
3623
  update: [sitesUpdate, "args"]
3286
3624
  }
3287
3625
  });
3626
+ //#endregion
3627
+ //#region src/commands/sites/help.ts
3288
3628
  function showSitesHelp(subcommand) {
3289
3629
  if (subcommand === "list" || subcommand === "ls") console.log(`
3290
3630
  ${colors.bold("forge sites list")} - List sites on a server
@@ -3335,6 +3675,172 @@ ${colors.bold("EXAMPLES:")}
3335
3675
  Run ${colors.cyan("forge sites <subcommand> --help")} for subcommand details.
3336
3676
  `);
3337
3677
  }
3678
+ /**
3679
+ * Resolve the concrete user/host/port to connect to.
3680
+ *
3681
+ * @throws {ValidationError} when the selected IP is not available yet, or the port override is invalid.
3682
+ */
3683
+ function resolveSshTarget(server, options = {}) {
3684
+ const user = options.user?.trim() || "forge";
3685
+ const host = options.private ? server.private_ip_address : server.ip_address;
3686
+ if (!host) {
3687
+ const which = options.private ? "private IP address" : "IP address";
3688
+ const hints = [server.is_ready ? "The server is ready but no address is available — check the Forge dashboard." : "The server is still provisioning. Wait until it is ready, then try again.", options.private ? void 0 : "Use --private to connect over the private network."].filter((hint) => Boolean(hint));
3689
+ throw new ValidationError(`Server "${server.name}" has no ${which} available`, "server", hints);
3690
+ }
3691
+ let port = server.ssh_port;
3692
+ if (options.port !== void 0) {
3693
+ const parsed = Number(options.port);
3694
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) throw new ValidationError(`Invalid SSH port "${options.port}"`, "port", ["Port must be an integer between 1 and 65535."]);
3695
+ port = parsed;
3696
+ }
3697
+ return {
3698
+ user,
3699
+ host,
3700
+ port
3701
+ };
3702
+ }
3703
+ /**
3704
+ * Build the argument list passed to the `ssh` binary.
3705
+ *
3706
+ * Any {@link remoteCommand} is appended after the connection flags, so
3707
+ * `forge ssh web uptime` runs `uptime` remotely and exits.
3708
+ */
3709
+ function buildSshArgs(target, remoteCommand = []) {
3710
+ const args = [
3711
+ `${target.user}@${target.host}`,
3712
+ "-p",
3713
+ String(target.port)
3714
+ ];
3715
+ if (remoteCommand.length > 0) args.push(...remoteCommand);
3716
+ return args;
3717
+ }
3718
+ //#endregion
3719
+ //#region src/commands/ssh/handlers.ts
3720
+ /**
3721
+ * CLI handler for `forge ssh` — open an interactive SSH session to a server.
3722
+ *
3723
+ * Resolves the server, reads its connection details from the API, then hands
3724
+ * off to the local `ssh` binary with an inherited stdio (interactive) session.
3725
+ */
3726
+ /**
3727
+ * Connect to a server over SSH.
3728
+ *
3729
+ * @param server - Server ID or name (resolved via {@link resolveServerId}).
3730
+ * @param remote - Optional remote command to run instead of an interactive shell.
3731
+ * @param ctx - CLI command context.
3732
+ */
3733
+ async function sshConnect(server, remote, ctx) {
3734
+ if (!server) exitWithValidationError("server", "forge ssh <server> [command...]", ctx.formatter);
3735
+ await runCommand(async () => {
3736
+ const token = ctx.getToken();
3737
+ const execCtx = ctx.createExecutorContext(token);
3738
+ const server_id = await resolveServerId(server, execCtx);
3739
+ const { data } = await getServer({ server_id }, execCtx);
3740
+ const target = resolveSshTarget(data, {
3741
+ user: typeof ctx.options.user === "string" ? ctx.options.user : void 0,
3742
+ private: ctx.options.private === true,
3743
+ port: typeof ctx.options.port === "string" ? ctx.options.port : void 0
3744
+ });
3745
+ const sshArgs = buildSshArgs(target, remote);
3746
+ const printable = `ssh ${sshArgs.join(" ")}`;
3747
+ if (ctx.formatter.isJson()) {
3748
+ ctx.formatter.output({
3749
+ command: printable,
3750
+ ...target,
3751
+ args: sshArgs
3752
+ });
3753
+ return;
3754
+ }
3755
+ if (ctx.options["dry-run"] === true) {
3756
+ console.log(printable);
3757
+ return;
3758
+ }
3759
+ const auditArgs = {
3760
+ server: server_id,
3761
+ user: target.user,
3762
+ host: target.host,
3763
+ port: target.port
3764
+ };
3765
+ try {
3766
+ const child = spawnSync("ssh", sshArgs, { stdio: "inherit" });
3767
+ if (child.error) throw child.error;
3768
+ getCliAuditLogger().log({
3769
+ source: "cli",
3770
+ resource: "ssh",
3771
+ action: "connect",
3772
+ args: auditArgs,
3773
+ status: "success"
3774
+ });
3775
+ if (typeof child.status === "number" && child.status !== 0) process.exit(child.status);
3776
+ } catch (err) {
3777
+ getCliAuditLogger().log({
3778
+ source: "cli",
3779
+ resource: "ssh",
3780
+ action: "connect",
3781
+ args: auditArgs,
3782
+ status: "error",
3783
+ error: err instanceof Error ? err.message : String(err)
3784
+ });
3785
+ throw err;
3786
+ }
3787
+ }, ctx.formatter);
3788
+ }
3789
+ //#endregion
3790
+ //#region src/commands/ssh/command.ts
3791
+ /**
3792
+ * Route the top-level `forge ssh` command.
3793
+ *
3794
+ * Unlike resource commands there is no sub-action: the first positional is the
3795
+ * server, and any remaining positionals form an optional remote command.
3796
+ */
3797
+ async function handleSshCommand(server, remote, options) {
3798
+ await sshConnect(server, remote, createContext(options));
3799
+ }
3800
+ //#endregion
3801
+ //#region src/commands/ssh/help.ts
3802
+ function showSshHelp() {
3803
+ console.log(`
3804
+ ${colors.bold("forge ssh")} - Open an SSH session to a server
3805
+
3806
+ ${colors.bold("USAGE:")}
3807
+ forge ssh <server> [command...] [options]
3808
+
3809
+ ${colors.bold("ARGUMENTS:")}
3810
+ <server> Server ID or name (required)
3811
+ [command...] Optional remote command to run, then exit
3812
+
3813
+ ${colors.bold("OPTIONS:")}
3814
+ --user <name> SSH user (default: forge)
3815
+ --private Connect over the private IP address
3816
+ --port <n> Override the server's SSH port
3817
+ --dry-run Print the ssh command instead of connecting
3818
+ -f, --format json Print the resolved connection details as JSON
3819
+ -h, --help Show this help
3820
+
3821
+ ${colors.bold("EXAMPLES:")}
3822
+ # Interactive session (connects as forge@<ip>)
3823
+ forge ssh my-server
3824
+
3825
+ # Connect as a custom user
3826
+ forge ssh my-server --user deploy
3827
+
3828
+ # Use the private network
3829
+ forge ssh my-server --private
3830
+
3831
+ # Run a one-off remote command
3832
+ forge ssh my-server uptime
3833
+
3834
+ # Just show the command that would run
3835
+ forge ssh my-server --dry-run
3836
+
3837
+ ${colors.bold("NOTES:")}
3838
+ Requires your local SSH key to be authorized on the server; this command
3839
+ launches ssh, it does not upload keys.
3840
+ `);
3841
+ }
3842
+ //#endregion
3843
+ //#region src/commands/ssh-keys/handlers.ts
3338
3844
  async function sshKeysList(ctx) {
3339
3845
  const server = String(ctx.options.server ?? "");
3340
3846
  if (!server) exitWithValidationError("server_id", "forge ssh-keys list --server <server_id>", ctx.formatter);
@@ -3369,7 +3875,9 @@ async function sshKeysGet(args, ctx) {
3369
3875
  ]);
3370
3876
  }, ctx.formatter);
3371
3877
  }
3372
- const handleSshKeysCommand = createCommandRouter({
3878
+ //#endregion
3879
+ //#region src/commands/ssh-keys/command.ts
3880
+ var handleSshKeysCommand = createCommandRouter({
3373
3881
  resource: "ssh-keys",
3374
3882
  handlers: {
3375
3883
  list: sshKeysList,
@@ -3377,6 +3885,8 @@ const handleSshKeysCommand = createCommandRouter({
3377
3885
  get: [sshKeysGet, "args"]
3378
3886
  }
3379
3887
  });
3888
+ //#endregion
3889
+ //#region src/commands/ssh-keys/help.ts
3380
3890
  function showSshKeysHelp(subcommand) {
3381
3891
  if (subcommand === "list" || subcommand === "ls") console.log(`
3382
3892
  ${colors.bold("forge ssh-keys list")} - List SSH keys on a server
@@ -3407,6 +3917,8 @@ ${colors.bold("OPTIONS:")}
3407
3917
  Run ${colors.cyan("forge ssh-keys <subcommand> --help")} for details.
3408
3918
  `);
3409
3919
  }
3920
+ //#endregion
3921
+ //#region src/commands/user/handlers.ts
3410
3922
  async function userGet(ctx) {
3411
3923
  await runCommand(async () => {
3412
3924
  const token = ctx.getToken();
@@ -3414,10 +3926,14 @@ async function userGet(ctx) {
3414
3926
  ctx.formatter.output(result.data);
3415
3927
  }, ctx.formatter);
3416
3928
  }
3417
- const handleUserCommand = createCommandRouter({
3929
+ //#endregion
3930
+ //#region src/commands/user/command.ts
3931
+ var handleUserCommand = createCommandRouter({
3418
3932
  resource: "user",
3419
3933
  handlers: { get: userGet }
3420
3934
  });
3935
+ //#endregion
3936
+ //#region src/commands/user/help.ts
3421
3937
  function showUserHelp(subcommand) {
3422
3938
  if (subcommand === "get") console.log(`
3423
3939
  ${colors.bold("forge user get")} - Get the authenticated user profile
@@ -3443,6 +3959,8 @@ ${colors.bold("OPTIONS:")}
3443
3959
  Run ${colors.cyan("forge user <subcommand> --help")} for details.
3444
3960
  `);
3445
3961
  }
3962
+ //#endregion
3963
+ //#region src/utils/args.ts
3446
3964
  function parseArgs(argv = process.argv.slice(2)) {
3447
3965
  const options = {};
3448
3966
  const positional = [];
@@ -3479,7 +3997,9 @@ function parseArgs(argv = process.argv.slice(2)) {
3479
3997
  positional
3480
3998
  };
3481
3999
  }
3482
- var VERSION = "0.4.4";
4000
+ //#endregion
4001
+ //#region src/cli.ts
4002
+ var VERSION = "0.4.6";
3483
4003
  function showHelp() {
3484
4004
  console.log(`
3485
4005
  ${colors.bold("forge")} v${VERSION}
@@ -3538,6 +4058,9 @@ ${colors.bold("COMMANDS:")}
3538
4058
  list, ls List firewall rules (requires --server)
3539
4059
  get <id> Get firewall rule details (requires --server)
3540
4060
 
4061
+ ssh <server> Open an SSH session to a server
4062
+ --user <name> (default: forge), --private, --port <n>
4063
+
3541
4064
  ssh-keys Manage SSH keys
3542
4065
  list, ls List SSH keys (requires --server)
3543
4066
  get <id> Get SSH key details (requires --server)
@@ -3559,6 +4082,10 @@ ${colors.bold("COMMANDS:")}
3559
4082
  create Create a scheduled job (requires --server --command)
3560
4083
  delete <id> Delete a scheduled job (requires --server)
3561
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
+
3562
4089
  user Display authenticated user profile
3563
4090
  get Get the authenticated user profile
3564
4091
 
@@ -3647,7 +4174,7 @@ async function main() {
3647
4174
  showHelp();
3648
4175
  process.exit(0);
3649
4176
  }
3650
- if (options.version || options.v) {
4177
+ if (options.version === true || options.v === true) {
3651
4178
  console.log(VERSION);
3652
4179
  process.exit(0);
3653
4180
  }
@@ -3740,6 +4267,13 @@ async function main() {
3740
4267
  }
3741
4268
  await handleFirewallRulesCommand(subcommand ?? "list", positional, options);
3742
4269
  break;
4270
+ case "ssh":
4271
+ if (wantsHelp) {
4272
+ showSshHelp();
4273
+ process.exit(0);
4274
+ }
4275
+ await handleSshCommand(subcommand, positional, options);
4276
+ break;
3743
4277
  case "ssh-keys":
3744
4278
  if (wantsHelp) {
3745
4279
  showSshKeysHelp(subcommand);
@@ -3768,6 +4302,13 @@ async function main() {
3768
4302
  }
3769
4303
  await handleScheduledJobsCommand(subcommand ?? "list", positional, options);
3770
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;
3771
4312
  case "user":
3772
4313
  if (wantsHelp) {
3773
4314
  showUserHelp(subcommand);
@@ -3831,5 +4372,6 @@ main().catch((error) => {
3831
4372
  console.error(colors.red("Unhandled error:"), error);
3832
4373
  process.exit(1);
3833
4374
  });
4375
+ //#endregion
3834
4376
 
3835
4377
  //# sourceMappingURL=cli.js.map