@studiometa/forge-cli 0.4.4 → 0.4.5

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,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";
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
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";
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,7 +336,7 @@ _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 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"
@@ -349,7 +363,7 @@ _forge_completions() {
349
363
  local completion_cmds="bash zsh fish"
350
364
 
351
365
  # 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"
366
+ 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
367
 
354
368
  # Format options
355
369
  local formats="json human table"
@@ -486,6 +500,7 @@ _forge() {
486
500
  'certs:Manage SSL certificates (alias)'
487
501
  'firewall-rules:Manage firewall rules'
488
502
  'fw:Manage firewall rules (alias)'
503
+ 'ssh:Open an SSH session to a server'
489
504
  'ssh-keys:Manage SSH keys'
490
505
  'backups:Manage backup configurations'
491
506
  'commands:Manage site commands'
@@ -775,6 +790,7 @@ complete -c forge -f -n "__fish_use_subcommand" -a "certificates" -d "Manage SSL
775
790
  complete -c forge -f -n "__fish_use_subcommand" -a "certs" -d "Manage SSL certificates (alias)"
776
791
  complete -c forge -f -n "__fish_use_subcommand" -a "firewall-rules" -d "Manage firewall rules"
777
792
  complete -c forge -f -n "__fish_use_subcommand" -a "fw" -d "Manage firewall rules (alias)"
793
+ complete -c forge -f -n "__fish_use_subcommand" -a "ssh" -d "Open an SSH session to a server"
778
794
  complete -c forge -f -n "__fish_use_subcommand" -a "ssh-keys" -d "Manage SSH keys"
779
795
  complete -c forge -f -n "__fish_use_subcommand" -a "backups" -d "Manage backup configurations"
780
796
  complete -c forge -f -n "__fish_use_subcommand" -a "commands" -d "Manage site commands"
@@ -1053,6 +1069,8 @@ function handleCompletionCommand(shell, options = {}) {
1053
1069
  process.exit(1);
1054
1070
  }
1055
1071
  }
1072
+ //#endregion
1073
+ //#region src/commands/certificates/handlers.ts
1056
1074
  function requireServerSiteDomainRaw(ctx, usage) {
1057
1075
  const server = String(ctx.options.server ?? "");
1058
1076
  const site = String(ctx.options.site ?? "");
@@ -1104,7 +1122,9 @@ async function certificatesActivate(args, ctx) {
1104
1122
  ctx.formatter.success(`Certificate for domain ${domainId} activated.`);
1105
1123
  }, ctx.formatter);
1106
1124
  }
1107
- const handleCertificatesCommand = createCommandRouter({
1125
+ //#endregion
1126
+ //#region src/commands/certificates/command.ts
1127
+ var handleCertificatesCommand = createCommandRouter({
1108
1128
  resource: "certificates",
1109
1129
  handlers: {
1110
1130
  get: [certificatesGet, "args"],
@@ -1112,6 +1132,8 @@ const handleCertificatesCommand = createCommandRouter({
1112
1132
  },
1113
1133
  writeSubcommands: ["activate"]
1114
1134
  });
1135
+ //#endregion
1136
+ //#region src/commands/certificates/help.ts
1115
1137
  function showCertificatesHelp(subcommand) {
1116
1138
  if (subcommand === "get") console.log(`
1117
1139
  ${colors.bold("forge certificates get")} - Get certificate for a domain
@@ -1150,6 +1172,8 @@ ${colors.bold("NOTE:")}
1150
1172
  Run ${colors.cyan("forge certificates <subcommand> --help")} for details.
1151
1173
  `);
1152
1174
  }
1175
+ //#endregion
1176
+ //#region src/commands/commands/handlers.ts
1153
1177
  function requireServerAndSiteRaw$2(ctx, usage) {
1154
1178
  const server = String(ctx.options.server ?? "");
1155
1179
  const site = String(ctx.options.site ?? "");
@@ -1219,7 +1243,9 @@ async function commandsCreate(ctx) {
1219
1243
  ctx.formatter.success("Command queued for execution.");
1220
1244
  }, ctx.formatter);
1221
1245
  }
1222
- const handleCommandsCommand = createCommandRouter({
1246
+ //#endregion
1247
+ //#region src/commands/commands/command.ts
1248
+ var handleCommandsCommand = createCommandRouter({
1223
1249
  resource: "commands",
1224
1250
  handlers: {
1225
1251
  list: commandsList,
@@ -1229,6 +1255,8 @@ const handleCommandsCommand = createCommandRouter({
1229
1255
  },
1230
1256
  writeSubcommands: ["create"]
1231
1257
  });
1258
+ //#endregion
1259
+ //#region src/commands/commands/help.ts
1232
1260
  function showCommandsHelp(subcommand) {
1233
1261
  if (subcommand === "list" || subcommand === "ls") console.log(`
1234
1262
  ${colors.bold("forge commands list")} - List commands executed on a site
@@ -1278,6 +1306,8 @@ ${colors.bold("OPTIONS:")}
1278
1306
  Run ${colors.cyan("forge commands <subcommand> --help")} for details.
1279
1307
  `);
1280
1308
  }
1309
+ //#endregion
1310
+ //#region src/commands/config.ts
1281
1311
  /**
1282
1312
  * Config command — manage forge configuration
1283
1313
  */
@@ -1362,6 +1392,8 @@ function handleConfigCommand(subcommand, args, options) {
1362
1392
  function maskToken(token) {
1363
1393
  return token.length > 8 ? `${token.slice(0, 4)}${"*".repeat(token.length - 8)}${token.slice(-4)}` : "****";
1364
1394
  }
1395
+ //#endregion
1396
+ //#region src/commands/daemons/handlers.ts
1365
1397
  async function daemonsList(ctx) {
1366
1398
  const server = String(ctx.options.server ?? "");
1367
1399
  if (!server) exitWithValidationError("server_id", "forge daemons list --server <server_id>", ctx.formatter);
@@ -1443,7 +1475,9 @@ async function daemonsRestart(args, ctx) {
1443
1475
  ctx.formatter.success(`Daemon ${id} restarted.`);
1444
1476
  }, ctx.formatter);
1445
1477
  }
1446
- const handleDaemonsCommand = createCommandRouter({
1478
+ //#endregion
1479
+ //#region src/commands/daemons/command.ts
1480
+ var handleDaemonsCommand = createCommandRouter({
1447
1481
  resource: "daemons",
1448
1482
  handlers: {
1449
1483
  list: daemonsList,
@@ -1454,6 +1488,8 @@ const handleDaemonsCommand = createCommandRouter({
1454
1488
  },
1455
1489
  writeSubcommands: ["restart", "update"]
1456
1490
  });
1491
+ //#endregion
1492
+ //#region src/commands/daemons/help.ts
1457
1493
  function showDaemonsHelp(subcommand) {
1458
1494
  if (subcommand === "list" || subcommand === "ls") console.log(`
1459
1495
  ${colors.bold("forge daemons list")} - List daemons on a server
@@ -1498,6 +1534,8 @@ ${colors.bold("OPTIONS:")}
1498
1534
  Run ${colors.cyan("forge daemons <subcommand> --help")} for details.
1499
1535
  `);
1500
1536
  }
1537
+ //#endregion
1538
+ //#region src/commands/databases/handlers.ts
1501
1539
  async function databasesList(ctx) {
1502
1540
  const server = String(ctx.options.server ?? "");
1503
1541
  if (!server) exitWithValidationError("server_id", "forge databases list --server <server_id>", ctx.formatter);
@@ -1532,7 +1570,9 @@ async function databasesGet(args, ctx) {
1532
1570
  ]);
1533
1571
  }, ctx.formatter);
1534
1572
  }
1535
- const handleDatabasesCommand = createCommandRouter({
1573
+ //#endregion
1574
+ //#region src/commands/databases/command.ts
1575
+ var handleDatabasesCommand = createCommandRouter({
1536
1576
  resource: "databases",
1537
1577
  handlers: {
1538
1578
  list: databasesList,
@@ -1540,6 +1580,8 @@ const handleDatabasesCommand = createCommandRouter({
1540
1580
  get: [databasesGet, "args"]
1541
1581
  }
1542
1582
  });
1583
+ //#endregion
1584
+ //#region src/commands/databases/help.ts
1543
1585
  function showDatabasesHelp(subcommand) {
1544
1586
  if (subcommand === "list" || subcommand === "ls") console.log(`
1545
1587
  ${colors.bold("forge databases list")} - List databases on a server
@@ -1577,6 +1619,8 @@ ${colors.bold("OPTIONS:")}
1577
1619
  Run ${colors.cyan("forge databases <subcommand> --help")} for details.
1578
1620
  `);
1579
1621
  }
1622
+ //#endregion
1623
+ //#region src/commands/database-users/handlers.ts
1580
1624
  async function databaseUsersList(ctx) {
1581
1625
  const server = String(ctx.options.server ?? "");
1582
1626
  if (!server) exitWithValidationError("server_id", "forge database-users list --server <server_id>", ctx.formatter);
@@ -1677,7 +1721,9 @@ async function databaseUsersDelete(args, ctx) {
1677
1721
  ctx.formatter.success(`Database user ${id} deleted.`);
1678
1722
  }, ctx.formatter);
1679
1723
  }
1680
- const handleDatabaseUsersCommand = createCommandRouter({
1724
+ //#endregion
1725
+ //#region src/commands/database-users/command.ts
1726
+ var handleDatabaseUsersCommand = createCommandRouter({
1681
1727
  resource: "database-users",
1682
1728
  handlers: {
1683
1729
  list: databaseUsersList,
@@ -1693,6 +1739,8 @@ const handleDatabaseUsersCommand = createCommandRouter({
1693
1739
  "delete"
1694
1740
  ]
1695
1741
  });
1742
+ //#endregion
1743
+ //#region src/commands/database-users/help.ts
1696
1744
  function showDatabaseUsersHelp(subcommand) {
1697
1745
  if (subcommand === "list" || subcommand === "ls") console.log(`
1698
1746
  ${colors.bold("forge database-users list")} - List database users on a server
@@ -1761,6 +1809,8 @@ ${colors.bold("OPTIONS:")}
1761
1809
  Run ${colors.cyan("forge database-users <subcommand> --help")} for details.
1762
1810
  `);
1763
1811
  }
1812
+ //#endregion
1813
+ //#region src/commands/deployments/handlers.ts
1764
1814
  async function deploymentsList(ctx) {
1765
1815
  const server = String(ctx.options.server ?? "");
1766
1816
  const site = String(ctx.options.site ?? "");
@@ -1811,7 +1861,9 @@ async function deploymentsDeploy(ctx) {
1811
1861
  else throw new ApiError(`Deployment failed for site ${site_id} (${elapsedSec}s).`);
1812
1862
  }, ctx.formatter);
1813
1863
  }
1814
- const handleDeploymentsCommand = createCommandRouter({
1864
+ //#endregion
1865
+ //#region src/commands/deployments/command.ts
1866
+ var handleDeploymentsCommand = createCommandRouter({
1815
1867
  resource: "deployments",
1816
1868
  handlers: {
1817
1869
  list: deploymentsList,
@@ -1820,6 +1872,8 @@ const handleDeploymentsCommand = createCommandRouter({
1820
1872
  },
1821
1873
  writeSubcommands: ["deploy"]
1822
1874
  });
1875
+ //#endregion
1876
+ //#region src/commands/deployments/help.ts
1823
1877
  function showDeploymentsHelp(subcommand) {
1824
1878
  if (subcommand === "list" || subcommand === "ls") console.log(`
1825
1879
  ${colors.bold("forge deployments list")} - List deployments for a site
@@ -1865,6 +1919,8 @@ ${colors.bold("OPTIONS:")}
1865
1919
  Run ${colors.cyan("forge deployments <subcommand> --help")} for details.
1866
1920
  `);
1867
1921
  }
1922
+ //#endregion
1923
+ //#region src/commands/env/handlers.ts
1868
1924
  async function envGet(ctx) {
1869
1925
  const server = String(ctx.options.server ?? "");
1870
1926
  const site = String(ctx.options.site ?? "");
@@ -1901,7 +1957,9 @@ async function envUpdate(ctx) {
1901
1957
  ctx.formatter.success("Environment variables updated.");
1902
1958
  }, ctx.formatter);
1903
1959
  }
1904
- const handleEnvCommand = createCommandRouter({
1960
+ //#endregion
1961
+ //#region src/commands/env/command.ts
1962
+ var handleEnvCommand = createCommandRouter({
1905
1963
  resource: "env",
1906
1964
  handlers: {
1907
1965
  get: envGet,
@@ -1909,6 +1967,8 @@ const handleEnvCommand = createCommandRouter({
1909
1967
  },
1910
1968
  writeSubcommands: ["update"]
1911
1969
  });
1970
+ //#endregion
1971
+ //#region src/commands/env/help.ts
1912
1972
  function showEnvHelp(subcommand) {
1913
1973
  if (subcommand === "get") console.log(`
1914
1974
  ${colors.bold("forge env get")} - Get environment variables
@@ -1941,6 +2001,8 @@ ${colors.bold("OPTIONS:")}
1941
2001
  Run ${colors.cyan("forge env <subcommand> --help")} for details.
1942
2002
  `);
1943
2003
  }
2004
+ //#endregion
2005
+ //#region src/commands/firewall-rules/handlers.ts
1944
2006
  async function firewallRulesList(ctx) {
1945
2007
  const server = String(ctx.options.server ?? "");
1946
2008
  if (!server) exitWithValidationError("server_id", "forge firewall-rules list --server <server_id>", ctx.formatter);
@@ -1981,7 +2043,9 @@ async function firewallRulesGet(args, ctx) {
1981
2043
  ]);
1982
2044
  }, ctx.formatter);
1983
2045
  }
1984
- const handleFirewallRulesCommand = createCommandRouter({
2046
+ //#endregion
2047
+ //#region src/commands/firewall-rules/command.ts
2048
+ var handleFirewallRulesCommand = createCommandRouter({
1985
2049
  resource: "firewall-rules",
1986
2050
  handlers: {
1987
2051
  list: firewallRulesList,
@@ -1989,6 +2053,8 @@ const handleFirewallRulesCommand = createCommandRouter({
1989
2053
  get: [firewallRulesGet, "args"]
1990
2054
  }
1991
2055
  });
2056
+ //#endregion
2057
+ //#region src/commands/firewall-rules/help.ts
1992
2058
  function showFirewallRulesHelp(subcommand) {
1993
2059
  if (subcommand === "list" || subcommand === "ls") console.log(`
1994
2060
  ${colors.bold("forge firewall-rules list")} - List firewall rules on a server
@@ -2022,6 +2088,8 @@ ${colors.bold("OPTIONS:")}
2022
2088
  Run ${colors.cyan("forge firewall-rules <subcommand> --help")} for details.
2023
2089
  `);
2024
2090
  }
2091
+ //#endregion
2092
+ //#region src/commands/monitors/handlers.ts
2025
2093
  async function monitorsList(ctx) {
2026
2094
  const server = String(ctx.options.server ?? "");
2027
2095
  if (!server) exitWithValidationError("server_id", "forge monitors list --server <server_id>", ctx.formatter);
@@ -2107,7 +2175,9 @@ async function monitorsDelete(args, ctx) {
2107
2175
  ctx.formatter.success(`Monitor ${id} deleted.`);
2108
2176
  }, ctx.formatter);
2109
2177
  }
2110
- const handleMonitorsCommand = createCommandRouter({
2178
+ //#endregion
2179
+ //#region src/commands/monitors/command.ts
2180
+ var handleMonitorsCommand = createCommandRouter({
2111
2181
  resource: "monitors",
2112
2182
  handlers: {
2113
2183
  list: monitorsList,
@@ -2118,6 +2188,8 @@ const handleMonitorsCommand = createCommandRouter({
2118
2188
  },
2119
2189
  writeSubcommands: ["create", "delete"]
2120
2190
  });
2191
+ //#endregion
2192
+ //#region src/commands/monitors/help.ts
2121
2193
  function showMonitorsHelp(subcommand) {
2122
2194
  if (subcommand === "list" || subcommand === "ls") console.log(`
2123
2195
  ${colors.bold("forge monitors list")} - List monitors on a server
@@ -2174,6 +2246,8 @@ ${colors.bold("OPTIONS:")}
2174
2246
  Run ${colors.cyan("forge monitors <subcommand> --help")} for details.
2175
2247
  `);
2176
2248
  }
2249
+ //#endregion
2250
+ //#region src/commands/nginx/handlers.ts
2177
2251
  async function nginxGet(ctx) {
2178
2252
  const server = String(ctx.options.server ?? "");
2179
2253
  const site = String(ctx.options.site ?? "");
@@ -2210,7 +2284,9 @@ async function nginxUpdate(ctx) {
2210
2284
  ctx.formatter.success("Nginx configuration updated.");
2211
2285
  }, ctx.formatter);
2212
2286
  }
2213
- const handleNginxCommand = createCommandRouter({
2287
+ //#endregion
2288
+ //#region src/commands/nginx/command.ts
2289
+ var handleNginxCommand = createCommandRouter({
2214
2290
  resource: "nginx",
2215
2291
  handlers: {
2216
2292
  get: nginxGet,
@@ -2218,6 +2294,8 @@ const handleNginxCommand = createCommandRouter({
2218
2294
  },
2219
2295
  writeSubcommands: ["update"]
2220
2296
  });
2297
+ //#endregion
2298
+ //#region src/commands/nginx/help.ts
2221
2299
  function showNginxHelp(subcommand) {
2222
2300
  if (subcommand === "get") console.log(`
2223
2301
  ${colors.bold("forge nginx get")} - Get nginx configuration
@@ -2249,6 +2327,8 @@ ${colors.bold("OPTIONS:")}
2249
2327
  Run ${colors.cyan("forge nginx <subcommand> --help")} for details.
2250
2328
  `);
2251
2329
  }
2330
+ //#endregion
2331
+ //#region src/commands/nginx-templates/handlers.ts
2252
2332
  async function nginxTemplatesList(ctx) {
2253
2333
  const server = String(ctx.options.server ?? "");
2254
2334
  if (!server) exitWithValidationError("server_id", "forge nginx-templates list --server <server_id>", ctx.formatter);
@@ -2313,11 +2393,14 @@ async function nginxTemplatesUpdate(args, ctx) {
2313
2393
  await runCommand(async () => {
2314
2394
  const token = ctx.getToken();
2315
2395
  const execCtx = ctx.createExecutorContext(token);
2396
+ const server_id = await resolveServerId(server, execCtx);
2397
+ const name = ctx.options.name ? String(ctx.options.name) : void 0;
2398
+ const content = ctx.options.content ? String(ctx.options.content) : void 0;
2316
2399
  const result = await updateNginxTemplate({
2317
- server_id: await resolveServerId(server, execCtx),
2400
+ server_id,
2318
2401
  id,
2319
- name: ctx.options.name ? String(ctx.options.name) : void 0,
2320
- content: ctx.options.content ? String(ctx.options.content) : void 0
2402
+ name,
2403
+ content
2321
2404
  }, execCtx);
2322
2405
  ctx.formatter.outputOne(result.data, [
2323
2406
  "id",
@@ -2341,7 +2424,9 @@ async function nginxTemplatesDelete(args, ctx) {
2341
2424
  ctx.formatter.success(`Nginx template ${id} deleted.`);
2342
2425
  }, ctx.formatter);
2343
2426
  }
2344
- const handleNginxTemplatesCommand = createCommandRouter({
2427
+ //#endregion
2428
+ //#region src/commands/nginx-templates/command.ts
2429
+ var handleNginxTemplatesCommand = createCommandRouter({
2345
2430
  resource: "nginx-templates",
2346
2431
  handlers: {
2347
2432
  list: nginxTemplatesList,
@@ -2357,6 +2442,8 @@ const handleNginxTemplatesCommand = createCommandRouter({
2357
2442
  "delete"
2358
2443
  ]
2359
2444
  });
2445
+ //#endregion
2446
+ //#region src/commands/nginx-templates/help.ts
2360
2447
  function showNginxTemplatesHelp(subcommand) {
2361
2448
  if (subcommand === "list" || subcommand === "ls") console.log(`
2362
2449
  ${colors.bold("forge nginx-templates list")} - List Nginx templates on a server
@@ -2424,6 +2511,8 @@ ${colors.bold("OPTIONS:")}
2424
2511
  Run ${colors.cyan("forge nginx-templates <subcommand> --help")} for details.
2425
2512
  `);
2426
2513
  }
2514
+ //#endregion
2515
+ //#region src/commands/recipes/handlers.ts
2427
2516
  async function recipesList(ctx) {
2428
2517
  await runCommand(async () => {
2429
2518
  const token = ctx.getToken();
@@ -2492,7 +2581,9 @@ async function recipesRun(args, ctx) {
2492
2581
  ctx.formatter.success(`Recipe ${id} dispatched to ${servers.length} server(s).`);
2493
2582
  }, ctx.formatter);
2494
2583
  }
2495
- const handleRecipesCommand = createCommandRouter({
2584
+ //#endregion
2585
+ //#region src/commands/recipes/command.ts
2586
+ var handleRecipesCommand = createCommandRouter({
2496
2587
  resource: "recipes",
2497
2588
  handlers: {
2498
2589
  list: recipesList,
@@ -2503,6 +2594,8 @@ const handleRecipesCommand = createCommandRouter({
2503
2594
  },
2504
2595
  writeSubcommands: ["update", "run"]
2505
2596
  });
2597
+ //#endregion
2598
+ //#region src/commands/recipes/help.ts
2506
2599
  function showRecipesHelp(subcommand) {
2507
2600
  if (subcommand === "list" || subcommand === "ls") console.log(`
2508
2601
  ${colors.bold("forge recipes list")} - List all recipes
@@ -2554,6 +2647,8 @@ ${colors.bold("OPTIONS:")}
2554
2647
  Run ${colors.cyan("forge recipes <subcommand> --help")} for details.
2555
2648
  `);
2556
2649
  }
2650
+ //#endregion
2651
+ //#region src/commands/redirect-rules/handlers.ts
2557
2652
  function requireServerAndSiteRaw$1(ctx, usage) {
2558
2653
  const server = String(ctx.options.server ?? "");
2559
2654
  const site = String(ctx.options.site ?? "");
@@ -2644,7 +2739,9 @@ async function redirectRulesDelete(args, ctx) {
2644
2739
  ctx.formatter.success(`Redirect rule ${id} deleted.`);
2645
2740
  }, ctx.formatter);
2646
2741
  }
2647
- const handleRedirectRulesCommand = createCommandRouter({
2742
+ //#endregion
2743
+ //#region src/commands/redirect-rules/command.ts
2744
+ var handleRedirectRulesCommand = createCommandRouter({
2648
2745
  resource: "redirect-rules",
2649
2746
  handlers: {
2650
2747
  list: redirectRulesList,
@@ -2655,6 +2752,8 @@ const handleRedirectRulesCommand = createCommandRouter({
2655
2752
  },
2656
2753
  writeSubcommands: ["create", "delete"]
2657
2754
  });
2755
+ //#endregion
2756
+ //#region src/commands/redirect-rules/help.ts
2658
2757
  function showRedirectRulesHelp(subcommand) {
2659
2758
  if (subcommand === "list" || subcommand === "ls") console.log(`
2660
2759
  ${colors.bold("forge redirect-rules list")} - List redirect rules for a site
@@ -2712,6 +2811,8 @@ ${colors.bold("OPTIONS:")}
2712
2811
  Run ${colors.cyan("forge redirect-rules <subcommand> --help")} for details.
2713
2812
  `);
2714
2813
  }
2814
+ //#endregion
2815
+ //#region src/commands/scheduled-jobs/handlers.ts
2715
2816
  async function scheduledJobsList(ctx) {
2716
2817
  const server = String(ctx.options.server ?? "");
2717
2818
  const site = ctx.options.site ? String(ctx.options.site) : void 0;
@@ -2742,10 +2843,11 @@ async function scheduledJobsGet(args, ctx) {
2742
2843
  const token = ctx.getToken();
2743
2844
  const execCtx = ctx.createExecutorContext(token);
2744
2845
  const server_id = await resolveServerId(server, execCtx);
2846
+ const site_id = site ? await resolveSiteId(site, server_id, execCtx) : void 0;
2745
2847
  const result = await getScheduledJob({
2746
2848
  server_id,
2747
2849
  id,
2748
- site_id: site ? await resolveSiteId(site, server_id, execCtx) : void 0
2850
+ site_id
2749
2851
  }, execCtx);
2750
2852
  ctx.formatter.outputOne(result.data, [
2751
2853
  "id",
@@ -2800,15 +2902,18 @@ async function scheduledJobsDelete(args, ctx) {
2800
2902
  const token = ctx.getToken();
2801
2903
  const execCtx = ctx.createExecutorContext(token);
2802
2904
  const server_id = await resolveServerId(server, execCtx);
2905
+ const site_id = site ? await resolveSiteId(site, server_id, execCtx) : void 0;
2803
2906
  await deleteScheduledJob({
2804
2907
  server_id,
2805
2908
  id,
2806
- site_id: site ? await resolveSiteId(site, server_id, execCtx) : void 0
2909
+ site_id
2807
2910
  }, execCtx);
2808
2911
  ctx.formatter.success(`Scheduled job ${id} deleted.`);
2809
2912
  }, ctx.formatter);
2810
2913
  }
2811
- const handleScheduledJobsCommand = createCommandRouter({
2914
+ //#endregion
2915
+ //#region src/commands/scheduled-jobs/command.ts
2916
+ var handleScheduledJobsCommand = createCommandRouter({
2812
2917
  resource: "scheduled-jobs",
2813
2918
  handlers: {
2814
2919
  list: scheduledJobsList,
@@ -2819,6 +2924,8 @@ const handleScheduledJobsCommand = createCommandRouter({
2819
2924
  },
2820
2925
  writeSubcommands: ["create", "delete"]
2821
2926
  });
2927
+ //#endregion
2928
+ //#region src/commands/scheduled-jobs/help.ts
2822
2929
  function showScheduledJobsHelp(subcommand) {
2823
2930
  if (subcommand === "list" || subcommand === "ls") console.log(`
2824
2931
  ${colors.bold("forge scheduled-jobs list")} - List scheduled jobs on a server
@@ -2872,6 +2979,8 @@ ${colors.bold("OPTIONS:")}
2872
2979
  Run ${colors.cyan("forge scheduled-jobs <subcommand> --help")} for details.
2873
2980
  `);
2874
2981
  }
2982
+ //#endregion
2983
+ //#region src/commands/security-rules/handlers.ts
2875
2984
  function requireServerAndSiteRaw(ctx, usage) {
2876
2985
  const server = String(ctx.options.server ?? "");
2877
2986
  const site = String(ctx.options.site ?? "");
@@ -2987,7 +3096,9 @@ async function securityRulesDelete(args, ctx) {
2987
3096
  ctx.formatter.success(`Security rule ${id} deleted.`);
2988
3097
  }, ctx.formatter);
2989
3098
  }
2990
- const handleSecurityRulesCommand = createCommandRouter({
3099
+ //#endregion
3100
+ //#region src/commands/security-rules/command.ts
3101
+ var handleSecurityRulesCommand = createCommandRouter({
2991
3102
  resource: "security-rules",
2992
3103
  handlers: {
2993
3104
  list: securityRulesList,
@@ -3003,6 +3114,8 @@ const handleSecurityRulesCommand = createCommandRouter({
3003
3114
  "delete"
3004
3115
  ]
3005
3116
  });
3117
+ //#endregion
3118
+ //#region src/commands/security-rules/help.ts
3006
3119
  function showSecurityRulesHelp(subcommand) {
3007
3120
  if (subcommand === "list" || subcommand === "ls") console.log(`
3008
3121
  ${colors.bold("forge security-rules list")} - List security rules for a site
@@ -3072,6 +3185,8 @@ ${colors.bold("OPTIONS:")}
3072
3185
  Run ${colors.cyan("forge security-rules <subcommand> --help")} for details.
3073
3186
  `);
3074
3187
  }
3188
+ //#endregion
3189
+ //#region src/commands/servers/handlers.ts
3075
3190
  /**
3076
3191
  * CLI handlers for servers commands.
3077
3192
  * Delegates all HTTP logic to forge-core executors.
@@ -3121,7 +3236,9 @@ async function serversReboot(args, ctx) {
3121
3236
  ctx.formatter.success(`Server ${server_id} reboot initiated.`);
3122
3237
  }, ctx.formatter);
3123
3238
  }
3124
- const handleServersCommand = createCommandRouter({
3239
+ //#endregion
3240
+ //#region src/commands/servers/command.ts
3241
+ var handleServersCommand = createCommandRouter({
3125
3242
  resource: "servers",
3126
3243
  handlers: {
3127
3244
  list: serversList,
@@ -3131,6 +3248,8 @@ const handleServersCommand = createCommandRouter({
3131
3248
  },
3132
3249
  writeSubcommands: ["reboot"]
3133
3250
  });
3251
+ //#endregion
3252
+ //#region src/commands/servers/help.ts
3134
3253
  function showServersHelp(subcommand) {
3135
3254
  if (subcommand === "list" || subcommand === "ls") console.log(`
3136
3255
  ${colors.bold("forge servers list")} - List all servers
@@ -3201,6 +3320,8 @@ ${colors.bold("EXAMPLES:")}
3201
3320
  Run ${colors.cyan("forge servers <subcommand> --help")} for subcommand details.
3202
3321
  `);
3203
3322
  }
3323
+ //#endregion
3324
+ //#region src/commands/sites/handlers.ts
3204
3325
  async function sitesList(ctx) {
3205
3326
  const server = String(ctx.options.server ?? "");
3206
3327
  if (!server) exitWithValidationError("server_id", "forge sites list --server <server_id>", ctx.formatter);
@@ -3276,7 +3397,9 @@ async function sitesUpdate(args, ctx) {
3276
3397
  ]);
3277
3398
  }, ctx.formatter);
3278
3399
  }
3279
- const handleSitesCommand = createCommandRouter({
3400
+ //#endregion
3401
+ //#region src/commands/sites/command.ts
3402
+ var handleSitesCommand = createCommandRouter({
3280
3403
  resource: "sites",
3281
3404
  handlers: {
3282
3405
  list: sitesList,
@@ -3285,6 +3408,8 @@ const handleSitesCommand = createCommandRouter({
3285
3408
  update: [sitesUpdate, "args"]
3286
3409
  }
3287
3410
  });
3411
+ //#endregion
3412
+ //#region src/commands/sites/help.ts
3288
3413
  function showSitesHelp(subcommand) {
3289
3414
  if (subcommand === "list" || subcommand === "ls") console.log(`
3290
3415
  ${colors.bold("forge sites list")} - List sites on a server
@@ -3335,6 +3460,172 @@ ${colors.bold("EXAMPLES:")}
3335
3460
  Run ${colors.cyan("forge sites <subcommand> --help")} for subcommand details.
3336
3461
  `);
3337
3462
  }
3463
+ /**
3464
+ * Resolve the concrete user/host/port to connect to.
3465
+ *
3466
+ * @throws {ValidationError} when the selected IP is not available yet, or the port override is invalid.
3467
+ */
3468
+ function resolveSshTarget(server, options = {}) {
3469
+ const user = options.user?.trim() || "forge";
3470
+ const host = options.private ? server.private_ip_address : server.ip_address;
3471
+ if (!host) {
3472
+ const which = options.private ? "private IP address" : "IP address";
3473
+ 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));
3474
+ throw new ValidationError(`Server "${server.name}" has no ${which} available`, "server", hints);
3475
+ }
3476
+ let port = server.ssh_port;
3477
+ if (options.port !== void 0) {
3478
+ const parsed = Number(options.port);
3479
+ 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."]);
3480
+ port = parsed;
3481
+ }
3482
+ return {
3483
+ user,
3484
+ host,
3485
+ port
3486
+ };
3487
+ }
3488
+ /**
3489
+ * Build the argument list passed to the `ssh` binary.
3490
+ *
3491
+ * Any {@link remoteCommand} is appended after the connection flags, so
3492
+ * `forge ssh web uptime` runs `uptime` remotely and exits.
3493
+ */
3494
+ function buildSshArgs(target, remoteCommand = []) {
3495
+ const args = [
3496
+ `${target.user}@${target.host}`,
3497
+ "-p",
3498
+ String(target.port)
3499
+ ];
3500
+ if (remoteCommand.length > 0) args.push(...remoteCommand);
3501
+ return args;
3502
+ }
3503
+ //#endregion
3504
+ //#region src/commands/ssh/handlers.ts
3505
+ /**
3506
+ * CLI handler for `forge ssh` — open an interactive SSH session to a server.
3507
+ *
3508
+ * Resolves the server, reads its connection details from the API, then hands
3509
+ * off to the local `ssh` binary with an inherited stdio (interactive) session.
3510
+ */
3511
+ /**
3512
+ * Connect to a server over SSH.
3513
+ *
3514
+ * @param server - Server ID or name (resolved via {@link resolveServerId}).
3515
+ * @param remote - Optional remote command to run instead of an interactive shell.
3516
+ * @param ctx - CLI command context.
3517
+ */
3518
+ async function sshConnect(server, remote, ctx) {
3519
+ if (!server) exitWithValidationError("server", "forge ssh <server> [command...]", ctx.formatter);
3520
+ await runCommand(async () => {
3521
+ const token = ctx.getToken();
3522
+ const execCtx = ctx.createExecutorContext(token);
3523
+ const server_id = await resolveServerId(server, execCtx);
3524
+ const { data } = await getServer({ server_id }, execCtx);
3525
+ const target = resolveSshTarget(data, {
3526
+ user: typeof ctx.options.user === "string" ? ctx.options.user : void 0,
3527
+ private: ctx.options.private === true,
3528
+ port: typeof ctx.options.port === "string" ? ctx.options.port : void 0
3529
+ });
3530
+ const sshArgs = buildSshArgs(target, remote);
3531
+ const printable = `ssh ${sshArgs.join(" ")}`;
3532
+ if (ctx.formatter.isJson()) {
3533
+ ctx.formatter.output({
3534
+ command: printable,
3535
+ ...target,
3536
+ args: sshArgs
3537
+ });
3538
+ return;
3539
+ }
3540
+ if (ctx.options["dry-run"] === true) {
3541
+ console.log(printable);
3542
+ return;
3543
+ }
3544
+ const auditArgs = {
3545
+ server: server_id,
3546
+ user: target.user,
3547
+ host: target.host,
3548
+ port: target.port
3549
+ };
3550
+ try {
3551
+ const child = spawnSync("ssh", sshArgs, { stdio: "inherit" });
3552
+ if (child.error) throw child.error;
3553
+ getCliAuditLogger().log({
3554
+ source: "cli",
3555
+ resource: "ssh",
3556
+ action: "connect",
3557
+ args: auditArgs,
3558
+ status: "success"
3559
+ });
3560
+ if (typeof child.status === "number" && child.status !== 0) process.exit(child.status);
3561
+ } catch (err) {
3562
+ getCliAuditLogger().log({
3563
+ source: "cli",
3564
+ resource: "ssh",
3565
+ action: "connect",
3566
+ args: auditArgs,
3567
+ status: "error",
3568
+ error: err instanceof Error ? err.message : String(err)
3569
+ });
3570
+ throw err;
3571
+ }
3572
+ }, ctx.formatter);
3573
+ }
3574
+ //#endregion
3575
+ //#region src/commands/ssh/command.ts
3576
+ /**
3577
+ * Route the top-level `forge ssh` command.
3578
+ *
3579
+ * Unlike resource commands there is no sub-action: the first positional is the
3580
+ * server, and any remaining positionals form an optional remote command.
3581
+ */
3582
+ async function handleSshCommand(server, remote, options) {
3583
+ await sshConnect(server, remote, createContext(options));
3584
+ }
3585
+ //#endregion
3586
+ //#region src/commands/ssh/help.ts
3587
+ function showSshHelp() {
3588
+ console.log(`
3589
+ ${colors.bold("forge ssh")} - Open an SSH session to a server
3590
+
3591
+ ${colors.bold("USAGE:")}
3592
+ forge ssh <server> [command...] [options]
3593
+
3594
+ ${colors.bold("ARGUMENTS:")}
3595
+ <server> Server ID or name (required)
3596
+ [command...] Optional remote command to run, then exit
3597
+
3598
+ ${colors.bold("OPTIONS:")}
3599
+ --user <name> SSH user (default: forge)
3600
+ --private Connect over the private IP address
3601
+ --port <n> Override the server's SSH port
3602
+ --dry-run Print the ssh command instead of connecting
3603
+ -f, --format json Print the resolved connection details as JSON
3604
+ -h, --help Show this help
3605
+
3606
+ ${colors.bold("EXAMPLES:")}
3607
+ # Interactive session (connects as forge@<ip>)
3608
+ forge ssh my-server
3609
+
3610
+ # Connect as a custom user
3611
+ forge ssh my-server --user deploy
3612
+
3613
+ # Use the private network
3614
+ forge ssh my-server --private
3615
+
3616
+ # Run a one-off remote command
3617
+ forge ssh my-server uptime
3618
+
3619
+ # Just show the command that would run
3620
+ forge ssh my-server --dry-run
3621
+
3622
+ ${colors.bold("NOTES:")}
3623
+ Requires your local SSH key to be authorized on the server; this command
3624
+ launches ssh, it does not upload keys.
3625
+ `);
3626
+ }
3627
+ //#endregion
3628
+ //#region src/commands/ssh-keys/handlers.ts
3338
3629
  async function sshKeysList(ctx) {
3339
3630
  const server = String(ctx.options.server ?? "");
3340
3631
  if (!server) exitWithValidationError("server_id", "forge ssh-keys list --server <server_id>", ctx.formatter);
@@ -3369,7 +3660,9 @@ async function sshKeysGet(args, ctx) {
3369
3660
  ]);
3370
3661
  }, ctx.formatter);
3371
3662
  }
3372
- const handleSshKeysCommand = createCommandRouter({
3663
+ //#endregion
3664
+ //#region src/commands/ssh-keys/command.ts
3665
+ var handleSshKeysCommand = createCommandRouter({
3373
3666
  resource: "ssh-keys",
3374
3667
  handlers: {
3375
3668
  list: sshKeysList,
@@ -3377,6 +3670,8 @@ const handleSshKeysCommand = createCommandRouter({
3377
3670
  get: [sshKeysGet, "args"]
3378
3671
  }
3379
3672
  });
3673
+ //#endregion
3674
+ //#region src/commands/ssh-keys/help.ts
3380
3675
  function showSshKeysHelp(subcommand) {
3381
3676
  if (subcommand === "list" || subcommand === "ls") console.log(`
3382
3677
  ${colors.bold("forge ssh-keys list")} - List SSH keys on a server
@@ -3407,6 +3702,8 @@ ${colors.bold("OPTIONS:")}
3407
3702
  Run ${colors.cyan("forge ssh-keys <subcommand> --help")} for details.
3408
3703
  `);
3409
3704
  }
3705
+ //#endregion
3706
+ //#region src/commands/user/handlers.ts
3410
3707
  async function userGet(ctx) {
3411
3708
  await runCommand(async () => {
3412
3709
  const token = ctx.getToken();
@@ -3414,10 +3711,14 @@ async function userGet(ctx) {
3414
3711
  ctx.formatter.output(result.data);
3415
3712
  }, ctx.formatter);
3416
3713
  }
3417
- const handleUserCommand = createCommandRouter({
3714
+ //#endregion
3715
+ //#region src/commands/user/command.ts
3716
+ var handleUserCommand = createCommandRouter({
3418
3717
  resource: "user",
3419
3718
  handlers: { get: userGet }
3420
3719
  });
3720
+ //#endregion
3721
+ //#region src/commands/user/help.ts
3421
3722
  function showUserHelp(subcommand) {
3422
3723
  if (subcommand === "get") console.log(`
3423
3724
  ${colors.bold("forge user get")} - Get the authenticated user profile
@@ -3443,6 +3744,8 @@ ${colors.bold("OPTIONS:")}
3443
3744
  Run ${colors.cyan("forge user <subcommand> --help")} for details.
3444
3745
  `);
3445
3746
  }
3747
+ //#endregion
3748
+ //#region src/utils/args.ts
3446
3749
  function parseArgs(argv = process.argv.slice(2)) {
3447
3750
  const options = {};
3448
3751
  const positional = [];
@@ -3479,7 +3782,9 @@ function parseArgs(argv = process.argv.slice(2)) {
3479
3782
  positional
3480
3783
  };
3481
3784
  }
3482
- var VERSION = "0.4.4";
3785
+ //#endregion
3786
+ //#region src/cli.ts
3787
+ var VERSION = "0.4.5";
3483
3788
  function showHelp() {
3484
3789
  console.log(`
3485
3790
  ${colors.bold("forge")} v${VERSION}
@@ -3538,6 +3843,9 @@ ${colors.bold("COMMANDS:")}
3538
3843
  list, ls List firewall rules (requires --server)
3539
3844
  get <id> Get firewall rule details (requires --server)
3540
3845
 
3846
+ ssh <server> Open an SSH session to a server
3847
+ --user <name> (default: forge), --private, --port <n>
3848
+
3541
3849
  ssh-keys Manage SSH keys
3542
3850
  list, ls List SSH keys (requires --server)
3543
3851
  get <id> Get SSH key details (requires --server)
@@ -3740,6 +4048,13 @@ async function main() {
3740
4048
  }
3741
4049
  await handleFirewallRulesCommand(subcommand ?? "list", positional, options);
3742
4050
  break;
4051
+ case "ssh":
4052
+ if (wantsHelp) {
4053
+ showSshHelp();
4054
+ process.exit(0);
4055
+ }
4056
+ await handleSshCommand(subcommand, positional, options);
4057
+ break;
3743
4058
  case "ssh-keys":
3744
4059
  if (wantsHelp) {
3745
4060
  showSshKeysHelp(subcommand);
@@ -3831,5 +4146,6 @@ main().catch((error) => {
3831
4146
  console.error(colors.red("Unhandled error:"), error);
3832
4147
  process.exit(1);
3833
4148
  });
4149
+ //#endregion
3834
4150
 
3835
4151
  //# sourceMappingURL=cli.js.map