@studiometa/forge-cli 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/README.md +236 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +2746 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/backups/command.d.ts +2 -0
  7. package/dist/commands/backups/command.d.ts.map +1 -0
  8. package/dist/commands/backups/handlers.d.ts +6 -0
  9. package/dist/commands/backups/handlers.d.ts.map +1 -0
  10. package/dist/commands/backups/help.d.ts +2 -0
  11. package/dist/commands/backups/help.d.ts.map +1 -0
  12. package/dist/commands/backups/index.d.ts +3 -0
  13. package/dist/commands/backups/index.d.ts.map +1 -0
  14. package/dist/commands/certificates/command.d.ts +2 -0
  15. package/dist/commands/certificates/command.d.ts.map +1 -0
  16. package/dist/commands/certificates/handlers.d.ts +5 -0
  17. package/dist/commands/certificates/handlers.d.ts.map +1 -0
  18. package/dist/commands/certificates/help.d.ts +2 -0
  19. package/dist/commands/certificates/help.d.ts.map +1 -0
  20. package/dist/commands/certificates/index.d.ts +3 -0
  21. package/dist/commands/certificates/index.d.ts.map +1 -0
  22. package/dist/commands/commands/command.d.ts +2 -0
  23. package/dist/commands/commands/command.d.ts.map +1 -0
  24. package/dist/commands/commands/handlers.d.ts +5 -0
  25. package/dist/commands/commands/handlers.d.ts.map +1 -0
  26. package/dist/commands/commands/help.d.ts +2 -0
  27. package/dist/commands/commands/help.d.ts.map +1 -0
  28. package/dist/commands/commands/index.d.ts +3 -0
  29. package/dist/commands/commands/index.d.ts.map +1 -0
  30. package/dist/commands/config.d.ts +6 -0
  31. package/dist/commands/config.d.ts.map +1 -0
  32. package/dist/commands/daemons/command.d.ts +2 -0
  33. package/dist/commands/daemons/command.d.ts.map +1 -0
  34. package/dist/commands/daemons/handlers.d.ts +5 -0
  35. package/dist/commands/daemons/handlers.d.ts.map +1 -0
  36. package/dist/commands/daemons/help.d.ts +2 -0
  37. package/dist/commands/daemons/help.d.ts.map +1 -0
  38. package/dist/commands/daemons/index.d.ts +3 -0
  39. package/dist/commands/daemons/index.d.ts.map +1 -0
  40. package/dist/commands/database-users/command.d.ts +2 -0
  41. package/dist/commands/database-users/command.d.ts.map +1 -0
  42. package/dist/commands/database-users/handlers.d.ts +6 -0
  43. package/dist/commands/database-users/handlers.d.ts.map +1 -0
  44. package/dist/commands/database-users/help.d.ts +2 -0
  45. package/dist/commands/database-users/help.d.ts.map +1 -0
  46. package/dist/commands/database-users/index.d.ts +3 -0
  47. package/dist/commands/database-users/index.d.ts.map +1 -0
  48. package/dist/commands/databases/command.d.ts +2 -0
  49. package/dist/commands/databases/command.d.ts.map +1 -0
  50. package/dist/commands/databases/handlers.d.ts +4 -0
  51. package/dist/commands/databases/handlers.d.ts.map +1 -0
  52. package/dist/commands/databases/help.d.ts +2 -0
  53. package/dist/commands/databases/help.d.ts.map +1 -0
  54. package/dist/commands/databases/index.d.ts +3 -0
  55. package/dist/commands/databases/index.d.ts.map +1 -0
  56. package/dist/commands/deployments/command.d.ts +2 -0
  57. package/dist/commands/deployments/command.d.ts.map +1 -0
  58. package/dist/commands/deployments/handlers.d.ts +4 -0
  59. package/dist/commands/deployments/handlers.d.ts.map +1 -0
  60. package/dist/commands/deployments/help.d.ts +2 -0
  61. package/dist/commands/deployments/help.d.ts.map +1 -0
  62. package/dist/commands/deployments/index.d.ts +3 -0
  63. package/dist/commands/deployments/index.d.ts.map +1 -0
  64. package/dist/commands/env/command.d.ts +2 -0
  65. package/dist/commands/env/command.d.ts.map +1 -0
  66. package/dist/commands/env/handlers.d.ts +4 -0
  67. package/dist/commands/env/handlers.d.ts.map +1 -0
  68. package/dist/commands/env/help.d.ts +2 -0
  69. package/dist/commands/env/help.d.ts.map +1 -0
  70. package/dist/commands/env/index.d.ts +3 -0
  71. package/dist/commands/env/index.d.ts.map +1 -0
  72. package/dist/commands/firewall-rules/command.d.ts +2 -0
  73. package/dist/commands/firewall-rules/command.d.ts.map +1 -0
  74. package/dist/commands/firewall-rules/handlers.d.ts +4 -0
  75. package/dist/commands/firewall-rules/handlers.d.ts.map +1 -0
  76. package/dist/commands/firewall-rules/help.d.ts +2 -0
  77. package/dist/commands/firewall-rules/help.d.ts.map +1 -0
  78. package/dist/commands/firewall-rules/index.d.ts +3 -0
  79. package/dist/commands/firewall-rules/index.d.ts.map +1 -0
  80. package/dist/commands/monitors/command.d.ts +2 -0
  81. package/dist/commands/monitors/command.d.ts.map +1 -0
  82. package/dist/commands/monitors/handlers.d.ts +6 -0
  83. package/dist/commands/monitors/handlers.d.ts.map +1 -0
  84. package/dist/commands/monitors/help.d.ts +2 -0
  85. package/dist/commands/monitors/help.d.ts.map +1 -0
  86. package/dist/commands/monitors/index.d.ts +3 -0
  87. package/dist/commands/monitors/index.d.ts.map +1 -0
  88. package/dist/commands/nginx/command.d.ts +2 -0
  89. package/dist/commands/nginx/command.d.ts.map +1 -0
  90. package/dist/commands/nginx/handlers.d.ts +4 -0
  91. package/dist/commands/nginx/handlers.d.ts.map +1 -0
  92. package/dist/commands/nginx/help.d.ts +2 -0
  93. package/dist/commands/nginx/help.d.ts.map +1 -0
  94. package/dist/commands/nginx/index.d.ts +3 -0
  95. package/dist/commands/nginx/index.d.ts.map +1 -0
  96. package/dist/commands/nginx-templates/command.d.ts +2 -0
  97. package/dist/commands/nginx-templates/command.d.ts.map +1 -0
  98. package/dist/commands/nginx-templates/handlers.d.ts +7 -0
  99. package/dist/commands/nginx-templates/handlers.d.ts.map +1 -0
  100. package/dist/commands/nginx-templates/help.d.ts +2 -0
  101. package/dist/commands/nginx-templates/help.d.ts.map +1 -0
  102. package/dist/commands/nginx-templates/index.d.ts +3 -0
  103. package/dist/commands/nginx-templates/index.d.ts.map +1 -0
  104. package/dist/commands/recipes/command.d.ts +2 -0
  105. package/dist/commands/recipes/command.d.ts.map +1 -0
  106. package/dist/commands/recipes/handlers.d.ts +5 -0
  107. package/dist/commands/recipes/handlers.d.ts.map +1 -0
  108. package/dist/commands/recipes/help.d.ts +2 -0
  109. package/dist/commands/recipes/help.d.ts.map +1 -0
  110. package/dist/commands/recipes/index.d.ts +3 -0
  111. package/dist/commands/recipes/index.d.ts.map +1 -0
  112. package/dist/commands/redirect-rules/command.d.ts +2 -0
  113. package/dist/commands/redirect-rules/command.d.ts.map +1 -0
  114. package/dist/commands/redirect-rules/handlers.d.ts +6 -0
  115. package/dist/commands/redirect-rules/handlers.d.ts.map +1 -0
  116. package/dist/commands/redirect-rules/help.d.ts +2 -0
  117. package/dist/commands/redirect-rules/help.d.ts.map +1 -0
  118. package/dist/commands/redirect-rules/index.d.ts +3 -0
  119. package/dist/commands/redirect-rules/index.d.ts.map +1 -0
  120. package/dist/commands/scheduled-jobs/command.d.ts +2 -0
  121. package/dist/commands/scheduled-jobs/command.d.ts.map +1 -0
  122. package/dist/commands/scheduled-jobs/handlers.d.ts +6 -0
  123. package/dist/commands/scheduled-jobs/handlers.d.ts.map +1 -0
  124. package/dist/commands/scheduled-jobs/help.d.ts +2 -0
  125. package/dist/commands/scheduled-jobs/help.d.ts.map +1 -0
  126. package/dist/commands/scheduled-jobs/index.d.ts +3 -0
  127. package/dist/commands/scheduled-jobs/index.d.ts.map +1 -0
  128. package/dist/commands/security-rules/command.d.ts +2 -0
  129. package/dist/commands/security-rules/command.d.ts.map +1 -0
  130. package/dist/commands/security-rules/handlers.d.ts +6 -0
  131. package/dist/commands/security-rules/handlers.d.ts.map +1 -0
  132. package/dist/commands/security-rules/help.d.ts +2 -0
  133. package/dist/commands/security-rules/help.d.ts.map +1 -0
  134. package/dist/commands/security-rules/index.d.ts +3 -0
  135. package/dist/commands/security-rules/index.d.ts.map +1 -0
  136. package/dist/commands/servers/command.d.ts +2 -0
  137. package/dist/commands/servers/command.d.ts.map +1 -0
  138. package/dist/commands/servers/handlers.d.ts +18 -0
  139. package/dist/commands/servers/handlers.d.ts.map +1 -0
  140. package/dist/commands/servers/help.d.ts +2 -0
  141. package/dist/commands/servers/help.d.ts.map +1 -0
  142. package/dist/commands/servers/index.d.ts +3 -0
  143. package/dist/commands/servers/index.d.ts.map +1 -0
  144. package/dist/commands/sites/command.d.ts +2 -0
  145. package/dist/commands/sites/command.d.ts.map +1 -0
  146. package/dist/commands/sites/handlers.d.ts +4 -0
  147. package/dist/commands/sites/handlers.d.ts.map +1 -0
  148. package/dist/commands/sites/help.d.ts +2 -0
  149. package/dist/commands/sites/help.d.ts.map +1 -0
  150. package/dist/commands/sites/index.d.ts +3 -0
  151. package/dist/commands/sites/index.d.ts.map +1 -0
  152. package/dist/commands/ssh-keys/command.d.ts +2 -0
  153. package/dist/commands/ssh-keys/command.d.ts.map +1 -0
  154. package/dist/commands/ssh-keys/handlers.d.ts +4 -0
  155. package/dist/commands/ssh-keys/handlers.d.ts.map +1 -0
  156. package/dist/commands/ssh-keys/help.d.ts +2 -0
  157. package/dist/commands/ssh-keys/help.d.ts.map +1 -0
  158. package/dist/commands/ssh-keys/index.d.ts +3 -0
  159. package/dist/commands/ssh-keys/index.d.ts.map +1 -0
  160. package/dist/commands/user/command.d.ts +2 -0
  161. package/dist/commands/user/command.d.ts.map +1 -0
  162. package/dist/commands/user/handlers.d.ts +3 -0
  163. package/dist/commands/user/handlers.d.ts.map +1 -0
  164. package/dist/commands/user/help.d.ts +2 -0
  165. package/dist/commands/user/help.d.ts.map +1 -0
  166. package/dist/commands/user/index.d.ts +3 -0
  167. package/dist/commands/user/index.d.ts.map +1 -0
  168. package/dist/config.d.ts +18 -0
  169. package/dist/config.d.ts.map +1 -0
  170. package/dist/context.d.ts +50 -0
  171. package/dist/context.d.ts.map +1 -0
  172. package/dist/error-handler-BahHMt6t.js +396 -0
  173. package/dist/error-handler-BahHMt6t.js.map +1 -0
  174. package/dist/error-handler.d.ts +39 -0
  175. package/dist/error-handler.d.ts.map +1 -0
  176. package/dist/errors.d.ts +45 -0
  177. package/dist/errors.d.ts.map +1 -0
  178. package/dist/index.d.ts +8 -0
  179. package/dist/index.d.ts.map +1 -0
  180. package/dist/index.js +2 -0
  181. package/dist/output.d.ts +47 -0
  182. package/dist/output.d.ts.map +1 -0
  183. package/dist/types.d.ts +7 -0
  184. package/dist/types.d.ts.map +1 -0
  185. package/dist/utils/args.d.ts +14 -0
  186. package/dist/utils/args.d.ts.map +1 -0
  187. package/dist/utils/colors.d.ts +17 -0
  188. package/dist/utils/colors.d.ts.map +1 -0
  189. package/dist/utils/command-router.d.ts +47 -0
  190. package/dist/utils/command-router.d.ts.map +1 -0
  191. package/dist/utils/resolve.d.ts +25 -0
  192. package/dist/utils/resolve.d.ts.map +1 -0
  193. package/package.json +41 -1
package/dist/cli.js ADDED
@@ -0,0 +1,2746 @@
1
+ #!/usr/bin/env node
2
+ import { a as createContext, c as colors, d as deleteToken, i as runCommand, l as setColorEnabled, m as setToken, n as exitWithValidationError, p as getToken, s as OutputFormatter, u as createConfigStore, v as ValidationError } from "./error-handler-BahHMt6t.js";
3
+ import { activateCertificate, createBackupConfig, createCommand, createDatabaseUser, createMonitor, createNginxTemplate, createRedirectRule, createScheduledJob, createSecurityRule, deleteBackupConfig, deleteDatabaseUser, deleteMonitor, deleteNginxTemplate, deleteRedirectRule, deleteScheduledJob, deleteSecurityRule, deploySite, getBackupConfig, getCertificate, getCommand, getDaemon, getDatabase, getDatabaseUser, getEnv, getFirewallRule, getMonitor, getNginxConfig, getNginxTemplate, getRecipe, getRedirectRule, getScheduledJob, getSecurityRule, getServer, getSite, getSshKey, getUser, listBackupConfigs, listCertificates, listCommands, listDaemons, listDatabaseUsers, listDatabases, listDeployments, listFirewallRules, listMonitors, listNginxTemplates, listRecipes, listRedirectRules, listScheduledJobs, listSecurityRules, listServers, listSites, listSshKeys, rebootServer, restartDaemon, runRecipe, updateEnv, updateNginxConfig, updateNginxTemplate } from "@studiometa/forge-core";
4
+ /**
5
+ * Creates a command router function that dispatches to the appropriate handler.
6
+ */
7
+ function createCommandRouter(config) {
8
+ return async function(subcommand, args, options) {
9
+ const formatter = new OutputFormatter(options.format ?? options.f ?? "human", options["no-color"] === true);
10
+ const ctx = createContext(options);
11
+ const handler = config.handlers[subcommand];
12
+ if (!handler) {
13
+ formatter.error(`Unknown ${config.resource} subcommand: ${subcommand}`);
14
+ process.exit(1);
15
+ return;
16
+ }
17
+ if (Array.isArray(handler)) await handler[0](args, ctx);
18
+ else await handler(ctx);
19
+ };
20
+ }
21
+ /**
22
+ * Smart resolution utilities for --server and --site options.
23
+ *
24
+ * Accepts numeric IDs (used as-is) or plain strings (resolved by name/partial match).
25
+ */
26
+ /**
27
+ * Resolve a --server value to a numeric server ID string.
28
+ *
29
+ * - Numeric string → returned as-is (no API call)
30
+ * - Plain string → fetch all servers, exact match first, then partial match
31
+ * - 0 matches → ValidationError listing available servers
32
+ * - 2+ matches → ValidationError listing ambiguous matches
33
+ */
34
+ async function resolveServerId(value, execCtx) {
35
+ if (/^\d+$/.test(value)) return value;
36
+ const servers = (await listServers({}, execCtx)).data;
37
+ const lower = value.toLowerCase();
38
+ const exact = servers.filter((s) => s.name.toLowerCase() === lower);
39
+ if (exact.length === 1) return String(exact[0].id);
40
+ const partial = servers.filter((s) => s.name.toLowerCase().includes(lower));
41
+ if (partial.length === 0) {
42
+ const available = servers.map((s) => ` ${s.name} (${s.id})`).join("\n");
43
+ throw new ValidationError(`No server found matching "${value}"`, "server", [`Available servers:\n${available}`]);
44
+ }
45
+ if (partial.length > 1) {
46
+ const matches = partial.map((s) => ` ${s.name} (${s.id})`).join("\n");
47
+ throw new ValidationError(`Ambiguous server name "${value}" — ${partial.length} matches found`, "server", [`Matching servers:\n${matches}`, "Use a more specific name or pass the numeric ID"]);
48
+ }
49
+ return String(partial[0].id);
50
+ }
51
+ /**
52
+ * Resolve a --site value to a numeric site ID string.
53
+ *
54
+ * - Numeric string → returned as-is (no API call)
55
+ * - Plain string → fetch all sites for the server, exact domain match first, then partial
56
+ * - 0 matches → ValidationError listing available sites
57
+ * - 2+ matches → ValidationError listing ambiguous matches
58
+ */
59
+ async function resolveSiteId(value, serverId, execCtx) {
60
+ if (/^\d+$/.test(value)) return value;
61
+ const sites = (await listSites({ server_id: serverId }, execCtx)).data;
62
+ const lower = value.toLowerCase();
63
+ const exact = sites.filter((s) => s.name.toLowerCase() === lower);
64
+ if (exact.length === 1) return String(exact[0].id);
65
+ const partial = sites.filter((s) => s.name.toLowerCase().includes(lower));
66
+ if (partial.length === 0) {
67
+ const available = sites.map((s) => ` ${s.name} (${s.id})`).join("\n");
68
+ throw new ValidationError(`No site found matching "${value}" on server ${serverId}`, "site", [`Available sites:\n${available}`]);
69
+ }
70
+ if (partial.length > 1) {
71
+ const matches = partial.map((s) => ` ${s.name} (${s.id})`).join("\n");
72
+ throw new ValidationError(`Ambiguous site name "${value}" — ${partial.length} matches found`, "site", [`Matching sites:\n${matches}`, "Use a more specific name or pass the numeric ID"]);
73
+ }
74
+ return String(partial[0].id);
75
+ }
76
+ async function backupsList(ctx) {
77
+ const server = String(ctx.options.server ?? "");
78
+ if (!server) exitWithValidationError("server_id", "forge-cli backups list --server <server_id>", ctx.formatter);
79
+ await runCommand(async () => {
80
+ const token = ctx.getToken();
81
+ const execCtx = ctx.createExecutorContext(token);
82
+ const result = await listBackupConfigs({ server_id: await resolveServerId(server, execCtx) }, execCtx);
83
+ ctx.formatter.outputList(result.data, [
84
+ "id",
85
+ "provider_name",
86
+ "frequency",
87
+ "status"
88
+ ], "No backup configurations found.", (b) => `${String(b.id).padEnd(8)} ${b.provider_name.padEnd(20)} ${b.frequency.padEnd(12)} ${b.status}`);
89
+ }, ctx.formatter);
90
+ }
91
+ async function backupsGet(args, ctx) {
92
+ const [id] = args;
93
+ const server = String(ctx.options.server ?? "");
94
+ if (!id) exitWithValidationError("backup_id", "forge-cli backups get <backup_id> --server <server_id>", ctx.formatter);
95
+ if (!server) exitWithValidationError("server_id", "forge-cli backups get <backup_id> --server <server_id>", ctx.formatter);
96
+ await runCommand(async () => {
97
+ const token = ctx.getToken();
98
+ const execCtx = ctx.createExecutorContext(token);
99
+ const result = await getBackupConfig({
100
+ server_id: await resolveServerId(server, execCtx),
101
+ id
102
+ }, execCtx);
103
+ ctx.formatter.outputOne(result.data, [
104
+ "id",
105
+ "provider",
106
+ "provider_name",
107
+ "frequency",
108
+ "retention",
109
+ "status",
110
+ "last_backup_time"
111
+ ]);
112
+ }, ctx.formatter);
113
+ }
114
+ async function backupsCreate(ctx) {
115
+ const server = String(ctx.options.server ?? "");
116
+ const provider = String(ctx.options.provider ?? "");
117
+ const frequency = String(ctx.options.frequency ?? "");
118
+ const databasesRaw = ctx.options.databases;
119
+ const databases = Array.isArray(databasesRaw) ? databasesRaw.map(Number) : databasesRaw ? [Number(databasesRaw)] : [];
120
+ if (!server) exitWithValidationError("server_id", "forge-cli backups create --server <server_id> --provider <provider> --frequency <frequency>", ctx.formatter);
121
+ if (!provider) exitWithValidationError("provider", "forge-cli backups create --server <server_id> --provider <provider> --frequency <frequency>", ctx.formatter);
122
+ if (!frequency) exitWithValidationError("frequency", "forge-cli backups create --server <server_id> --provider <provider> --frequency <frequency>", ctx.formatter);
123
+ await runCommand(async () => {
124
+ const token = ctx.getToken();
125
+ const execCtx = ctx.createExecutorContext(token);
126
+ const result = await createBackupConfig({
127
+ server_id: await resolveServerId(server, execCtx),
128
+ provider,
129
+ frequency,
130
+ credentials: {},
131
+ databases
132
+ }, execCtx);
133
+ ctx.formatter.outputOne(result.data, [
134
+ "id",
135
+ "provider",
136
+ "provider_name",
137
+ "frequency",
138
+ "retention",
139
+ "status"
140
+ ]);
141
+ }, ctx.formatter);
142
+ }
143
+ async function backupsDelete(args, ctx) {
144
+ const [id] = args;
145
+ const server = String(ctx.options.server ?? "");
146
+ if (!id) exitWithValidationError("backup_id", "forge-cli backups delete <backup_id> --server <server_id>", ctx.formatter);
147
+ if (!server) exitWithValidationError("server_id", "forge-cli backups delete <backup_id> --server <server_id>", ctx.formatter);
148
+ await runCommand(async () => {
149
+ const token = ctx.getToken();
150
+ const execCtx = ctx.createExecutorContext(token);
151
+ await deleteBackupConfig({
152
+ server_id: await resolveServerId(server, execCtx),
153
+ id
154
+ }, execCtx);
155
+ ctx.formatter.success(`Backup configuration ${id} deleted.`);
156
+ }, ctx.formatter);
157
+ }
158
+ const handleBackupsCommand = createCommandRouter({
159
+ resource: "backups",
160
+ handlers: {
161
+ list: backupsList,
162
+ ls: backupsList,
163
+ get: [backupsGet, "args"],
164
+ create: backupsCreate,
165
+ delete: [backupsDelete, "args"]
166
+ }
167
+ });
168
+ function showBackupsHelp(subcommand) {
169
+ if (subcommand === "list" || subcommand === "ls") console.log(`
170
+ ${colors.bold("forge-cli backups list")} - List backup configurations on a server
171
+
172
+ ${colors.bold("USAGE:")}
173
+ forge-cli backups list --server <server_id> [options]
174
+
175
+ ${colors.bold("OPTIONS:")}
176
+ --server <id> Server ID (required)
177
+ -f, --format <fmt> Output format: json, human, table
178
+ `);
179
+ else if (subcommand === "get") console.log(`
180
+ ${colors.bold("forge-cli backups get")} - Get backup configuration details
181
+
182
+ ${colors.bold("USAGE:")}
183
+ forge-cli backups get <backup_id> --server <server_id> [options]
184
+ `);
185
+ else if (subcommand === "create") console.log(`
186
+ ${colors.bold("forge-cli backups create")} - Create a new backup configuration
187
+
188
+ ${colors.bold("USAGE:")}
189
+ forge-cli backups create --server <server_id> --provider <provider> --frequency <frequency> [options]
190
+
191
+ ${colors.bold("OPTIONS:")}
192
+ --server <id> Server ID (required)
193
+ --provider <name> Backup provider (required)
194
+ --frequency <freq> Backup frequency (required)
195
+ --databases <ids> Comma-separated database IDs to back up (optional)
196
+ -f, --format <fmt> Output format: json, human, table
197
+ `);
198
+ else if (subcommand === "delete") console.log(`
199
+ ${colors.bold("forge-cli backups delete")} - Delete a backup configuration
200
+
201
+ ${colors.bold("USAGE:")}
202
+ forge-cli backups delete <backup_id> --server <server_id> [options]
203
+ `);
204
+ else console.log(`
205
+ ${colors.bold("forge-cli backups")} - Manage backup configurations
206
+
207
+ ${colors.bold("USAGE:")}
208
+ forge-cli backups <subcommand> [options]
209
+
210
+ ${colors.bold("SUBCOMMANDS:")}
211
+ list, ls List backup configurations
212
+ get <id> Get backup configuration details
213
+ create Create a new backup configuration
214
+ delete <id> Delete a backup configuration
215
+
216
+ ${colors.bold("OPTIONS:")}
217
+ --server <id> Server ID (required)
218
+ -f, --format <fmt> Output format: json, human, table
219
+
220
+ Run ${colors.cyan("forge-cli backups <subcommand> --help")} for details.
221
+ `);
222
+ }
223
+ function requireServerAndSiteRaw$3(ctx, usage) {
224
+ const server = String(ctx.options.server ?? "");
225
+ const site = String(ctx.options.site ?? "");
226
+ if (!server) exitWithValidationError("server_id", usage, ctx.formatter);
227
+ if (!site) exitWithValidationError("site_id", usage, ctx.formatter);
228
+ return {
229
+ server,
230
+ site
231
+ };
232
+ }
233
+ async function certificatesList(ctx) {
234
+ const { server, site } = requireServerAndSiteRaw$3(ctx, "forge-cli certificates list --server <server_id> --site <site_id>");
235
+ await runCommand(async () => {
236
+ const token = ctx.getToken();
237
+ const execCtx = ctx.createExecutorContext(token);
238
+ const server_id = await resolveServerId(server, execCtx);
239
+ const result = await listCertificates({
240
+ server_id,
241
+ site_id: await resolveSiteId(site, server_id, execCtx)
242
+ }, execCtx);
243
+ ctx.formatter.outputList(result.data, [
244
+ "id",
245
+ "domain",
246
+ "type",
247
+ "status",
248
+ "active"
249
+ ], "No certificates found.", (c) => `${String(c.id).padEnd(8)} ${c.domain.padEnd(40)} ${c.type.padEnd(10)} ${c.status.padEnd(12)} ${c.active ? "active" : "—"}`);
250
+ }, ctx.formatter);
251
+ }
252
+ async function certificatesGet(args, ctx) {
253
+ const [id] = args;
254
+ const usage = "forge-cli certificates get <cert_id> --server <server_id> --site <site_id>";
255
+ if (!id) exitWithValidationError("certificate_id", usage, ctx.formatter);
256
+ const { server, site } = requireServerAndSiteRaw$3(ctx, usage);
257
+ await runCommand(async () => {
258
+ const token = ctx.getToken();
259
+ const execCtx = ctx.createExecutorContext(token);
260
+ const server_id = await resolveServerId(server, execCtx);
261
+ const result = await getCertificate({
262
+ server_id,
263
+ site_id: await resolveSiteId(site, server_id, execCtx),
264
+ id
265
+ }, execCtx);
266
+ ctx.formatter.outputOne(result.data, [
267
+ "id",
268
+ "domain",
269
+ "type",
270
+ "status",
271
+ "request_status",
272
+ "active",
273
+ "existing",
274
+ "created_at"
275
+ ]);
276
+ }, ctx.formatter);
277
+ }
278
+ async function certificatesActivate(args, ctx) {
279
+ const [id] = args;
280
+ const usage = "forge-cli certificates activate <cert_id> --server <server_id> --site <site_id>";
281
+ if (!id) exitWithValidationError("certificate_id", usage, ctx.formatter);
282
+ const { server, site } = requireServerAndSiteRaw$3(ctx, usage);
283
+ await runCommand(async () => {
284
+ const token = ctx.getToken();
285
+ const execCtx = ctx.createExecutorContext(token);
286
+ const server_id = await resolveServerId(server, execCtx);
287
+ await activateCertificate({
288
+ server_id,
289
+ site_id: await resolveSiteId(site, server_id, execCtx),
290
+ id
291
+ }, execCtx);
292
+ ctx.formatter.success(`Certificate ${id} activated.`);
293
+ }, ctx.formatter);
294
+ }
295
+ const handleCertificatesCommand = createCommandRouter({
296
+ resource: "certificates",
297
+ handlers: {
298
+ list: certificatesList,
299
+ ls: certificatesList,
300
+ get: [certificatesGet, "args"],
301
+ activate: [certificatesActivate, "args"]
302
+ }
303
+ });
304
+ function showCertificatesHelp(subcommand) {
305
+ if (subcommand === "list" || subcommand === "ls") console.log(`
306
+ ${colors.bold("forge-cli certificates list")} - List certificates for a site
307
+
308
+ ${colors.bold("USAGE:")}
309
+ forge-cli certificates list --server <server_id> --site <site_id> [options]
310
+ `);
311
+ else if (subcommand === "get") console.log(`
312
+ ${colors.bold("forge-cli certificates get")} - Get certificate details
313
+
314
+ ${colors.bold("USAGE:")}
315
+ forge-cli certificates get <cert_id> --server <server_id> --site <site_id>
316
+ `);
317
+ else if (subcommand === "activate") console.log(`
318
+ ${colors.bold("forge-cli certificates activate")} - Activate a certificate
319
+
320
+ ${colors.bold("USAGE:")}
321
+ forge-cli certificates activate <cert_id> --server <server_id> --site <site_id>
322
+ `);
323
+ else console.log(`
324
+ ${colors.bold("forge-cli certificates")} - Manage SSL certificates
325
+
326
+ ${colors.bold("USAGE:")}
327
+ forge-cli certificates <subcommand> [options]
328
+
329
+ ${colors.bold("ALIASES:")}
330
+ forge-cli certs
331
+
332
+ ${colors.bold("SUBCOMMANDS:")}
333
+ list, ls List certificates
334
+ get <id> Get certificate details
335
+ activate <id> Activate a certificate
336
+
337
+ ${colors.bold("OPTIONS:")}
338
+ --server <id> Server ID (required)
339
+ --site <id> Site ID (required)
340
+ -f, --format <fmt> Output format: json, human, table
341
+
342
+ Run ${colors.cyan("forge-cli certificates <subcommand> --help")} for details.
343
+ `);
344
+ }
345
+ function requireServerAndSiteRaw$2(ctx, usage) {
346
+ const server = String(ctx.options.server ?? "");
347
+ const site = String(ctx.options.site ?? "");
348
+ if (!server) exitWithValidationError("server_id", usage, ctx.formatter);
349
+ if (!site) exitWithValidationError("site_id", usage, ctx.formatter);
350
+ return {
351
+ server,
352
+ site
353
+ };
354
+ }
355
+ async function commandsList(ctx) {
356
+ const { server, site } = requireServerAndSiteRaw$2(ctx, "forge-cli commands list --server <server_id> --site <site_id>");
357
+ await runCommand(async () => {
358
+ const token = ctx.getToken();
359
+ const execCtx = ctx.createExecutorContext(token);
360
+ const server_id = await resolveServerId(server, execCtx);
361
+ const result = await listCommands({
362
+ server_id,
363
+ site_id: await resolveSiteId(site, server_id, execCtx)
364
+ }, execCtx);
365
+ ctx.formatter.outputList(result.data, [
366
+ "id",
367
+ "command",
368
+ "status",
369
+ "created_at"
370
+ ], "No commands found.", (c) => `${String(c.id).padEnd(8)} ${c.command.padEnd(50)} ${c.status.padEnd(12)} ${c.created_at}`);
371
+ }, ctx.formatter);
372
+ }
373
+ async function commandsGet(args, ctx) {
374
+ const [id] = args;
375
+ const usage = "forge-cli commands get <command_id> --server <server_id> --site <site_id>";
376
+ if (!id) exitWithValidationError("command_id", usage, ctx.formatter);
377
+ const { server, site } = requireServerAndSiteRaw$2(ctx, usage);
378
+ await runCommand(async () => {
379
+ const token = ctx.getToken();
380
+ const execCtx = ctx.createExecutorContext(token);
381
+ const server_id = await resolveServerId(server, execCtx);
382
+ const result = await getCommand({
383
+ server_id,
384
+ site_id: await resolveSiteId(site, server_id, execCtx),
385
+ id
386
+ }, execCtx);
387
+ ctx.formatter.outputOne(result.data, [
388
+ "id",
389
+ "command",
390
+ "status",
391
+ "user_name",
392
+ "created_at"
393
+ ]);
394
+ }, ctx.formatter);
395
+ }
396
+ async function commandsCreate(ctx) {
397
+ const usage = "forge-cli commands create --server <server_id> --site <site_id> --command <command>";
398
+ const { server, site } = requireServerAndSiteRaw$2(ctx, usage);
399
+ const command = String(ctx.options.command ?? "");
400
+ if (!command) exitWithValidationError("command", usage, ctx.formatter);
401
+ await runCommand(async () => {
402
+ const token = ctx.getToken();
403
+ const execCtx = ctx.createExecutorContext(token);
404
+ const server_id = await resolveServerId(server, execCtx);
405
+ const result = await createCommand({
406
+ server_id,
407
+ site_id: await resolveSiteId(site, server_id, execCtx),
408
+ command
409
+ }, execCtx);
410
+ ctx.formatter.outputOne(result.data, [
411
+ "id",
412
+ "command",
413
+ "status",
414
+ "created_at"
415
+ ]);
416
+ }, ctx.formatter);
417
+ }
418
+ const handleCommandsCommand = createCommandRouter({
419
+ resource: "commands",
420
+ handlers: {
421
+ list: commandsList,
422
+ ls: commandsList,
423
+ get: [commandsGet, "args"],
424
+ create: commandsCreate
425
+ }
426
+ });
427
+ function showCommandsHelp(subcommand) {
428
+ if (subcommand === "list" || subcommand === "ls") console.log(`
429
+ ${colors.bold("forge-cli commands list")} - List commands executed on a site
430
+
431
+ ${colors.bold("USAGE:")}
432
+ forge-cli commands list --server <server_id> --site <site_id> [options]
433
+
434
+ ${colors.bold("OPTIONS:")}
435
+ --server <id> Server ID (required)
436
+ --site <id> Site ID (required)
437
+ -f, --format <fmt> Output format: json, human, table
438
+ `);
439
+ else if (subcommand === "get") console.log(`
440
+ ${colors.bold("forge-cli commands get")} - Get command details
441
+
442
+ ${colors.bold("USAGE:")}
443
+ forge-cli commands get <command_id> --server <server_id> --site <site_id> [options]
444
+ `);
445
+ else if (subcommand === "create") console.log(`
446
+ ${colors.bold("forge-cli commands create")} - Execute a command on a site
447
+
448
+ ${colors.bold("USAGE:")}
449
+ forge-cli commands create --server <server_id> --site <site_id> --command <command> [options]
450
+
451
+ ${colors.bold("OPTIONS:")}
452
+ --server <id> Server ID (required)
453
+ --site <id> Site ID (required)
454
+ --command <cmd> Command to execute (required)
455
+ -f, --format <fmt> Output format: json, human, table
456
+ `);
457
+ else console.log(`
458
+ ${colors.bold("forge-cli commands")} - Manage site commands
459
+
460
+ ${colors.bold("USAGE:")}
461
+ forge-cli commands <subcommand> [options]
462
+
463
+ ${colors.bold("SUBCOMMANDS:")}
464
+ list, ls List commands
465
+ get <id> Get command details
466
+ create Execute a command on a site
467
+
468
+ ${colors.bold("OPTIONS:")}
469
+ --server <id> Server ID (required)
470
+ --site <id> Site ID (required)
471
+ -f, --format <fmt> Output format: json, human, table
472
+
473
+ Run ${colors.cyan("forge-cli commands <subcommand> --help")} for details.
474
+ `);
475
+ }
476
+ /**
477
+ * Config command — manage forge-cli configuration
478
+ */
479
+ function showConfigHelp() {
480
+ console.log(`
481
+ ${colors.bold("forge-cli config")} - Manage CLI configuration
482
+
483
+ ${colors.bold("USAGE:")}
484
+ forge-cli config <subcommand> [options]
485
+
486
+ ${colors.bold("SUBCOMMANDS:")}
487
+ set <token> Save API token to config file
488
+ get Show current API token (masked)
489
+ delete Delete stored API token
490
+
491
+ ${colors.bold("EXAMPLES:")}
492
+ forge-cli config set YOUR_FORGE_TOKEN
493
+ forge-cli config get
494
+ forge-cli config delete
495
+ `);
496
+ }
497
+ function handleConfigCommand(subcommand, args, options) {
498
+ const format = String(options.format ?? options.f ?? "human");
499
+ const formatter = new OutputFormatter(format, options["no-color"] === true);
500
+ const store = createConfigStore();
501
+ switch (subcommand) {
502
+ case "set": {
503
+ const [token] = args;
504
+ if (!token) {
505
+ formatter.error("Token is required. Usage: forge-cli config set <token>");
506
+ process.exit(1);
507
+ return;
508
+ }
509
+ setToken(token, store);
510
+ formatter.success("API token saved.");
511
+ break;
512
+ }
513
+ case "get": {
514
+ const token = getToken(store);
515
+ if (!token) {
516
+ formatter.warning("No API token configured.");
517
+ formatter.info("Run: forge-cli config set <token>");
518
+ } else {
519
+ const masked = token.length > 8 ? `${token.slice(0, 4)}${"*".repeat(token.length - 8)}${token.slice(-4)}` : "****";
520
+ if (format === "json") formatter.output({ apiToken: masked });
521
+ else console.log(`apiToken: ${masked}`);
522
+ }
523
+ break;
524
+ }
525
+ case "delete":
526
+ deleteToken(store);
527
+ formatter.success("API token deleted.");
528
+ break;
529
+ default:
530
+ showConfigHelp();
531
+ break;
532
+ }
533
+ }
534
+ async function daemonsList(ctx) {
535
+ const server = String(ctx.options.server ?? "");
536
+ if (!server) exitWithValidationError("server_id", "forge-cli daemons list --server <server_id>", ctx.formatter);
537
+ await runCommand(async () => {
538
+ const token = ctx.getToken();
539
+ const execCtx = ctx.createExecutorContext(token);
540
+ const result = await listDaemons({ server_id: await resolveServerId(server, execCtx) }, execCtx);
541
+ ctx.formatter.outputList(result.data, [
542
+ "id",
543
+ "command",
544
+ "user",
545
+ "status"
546
+ ], "No daemons found.", (d) => `${String(d.id).padEnd(8)} ${d.command.padEnd(50)} ${d.user.padEnd(16)} ${d.status}`);
547
+ }, ctx.formatter);
548
+ }
549
+ async function daemonsGet(args, ctx) {
550
+ const [id] = args;
551
+ const server = String(ctx.options.server ?? "");
552
+ if (!id) exitWithValidationError("daemon_id", "forge-cli daemons get <daemon_id> --server <server_id>", ctx.formatter);
553
+ if (!server) exitWithValidationError("server_id", "forge-cli daemons get <daemon_id> --server <server_id>", ctx.formatter);
554
+ await runCommand(async () => {
555
+ const token = ctx.getToken();
556
+ const execCtx = ctx.createExecutorContext(token);
557
+ const result = await getDaemon({
558
+ server_id: await resolveServerId(server, execCtx),
559
+ id
560
+ }, execCtx);
561
+ ctx.formatter.outputOne(result.data, [
562
+ "id",
563
+ "command",
564
+ "user",
565
+ "directory",
566
+ "processes",
567
+ "status",
568
+ "created_at"
569
+ ]);
570
+ }, ctx.formatter);
571
+ }
572
+ async function daemonsRestart(args, ctx) {
573
+ const [id] = args;
574
+ const server = String(ctx.options.server ?? "");
575
+ if (!id) exitWithValidationError("daemon_id", "forge-cli daemons restart <daemon_id> --server <server_id>", ctx.formatter);
576
+ if (!server) exitWithValidationError("server_id", "forge-cli daemons restart <daemon_id> --server <server_id>", ctx.formatter);
577
+ await runCommand(async () => {
578
+ const token = ctx.getToken();
579
+ const execCtx = ctx.createExecutorContext(token);
580
+ await restartDaemon({
581
+ server_id: await resolveServerId(server, execCtx),
582
+ id
583
+ }, execCtx);
584
+ ctx.formatter.success(`Daemon ${id} restarted.`);
585
+ }, ctx.formatter);
586
+ }
587
+ const handleDaemonsCommand = createCommandRouter({
588
+ resource: "daemons",
589
+ handlers: {
590
+ list: daemonsList,
591
+ ls: daemonsList,
592
+ get: [daemonsGet, "args"],
593
+ restart: [daemonsRestart, "args"]
594
+ }
595
+ });
596
+ function showDaemonsHelp(subcommand) {
597
+ if (subcommand === "list" || subcommand === "ls") console.log(`
598
+ ${colors.bold("forge-cli daemons list")} - List daemons on a server
599
+
600
+ ${colors.bold("USAGE:")}
601
+ forge-cli daemons list --server <server_id> [options]
602
+ `);
603
+ else if (subcommand === "get") console.log(`
604
+ ${colors.bold("forge-cli daemons get")} - Get daemon details
605
+
606
+ ${colors.bold("USAGE:")}
607
+ forge-cli daemons get <daemon_id> --server <server_id>
608
+ `);
609
+ else if (subcommand === "restart") console.log(`
610
+ ${colors.bold("forge-cli daemons restart")} - Restart a daemon
611
+
612
+ ${colors.bold("USAGE:")}
613
+ forge-cli daemons restart <daemon_id> --server <server_id>
614
+ `);
615
+ else console.log(`
616
+ ${colors.bold("forge-cli daemons")} - Manage daemons
617
+
618
+ ${colors.bold("USAGE:")}
619
+ forge-cli daemons <subcommand> [options]
620
+
621
+ ${colors.bold("SUBCOMMANDS:")}
622
+ list, ls List daemons
623
+ get <id> Get daemon details
624
+ restart <id> Restart a daemon
625
+
626
+ ${colors.bold("OPTIONS:")}
627
+ --server <id> Server ID (required)
628
+ -f, --format <fmt> Output format: json, human, table
629
+
630
+ Run ${colors.cyan("forge-cli daemons <subcommand> --help")} for details.
631
+ `);
632
+ }
633
+ async function databasesList(ctx) {
634
+ const server = String(ctx.options.server ?? "");
635
+ if (!server) exitWithValidationError("server_id", "forge-cli databases list --server <server_id>", ctx.formatter);
636
+ await runCommand(async () => {
637
+ const token = ctx.getToken();
638
+ const execCtx = ctx.createExecutorContext(token);
639
+ const result = await listDatabases({ server_id: await resolveServerId(server, execCtx) }, execCtx);
640
+ ctx.formatter.outputList(result.data, [
641
+ "id",
642
+ "name",
643
+ "status"
644
+ ], "No databases found.", (db) => `${String(db.id).padEnd(8)} ${db.name.padEnd(30)} ${db.status}`);
645
+ }, ctx.formatter);
646
+ }
647
+ async function databasesGet(args, ctx) {
648
+ const [id] = args;
649
+ const server = String(ctx.options.server ?? "");
650
+ if (!id) exitWithValidationError("database_id", "forge-cli databases get <database_id> --server <server_id>", ctx.formatter);
651
+ if (!server) exitWithValidationError("server_id", "forge-cli databases get <database_id> --server <server_id>", ctx.formatter);
652
+ await runCommand(async () => {
653
+ const token = ctx.getToken();
654
+ const execCtx = ctx.createExecutorContext(token);
655
+ const result = await getDatabase({
656
+ server_id: await resolveServerId(server, execCtx),
657
+ id
658
+ }, execCtx);
659
+ ctx.formatter.outputOne(result.data, [
660
+ "id",
661
+ "name",
662
+ "status",
663
+ "created_at"
664
+ ]);
665
+ }, ctx.formatter);
666
+ }
667
+ const handleDatabasesCommand = createCommandRouter({
668
+ resource: "databases",
669
+ handlers: {
670
+ list: databasesList,
671
+ ls: databasesList,
672
+ get: [databasesGet, "args"]
673
+ }
674
+ });
675
+ function showDatabasesHelp(subcommand) {
676
+ if (subcommand === "list" || subcommand === "ls") console.log(`
677
+ ${colors.bold("forge-cli databases list")} - List databases on a server
678
+
679
+ ${colors.bold("USAGE:")}
680
+ forge-cli databases list --server <server_id> [options]
681
+
682
+ ${colors.bold("OPTIONS:")}
683
+ --server <id> Server ID (required)
684
+ -f, --format <fmt> Output format: json, human, table
685
+ `);
686
+ else if (subcommand === "get") console.log(`
687
+ ${colors.bold("forge-cli databases get")} - Get database details
688
+
689
+ ${colors.bold("USAGE:")}
690
+ forge-cli databases get <database_id> --server <server_id> [options]
691
+ `);
692
+ else console.log(`
693
+ ${colors.bold("forge-cli databases")} - Manage databases
694
+
695
+ ${colors.bold("USAGE:")}
696
+ forge-cli databases <subcommand> [options]
697
+
698
+ ${colors.bold("ALIASES:")}
699
+ forge-cli db
700
+
701
+ ${colors.bold("SUBCOMMANDS:")}
702
+ list, ls List databases
703
+ get <id> Get database details
704
+
705
+ ${colors.bold("OPTIONS:")}
706
+ --server <id> Server ID (required)
707
+ -f, --format <fmt> Output format: json, human, table
708
+
709
+ Run ${colors.cyan("forge-cli databases <subcommand> --help")} for details.
710
+ `);
711
+ }
712
+ async function databaseUsersList(ctx) {
713
+ const server = String(ctx.options.server ?? "");
714
+ if (!server) exitWithValidationError("server_id", "forge-cli database-users list --server <server_id>", ctx.formatter);
715
+ await runCommand(async () => {
716
+ const token = ctx.getToken();
717
+ const execCtx = ctx.createExecutorContext(token);
718
+ const result = await listDatabaseUsers({ server_id: await resolveServerId(server, execCtx) }, execCtx);
719
+ ctx.formatter.outputList(result.data, [
720
+ "id",
721
+ "name",
722
+ "status"
723
+ ], "No database users found.", (u) => `${String(u.id).padEnd(8)} ${u.name.padEnd(30)} ${u.status}`);
724
+ }, ctx.formatter);
725
+ }
726
+ async function databaseUsersGet(args, ctx) {
727
+ const [id] = args;
728
+ const server = String(ctx.options.server ?? "");
729
+ if (!id) exitWithValidationError("user_id", "forge-cli database-users get <user_id> --server <server_id>", ctx.formatter);
730
+ if (!server) exitWithValidationError("server_id", "forge-cli database-users get <user_id> --server <server_id>", ctx.formatter);
731
+ await runCommand(async () => {
732
+ const token = ctx.getToken();
733
+ const execCtx = ctx.createExecutorContext(token);
734
+ const result = await getDatabaseUser({
735
+ server_id: await resolveServerId(server, execCtx),
736
+ id
737
+ }, execCtx);
738
+ ctx.formatter.outputOne(result.data, [
739
+ "id",
740
+ "name",
741
+ "status",
742
+ "databases",
743
+ "created_at"
744
+ ]);
745
+ }, ctx.formatter);
746
+ }
747
+ async function databaseUsersCreate(ctx) {
748
+ const server = String(ctx.options.server ?? "");
749
+ const name = String(ctx.options.name ?? "");
750
+ const password = String(ctx.options.password ?? "");
751
+ const databasesRaw = ctx.options.databases;
752
+ const databases = Array.isArray(databasesRaw) ? databasesRaw.map(Number) : databasesRaw ? [Number(databasesRaw)] : void 0;
753
+ if (!server) exitWithValidationError("server_id", "forge-cli database-users create --server <server_id> --name <name> --password <password>", ctx.formatter);
754
+ if (!name) exitWithValidationError("name", "forge-cli database-users create --server <server_id> --name <name> --password <password>", ctx.formatter);
755
+ if (!password) exitWithValidationError("password", "forge-cli database-users create --server <server_id> --name <name> --password <password>", ctx.formatter);
756
+ await runCommand(async () => {
757
+ const token = ctx.getToken();
758
+ const execCtx = ctx.createExecutorContext(token);
759
+ const result = await createDatabaseUser({
760
+ server_id: await resolveServerId(server, execCtx),
761
+ name,
762
+ password,
763
+ ...databases ? { databases } : {}
764
+ }, execCtx);
765
+ ctx.formatter.outputOne(result.data, [
766
+ "id",
767
+ "name",
768
+ "status",
769
+ "databases",
770
+ "created_at"
771
+ ]);
772
+ }, ctx.formatter);
773
+ }
774
+ async function databaseUsersDelete(args, ctx) {
775
+ const [id] = args;
776
+ const server = String(ctx.options.server ?? "");
777
+ if (!id) exitWithValidationError("user_id", "forge-cli database-users delete <user_id> --server <server_id>", ctx.formatter);
778
+ if (!server) exitWithValidationError("server_id", "forge-cli database-users delete <user_id> --server <server_id>", ctx.formatter);
779
+ await runCommand(async () => {
780
+ const token = ctx.getToken();
781
+ const execCtx = ctx.createExecutorContext(token);
782
+ await deleteDatabaseUser({
783
+ server_id: await resolveServerId(server, execCtx),
784
+ id
785
+ }, execCtx);
786
+ ctx.formatter.success(`Database user ${id} deleted.`);
787
+ }, ctx.formatter);
788
+ }
789
+ const handleDatabaseUsersCommand = createCommandRouter({
790
+ resource: "database-users",
791
+ handlers: {
792
+ list: databaseUsersList,
793
+ ls: databaseUsersList,
794
+ get: [databaseUsersGet, "args"],
795
+ create: databaseUsersCreate,
796
+ delete: [databaseUsersDelete, "args"]
797
+ }
798
+ });
799
+ function showDatabaseUsersHelp(subcommand) {
800
+ if (subcommand === "list" || subcommand === "ls") console.log(`
801
+ ${colors.bold("forge-cli database-users list")} - List database users on a server
802
+
803
+ ${colors.bold("USAGE:")}
804
+ forge-cli database-users list --server <server_id> [options]
805
+
806
+ ${colors.bold("OPTIONS:")}
807
+ --server <id> Server ID (required)
808
+ -f, --format <fmt> Output format: json, human, table
809
+ `);
810
+ else if (subcommand === "get") console.log(`
811
+ ${colors.bold("forge-cli database-users get")} - Get database user details
812
+
813
+ ${colors.bold("USAGE:")}
814
+ forge-cli database-users get <user_id> --server <server_id> [options]
815
+ `);
816
+ else if (subcommand === "create") console.log(`
817
+ ${colors.bold("forge-cli database-users create")} - Create a new database user
818
+
819
+ ${colors.bold("USAGE:")}
820
+ forge-cli database-users create --server <server_id> --name <name> --password <password> [options]
821
+
822
+ ${colors.bold("OPTIONS:")}
823
+ --server <id> Server ID (required)
824
+ --name <name> Database user name (required)
825
+ --password <pass> User password (required)
826
+ --databases <ids> Comma-separated database IDs to grant access to (optional)
827
+ -f, --format <fmt> Output format: json, human, table
828
+ `);
829
+ else if (subcommand === "delete") console.log(`
830
+ ${colors.bold("forge-cli database-users delete")} - Delete a database user
831
+
832
+ ${colors.bold("USAGE:")}
833
+ forge-cli database-users delete <user_id> --server <server_id> [options]
834
+ `);
835
+ else console.log(`
836
+ ${colors.bold("forge-cli database-users")} - Manage database users
837
+
838
+ ${colors.bold("USAGE:")}
839
+ forge-cli database-users <subcommand> [options]
840
+
841
+ ${colors.bold("SUBCOMMANDS:")}
842
+ list, ls List database users
843
+ get <id> Get database user details
844
+ create Create a new database user
845
+ delete <id> Delete a database user
846
+
847
+ ${colors.bold("OPTIONS:")}
848
+ --server <id> Server ID (required)
849
+ -f, --format <fmt> Output format: json, human, table
850
+
851
+ Run ${colors.cyan("forge-cli database-users <subcommand> --help")} for details.
852
+ `);
853
+ }
854
+ async function deploymentsList(ctx) {
855
+ const server = String(ctx.options.server ?? "");
856
+ const site = String(ctx.options.site ?? "");
857
+ if (!server) exitWithValidationError("server_id", "forge-cli deployments list --server <server_id> --site <site_id>", ctx.formatter);
858
+ if (!site) exitWithValidationError("site_id", "forge-cli deployments list --server <server_id> --site <site_id>", ctx.formatter);
859
+ await runCommand(async () => {
860
+ const token = ctx.getToken();
861
+ const execCtx = ctx.createExecutorContext(token);
862
+ const server_id = await resolveServerId(server, execCtx);
863
+ const result = await listDeployments({
864
+ server_id,
865
+ site_id: await resolveSiteId(site, server_id, execCtx)
866
+ }, execCtx);
867
+ ctx.formatter.outputList(result.data, [
868
+ "id",
869
+ "status",
870
+ "commit_hash",
871
+ "started_at"
872
+ ], "No deployments found.", (d) => `${String(d.id).padEnd(8)} ${d.status.padEnd(12)} ${(d.commit_hash ?? "—").padEnd(10)} ${d.started_at}`);
873
+ }, ctx.formatter);
874
+ }
875
+ async function deploymentsDeploy(ctx) {
876
+ const server = String(ctx.options.server ?? "");
877
+ const site = String(ctx.options.site ?? "");
878
+ if (!server) exitWithValidationError("server_id", "forge-cli deployments deploy --server <server_id> --site <site_id>", ctx.formatter);
879
+ if (!site) exitWithValidationError("site_id", "forge-cli deployments deploy --server <server_id> --site <site_id>", ctx.formatter);
880
+ await runCommand(async () => {
881
+ const token = ctx.getToken();
882
+ const execCtx = ctx.createExecutorContext(token);
883
+ const server_id = await resolveServerId(server, execCtx);
884
+ const site_id = await resolveSiteId(site, server_id, execCtx);
885
+ await deploySite({
886
+ server_id,
887
+ site_id
888
+ }, execCtx);
889
+ ctx.formatter.success(`Deployment triggered for site ${site_id}.`);
890
+ }, ctx.formatter);
891
+ }
892
+ const handleDeploymentsCommand = createCommandRouter({
893
+ resource: "deployments",
894
+ handlers: {
895
+ list: deploymentsList,
896
+ ls: deploymentsList,
897
+ deploy: deploymentsDeploy
898
+ }
899
+ });
900
+ function showDeploymentsHelp(subcommand) {
901
+ if (subcommand === "list" || subcommand === "ls") console.log(`
902
+ ${colors.bold("forge-cli deployments list")} - List deployments for a site
903
+
904
+ ${colors.bold("USAGE:")}
905
+ forge-cli deployments list --server <server_id> --site <site_id> [options]
906
+
907
+ ${colors.bold("OPTIONS:")}
908
+ --server <id> Server ID (required)
909
+ --site <id> Site ID (required)
910
+ -f, --format <fmt> Output format: json, human, table
911
+ `);
912
+ else if (subcommand === "deploy") console.log(`
913
+ ${colors.bold("forge-cli deployments deploy")} - Trigger a deployment
914
+
915
+ ${colors.bold("USAGE:")}
916
+ forge-cli deployments deploy --server <server_id> --site <site_id>
917
+ `);
918
+ else console.log(`
919
+ ${colors.bold("forge-cli deployments")} - Manage deployments
920
+
921
+ ${colors.bold("USAGE:")}
922
+ forge-cli deployments <subcommand> [options]
923
+
924
+ ${colors.bold("ALIASES:")}
925
+ forge-cli d
926
+
927
+ ${colors.bold("SUBCOMMANDS:")}
928
+ list, ls List deployments for a site
929
+ deploy Trigger a deployment
930
+
931
+ ${colors.bold("OPTIONS:")}
932
+ --server <id> Server ID (required)
933
+ --site <id> Site ID (required)
934
+ -f, --format <fmt> Output format: json, human, table
935
+
936
+ Run ${colors.cyan("forge-cli deployments <subcommand> --help")} for details.
937
+ `);
938
+ }
939
+ async function envGet(ctx) {
940
+ const server = String(ctx.options.server ?? "");
941
+ const site = String(ctx.options.site ?? "");
942
+ if (!server) exitWithValidationError("server_id", "forge-cli env get --server <server_id> --site <site_id>", ctx.formatter);
943
+ if (!site) exitWithValidationError("site_id", "forge-cli env get --server <server_id> --site <site_id>", ctx.formatter);
944
+ await runCommand(async () => {
945
+ const token = ctx.getToken();
946
+ const execCtx = ctx.createExecutorContext(token);
947
+ const server_id = await resolveServerId(server, execCtx);
948
+ const result = await getEnv({
949
+ server_id,
950
+ site_id: await resolveSiteId(site, server_id, execCtx)
951
+ }, execCtx);
952
+ if (ctx.formatter.isJson()) console.log(JSON.stringify({ content: result.data }));
953
+ else console.log(result.data);
954
+ }, ctx.formatter);
955
+ }
956
+ async function envUpdate(ctx) {
957
+ const server = String(ctx.options.server ?? "");
958
+ const site = String(ctx.options.site ?? "");
959
+ const content = String(ctx.options.content ?? "");
960
+ if (!server) exitWithValidationError("server_id", "forge-cli env update --server <server_id> --site <site_id> --content <content>", ctx.formatter);
961
+ if (!site) exitWithValidationError("site_id", "forge-cli env update --server <server_id> --site <site_id> --content <content>", ctx.formatter);
962
+ if (!content) exitWithValidationError("content", "forge-cli env update --server <server_id> --site <site_id> --content <content>", ctx.formatter);
963
+ await runCommand(async () => {
964
+ const token = ctx.getToken();
965
+ const execCtx = ctx.createExecutorContext(token);
966
+ const server_id = await resolveServerId(server, execCtx);
967
+ await updateEnv({
968
+ server_id,
969
+ site_id: await resolveSiteId(site, server_id, execCtx),
970
+ content
971
+ }, execCtx);
972
+ ctx.formatter.success("Environment variables updated.");
973
+ }, ctx.formatter);
974
+ }
975
+ const handleEnvCommand = createCommandRouter({
976
+ resource: "env",
977
+ handlers: {
978
+ get: envGet,
979
+ update: envUpdate
980
+ }
981
+ });
982
+ function showEnvHelp(subcommand) {
983
+ if (subcommand === "get") console.log(`
984
+ ${colors.bold("forge-cli env get")} - Get environment variables
985
+
986
+ ${colors.bold("USAGE:")}
987
+ forge-cli env get --server <server_id> --site <site_id>
988
+ `);
989
+ else if (subcommand === "update") console.log(`
990
+ ${colors.bold("forge-cli env update")} - Update environment variables
991
+
992
+ ${colors.bold("USAGE:")}
993
+ forge-cli env update --server <server_id> --site <site_id> --content <env_content>
994
+ `);
995
+ else console.log(`
996
+ ${colors.bold("forge-cli env")} - Manage environment variables
997
+
998
+ ${colors.bold("USAGE:")}
999
+ forge-cli env <subcommand> [options]
1000
+
1001
+ ${colors.bold("SUBCOMMANDS:")}
1002
+ get Get environment variables
1003
+ update Update environment variables
1004
+
1005
+ ${colors.bold("OPTIONS:")}
1006
+ --server <id> Server ID (required)
1007
+ --site <id> Site ID (required)
1008
+ --content <str> New env content (for update)
1009
+ -f, --format <fmt> Output format: json, human
1010
+
1011
+ Run ${colors.cyan("forge-cli env <subcommand> --help")} for details.
1012
+ `);
1013
+ }
1014
+ async function firewallRulesList(ctx) {
1015
+ const server = String(ctx.options.server ?? "");
1016
+ if (!server) exitWithValidationError("server_id", "forge-cli firewall-rules list --server <server_id>", ctx.formatter);
1017
+ await runCommand(async () => {
1018
+ const token = ctx.getToken();
1019
+ const execCtx = ctx.createExecutorContext(token);
1020
+ const result = await listFirewallRules({ server_id: await resolveServerId(server, execCtx) }, execCtx);
1021
+ ctx.formatter.outputList(result.data, [
1022
+ "id",
1023
+ "name",
1024
+ "port",
1025
+ "type",
1026
+ "ip_address",
1027
+ "status"
1028
+ ], "No firewall rules found.", (r) => `${String(r.id).padEnd(8)} ${r.name.padEnd(20)} ${String(r.port).padEnd(8)} ${r.type.padEnd(8)} ${(r.ip_address ?? "any").padEnd(16)} ${r.status}`);
1029
+ }, ctx.formatter);
1030
+ }
1031
+ async function firewallRulesGet(args, ctx) {
1032
+ const [id] = args;
1033
+ const server = String(ctx.options.server ?? "");
1034
+ if (!id) exitWithValidationError("firewall_rule_id", "forge-cli firewall-rules get <rule_id> --server <server_id>", ctx.formatter);
1035
+ if (!server) exitWithValidationError("server_id", "forge-cli firewall-rules get <rule_id> --server <server_id>", ctx.formatter);
1036
+ await runCommand(async () => {
1037
+ const token = ctx.getToken();
1038
+ const execCtx = ctx.createExecutorContext(token);
1039
+ const result = await getFirewallRule({
1040
+ server_id: await resolveServerId(server, execCtx),
1041
+ id
1042
+ }, execCtx);
1043
+ ctx.formatter.outputOne(result.data, [
1044
+ "id",
1045
+ "name",
1046
+ "port",
1047
+ "type",
1048
+ "ip_address",
1049
+ "status",
1050
+ "created_at"
1051
+ ]);
1052
+ }, ctx.formatter);
1053
+ }
1054
+ const handleFirewallRulesCommand = createCommandRouter({
1055
+ resource: "firewall-rules",
1056
+ handlers: {
1057
+ list: firewallRulesList,
1058
+ ls: firewallRulesList,
1059
+ get: [firewallRulesGet, "args"]
1060
+ }
1061
+ });
1062
+ function showFirewallRulesHelp(subcommand) {
1063
+ if (subcommand === "list" || subcommand === "ls") console.log(`
1064
+ ${colors.bold("forge-cli firewall-rules list")} - List firewall rules on a server
1065
+
1066
+ ${colors.bold("USAGE:")}
1067
+ forge-cli firewall-rules list --server <server_id> [options]
1068
+ `);
1069
+ else if (subcommand === "get") console.log(`
1070
+ ${colors.bold("forge-cli firewall-rules get")} - Get firewall rule details
1071
+
1072
+ ${colors.bold("USAGE:")}
1073
+ forge-cli firewall-rules get <rule_id> --server <server_id>
1074
+ `);
1075
+ else console.log(`
1076
+ ${colors.bold("forge-cli firewall-rules")} - Manage firewall rules
1077
+
1078
+ ${colors.bold("USAGE:")}
1079
+ forge-cli firewall-rules <subcommand> [options]
1080
+
1081
+ ${colors.bold("ALIASES:")}
1082
+ forge-cli fw
1083
+
1084
+ ${colors.bold("SUBCOMMANDS:")}
1085
+ list, ls List firewall rules
1086
+ get <id> Get firewall rule details
1087
+
1088
+ ${colors.bold("OPTIONS:")}
1089
+ --server <id> Server ID (required)
1090
+ -f, --format <fmt> Output format: json, human, table
1091
+
1092
+ Run ${colors.cyan("forge-cli firewall-rules <subcommand> --help")} for details.
1093
+ `);
1094
+ }
1095
+ async function monitorsList(ctx) {
1096
+ const server = String(ctx.options.server ?? "");
1097
+ if (!server) exitWithValidationError("server_id", "forge-cli monitors list --server <server_id>", ctx.formatter);
1098
+ await runCommand(async () => {
1099
+ const token = ctx.getToken();
1100
+ const execCtx = ctx.createExecutorContext(token);
1101
+ const result = await listMonitors({ server_id: await resolveServerId(server, execCtx) }, execCtx);
1102
+ ctx.formatter.outputList(result.data, [
1103
+ "id",
1104
+ "type",
1105
+ "operator",
1106
+ "threshold",
1107
+ "state"
1108
+ ], "No monitors found.", (m) => `${String(m.id).padEnd(8)} ${m.type.padEnd(20)} ${m.operator.padEnd(8)} ${String(m.threshold).padEnd(6)} ${m.state}`);
1109
+ }, ctx.formatter);
1110
+ }
1111
+ async function monitorsGet(args, ctx) {
1112
+ const [id] = args;
1113
+ const server = String(ctx.options.server ?? "");
1114
+ if (!id) exitWithValidationError("monitor_id", "forge-cli monitors get <monitor_id> --server <server_id>", ctx.formatter);
1115
+ if (!server) exitWithValidationError("server_id", "forge-cli monitors get <monitor_id> --server <server_id>", ctx.formatter);
1116
+ await runCommand(async () => {
1117
+ const token = ctx.getToken();
1118
+ const execCtx = ctx.createExecutorContext(token);
1119
+ const result = await getMonitor({
1120
+ server_id: await resolveServerId(server, execCtx),
1121
+ id
1122
+ }, execCtx);
1123
+ ctx.formatter.outputOne(result.data, [
1124
+ "id",
1125
+ "type",
1126
+ "operator",
1127
+ "threshold",
1128
+ "minutes",
1129
+ "state",
1130
+ "state_changed_at"
1131
+ ]);
1132
+ }, ctx.formatter);
1133
+ }
1134
+ async function monitorsCreate(ctx) {
1135
+ const server = String(ctx.options.server ?? "");
1136
+ const type = String(ctx.options.type ?? "");
1137
+ const operator = String(ctx.options.operator ?? "");
1138
+ const threshold = Number(ctx.options.threshold ?? 0);
1139
+ const minutes = Number(ctx.options.minutes ?? 0);
1140
+ if (!server) exitWithValidationError("server_id", "forge-cli monitors create --server <server_id> --type <type> --operator <operator> --threshold <threshold> --minutes <minutes>", ctx.formatter);
1141
+ if (!type) exitWithValidationError("type", "forge-cli monitors create --server <server_id> --type <type> --operator <operator> --threshold <threshold> --minutes <minutes>", ctx.formatter);
1142
+ if (!operator) exitWithValidationError("operator", "forge-cli monitors create --server <server_id> --type <type> --operator <operator> --threshold <threshold> --minutes <minutes>", ctx.formatter);
1143
+ await runCommand(async () => {
1144
+ const token = ctx.getToken();
1145
+ const execCtx = ctx.createExecutorContext(token);
1146
+ const result = await createMonitor({
1147
+ server_id: await resolveServerId(server, execCtx),
1148
+ type,
1149
+ operator,
1150
+ threshold,
1151
+ minutes
1152
+ }, execCtx);
1153
+ ctx.formatter.outputOne(result.data, [
1154
+ "id",
1155
+ "type",
1156
+ "operator",
1157
+ "threshold",
1158
+ "minutes",
1159
+ "state"
1160
+ ]);
1161
+ }, ctx.formatter);
1162
+ }
1163
+ async function monitorsDelete(args, ctx) {
1164
+ const [id] = args;
1165
+ const server = String(ctx.options.server ?? "");
1166
+ if (!id) exitWithValidationError("monitor_id", "forge-cli monitors delete <monitor_id> --server <server_id>", ctx.formatter);
1167
+ if (!server) exitWithValidationError("server_id", "forge-cli monitors delete <monitor_id> --server <server_id>", ctx.formatter);
1168
+ await runCommand(async () => {
1169
+ const token = ctx.getToken();
1170
+ const execCtx = ctx.createExecutorContext(token);
1171
+ await deleteMonitor({
1172
+ server_id: await resolveServerId(server, execCtx),
1173
+ id
1174
+ }, execCtx);
1175
+ ctx.formatter.success(`Monitor ${id} deleted.`);
1176
+ }, ctx.formatter);
1177
+ }
1178
+ const handleMonitorsCommand = createCommandRouter({
1179
+ resource: "monitors",
1180
+ handlers: {
1181
+ list: monitorsList,
1182
+ ls: monitorsList,
1183
+ get: [monitorsGet, "args"],
1184
+ create: monitorsCreate,
1185
+ delete: [monitorsDelete, "args"]
1186
+ }
1187
+ });
1188
+ function showMonitorsHelp(subcommand) {
1189
+ if (subcommand === "list" || subcommand === "ls") console.log(`
1190
+ ${colors.bold("forge-cli monitors list")} - List monitors on a server
1191
+
1192
+ ${colors.bold("USAGE:")}
1193
+ forge-cli monitors list --server <server_id> [options]
1194
+
1195
+ ${colors.bold("OPTIONS:")}
1196
+ --server <id> Server ID (required)
1197
+ -f, --format <fmt> Output format: json, human, table
1198
+ `);
1199
+ else if (subcommand === "get") console.log(`
1200
+ ${colors.bold("forge-cli monitors get")} - Get monitor details
1201
+
1202
+ ${colors.bold("USAGE:")}
1203
+ forge-cli monitors get <monitor_id> --server <server_id> [options]
1204
+ `);
1205
+ else if (subcommand === "create") console.log(`
1206
+ ${colors.bold("forge-cli monitors create")} - Create a new monitor
1207
+
1208
+ ${colors.bold("USAGE:")}
1209
+ forge-cli monitors create --server <server_id> --type <type> --operator <operator> --threshold <threshold> --minutes <minutes> [options]
1210
+
1211
+ ${colors.bold("OPTIONS:")}
1212
+ --server <id> Server ID (required)
1213
+ --type <type> Monitor type (required)
1214
+ --operator <op> Comparison operator (required)
1215
+ --threshold <num> Alert threshold value (required)
1216
+ --minutes <num> Duration in minutes (required)
1217
+ -f, --format <fmt> Output format: json, human, table
1218
+ `);
1219
+ else if (subcommand === "delete") console.log(`
1220
+ ${colors.bold("forge-cli monitors delete")} - Delete a monitor
1221
+
1222
+ ${colors.bold("USAGE:")}
1223
+ forge-cli monitors delete <monitor_id> --server <server_id> [options]
1224
+ `);
1225
+ else console.log(`
1226
+ ${colors.bold("forge-cli monitors")} - Manage server monitors
1227
+
1228
+ ${colors.bold("USAGE:")}
1229
+ forge-cli monitors <subcommand> [options]
1230
+
1231
+ ${colors.bold("SUBCOMMANDS:")}
1232
+ list, ls List monitors
1233
+ get <id> Get monitor details
1234
+ create Create a new monitor
1235
+ delete <id> Delete a monitor
1236
+
1237
+ ${colors.bold("OPTIONS:")}
1238
+ --server <id> Server ID (required)
1239
+ -f, --format <fmt> Output format: json, human, table
1240
+
1241
+ Run ${colors.cyan("forge-cli monitors <subcommand> --help")} for details.
1242
+ `);
1243
+ }
1244
+ async function nginxGet(ctx) {
1245
+ const server = String(ctx.options.server ?? "");
1246
+ const site = String(ctx.options.site ?? "");
1247
+ if (!server) exitWithValidationError("server_id", "forge-cli nginx get --server <server_id> --site <site_id>", ctx.formatter);
1248
+ if (!site) exitWithValidationError("site_id", "forge-cli nginx get --server <server_id> --site <site_id>", ctx.formatter);
1249
+ await runCommand(async () => {
1250
+ const token = ctx.getToken();
1251
+ const execCtx = ctx.createExecutorContext(token);
1252
+ const server_id = await resolveServerId(server, execCtx);
1253
+ const result = await getNginxConfig({
1254
+ server_id,
1255
+ site_id: await resolveSiteId(site, server_id, execCtx)
1256
+ }, execCtx);
1257
+ if (ctx.formatter.isJson()) console.log(JSON.stringify({ content: result.data }));
1258
+ else console.log(result.data);
1259
+ }, ctx.formatter);
1260
+ }
1261
+ async function nginxUpdate(ctx) {
1262
+ const server = String(ctx.options.server ?? "");
1263
+ const site = String(ctx.options.site ?? "");
1264
+ const content = String(ctx.options.content ?? "");
1265
+ if (!server) exitWithValidationError("server_id", "forge-cli nginx update --server <server_id> --site <site_id> --content <content>", ctx.formatter);
1266
+ if (!site) exitWithValidationError("site_id", "forge-cli nginx update --server <server_id> --site <site_id> --content <content>", ctx.formatter);
1267
+ if (!content) exitWithValidationError("content", "forge-cli nginx update --server <server_id> --site <site_id> --content <content>", ctx.formatter);
1268
+ await runCommand(async () => {
1269
+ const token = ctx.getToken();
1270
+ const execCtx = ctx.createExecutorContext(token);
1271
+ const server_id = await resolveServerId(server, execCtx);
1272
+ await updateNginxConfig({
1273
+ server_id,
1274
+ site_id: await resolveSiteId(site, server_id, execCtx),
1275
+ content
1276
+ }, execCtx);
1277
+ ctx.formatter.success("Nginx configuration updated.");
1278
+ }, ctx.formatter);
1279
+ }
1280
+ const handleNginxCommand = createCommandRouter({
1281
+ resource: "nginx",
1282
+ handlers: {
1283
+ get: nginxGet,
1284
+ update: nginxUpdate
1285
+ }
1286
+ });
1287
+ function showNginxHelp(subcommand) {
1288
+ if (subcommand === "get") console.log(`
1289
+ ${colors.bold("forge-cli nginx get")} - Get nginx configuration
1290
+
1291
+ ${colors.bold("USAGE:")}
1292
+ forge-cli nginx get --server <server_id> --site <site_id>
1293
+ `);
1294
+ else if (subcommand === "update") console.log(`
1295
+ ${colors.bold("forge-cli nginx update")} - Update nginx configuration
1296
+
1297
+ ${colors.bold("USAGE:")}
1298
+ forge-cli nginx update --server <server_id> --site <site_id> --content <nginx_config>
1299
+ `);
1300
+ else console.log(`
1301
+ ${colors.bold("forge-cli nginx")} - Manage nginx configuration
1302
+
1303
+ ${colors.bold("USAGE:")}
1304
+ forge-cli nginx <subcommand> [options]
1305
+
1306
+ ${colors.bold("SUBCOMMANDS:")}
1307
+ get Get nginx configuration
1308
+ update Update nginx configuration
1309
+
1310
+ ${colors.bold("OPTIONS:")}
1311
+ --server <id> Server ID (required)
1312
+ --site <id> Site ID (required)
1313
+ --content <str> New nginx config (for update)
1314
+
1315
+ Run ${colors.cyan("forge-cli nginx <subcommand> --help")} for details.
1316
+ `);
1317
+ }
1318
+ async function nginxTemplatesList(ctx) {
1319
+ const server = String(ctx.options.server ?? "");
1320
+ if (!server) exitWithValidationError("server_id", "forge-cli nginx-templates list --server <server_id>", ctx.formatter);
1321
+ await runCommand(async () => {
1322
+ const token = ctx.getToken();
1323
+ const execCtx = ctx.createExecutorContext(token);
1324
+ const result = await listNginxTemplates({ server_id: await resolveServerId(server, execCtx) }, execCtx);
1325
+ ctx.formatter.outputList(result.data, [
1326
+ "id",
1327
+ "name",
1328
+ "created_at"
1329
+ ], "No Nginx templates found.", (t) => `${String(t.id).padEnd(8)} ${t.name.padEnd(40)} ${t.created_at}`);
1330
+ }, ctx.formatter);
1331
+ }
1332
+ async function nginxTemplatesGet(args, ctx) {
1333
+ const [id] = args;
1334
+ const server = String(ctx.options.server ?? "");
1335
+ if (!id) exitWithValidationError("template_id", "forge-cli nginx-templates get <template_id> --server <server_id>", ctx.formatter);
1336
+ if (!server) exitWithValidationError("server_id", "forge-cli nginx-templates get <template_id> --server <server_id>", ctx.formatter);
1337
+ await runCommand(async () => {
1338
+ const token = ctx.getToken();
1339
+ const execCtx = ctx.createExecutorContext(token);
1340
+ const result = await getNginxTemplate({
1341
+ server_id: await resolveServerId(server, execCtx),
1342
+ id
1343
+ }, execCtx);
1344
+ ctx.formatter.outputOne(result.data, [
1345
+ "id",
1346
+ "name",
1347
+ "created_at",
1348
+ "content"
1349
+ ]);
1350
+ }, ctx.formatter);
1351
+ }
1352
+ async function nginxTemplatesCreate(ctx) {
1353
+ const server = String(ctx.options.server ?? "");
1354
+ const name = String(ctx.options.name ?? "");
1355
+ const content = String(ctx.options.content ?? "");
1356
+ if (!server) exitWithValidationError("server_id", "forge-cli nginx-templates create --server <server_id> --name <name> --content <content>", ctx.formatter);
1357
+ if (!name) exitWithValidationError("name", "forge-cli nginx-templates create --server <server_id> --name <name> --content <content>", ctx.formatter);
1358
+ if (!content) exitWithValidationError("content", "forge-cli nginx-templates create --server <server_id> --name <name> --content <content>", ctx.formatter);
1359
+ await runCommand(async () => {
1360
+ const token = ctx.getToken();
1361
+ const execCtx = ctx.createExecutorContext(token);
1362
+ const result = await createNginxTemplate({
1363
+ server_id: await resolveServerId(server, execCtx),
1364
+ name,
1365
+ content
1366
+ }, execCtx);
1367
+ ctx.formatter.outputOne(result.data, [
1368
+ "id",
1369
+ "name",
1370
+ "created_at"
1371
+ ]);
1372
+ }, ctx.formatter);
1373
+ }
1374
+ async function nginxTemplatesUpdate(args, ctx) {
1375
+ const [id] = args;
1376
+ const server = String(ctx.options.server ?? "");
1377
+ if (!id) exitWithValidationError("template_id", "forge-cli nginx-templates update <template_id> --server <server_id>", ctx.formatter);
1378
+ if (!server) exitWithValidationError("server_id", "forge-cli nginx-templates update <template_id> --server <server_id>", ctx.formatter);
1379
+ await runCommand(async () => {
1380
+ const token = ctx.getToken();
1381
+ const execCtx = ctx.createExecutorContext(token);
1382
+ const result = await updateNginxTemplate({
1383
+ server_id: await resolveServerId(server, execCtx),
1384
+ id,
1385
+ name: ctx.options.name ? String(ctx.options.name) : void 0,
1386
+ content: ctx.options.content ? String(ctx.options.content) : void 0
1387
+ }, execCtx);
1388
+ ctx.formatter.outputOne(result.data, [
1389
+ "id",
1390
+ "name",
1391
+ "created_at"
1392
+ ]);
1393
+ }, ctx.formatter);
1394
+ }
1395
+ async function nginxTemplatesDelete(args, ctx) {
1396
+ const [id] = args;
1397
+ const server = String(ctx.options.server ?? "");
1398
+ if (!id) exitWithValidationError("template_id", "forge-cli nginx-templates delete <template_id> --server <server_id>", ctx.formatter);
1399
+ if (!server) exitWithValidationError("server_id", "forge-cli nginx-templates delete <template_id> --server <server_id>", ctx.formatter);
1400
+ await runCommand(async () => {
1401
+ const token = ctx.getToken();
1402
+ const execCtx = ctx.createExecutorContext(token);
1403
+ await deleteNginxTemplate({
1404
+ server_id: await resolveServerId(server, execCtx),
1405
+ id
1406
+ }, execCtx);
1407
+ ctx.formatter.success(`Nginx template ${id} deleted.`);
1408
+ }, ctx.formatter);
1409
+ }
1410
+ const handleNginxTemplatesCommand = createCommandRouter({
1411
+ resource: "nginx-templates",
1412
+ handlers: {
1413
+ list: nginxTemplatesList,
1414
+ ls: nginxTemplatesList,
1415
+ get: [nginxTemplatesGet, "args"],
1416
+ create: nginxTemplatesCreate,
1417
+ update: [nginxTemplatesUpdate, "args"],
1418
+ delete: [nginxTemplatesDelete, "args"]
1419
+ }
1420
+ });
1421
+ function showNginxTemplatesHelp(subcommand) {
1422
+ if (subcommand === "list" || subcommand === "ls") console.log(`
1423
+ ${colors.bold("forge-cli nginx-templates list")} - List Nginx templates on a server
1424
+
1425
+ ${colors.bold("USAGE:")}
1426
+ forge-cli nginx-templates list --server <server_id> [options]
1427
+
1428
+ ${colors.bold("OPTIONS:")}
1429
+ --server <id> Server ID (required)
1430
+ -f, --format <fmt> Output format: json, human, table
1431
+ `);
1432
+ else if (subcommand === "get") console.log(`
1433
+ ${colors.bold("forge-cli nginx-templates get")} - Get Nginx template details
1434
+
1435
+ ${colors.bold("USAGE:")}
1436
+ forge-cli nginx-templates get <template_id> --server <server_id> [options]
1437
+ `);
1438
+ else if (subcommand === "create") console.log(`
1439
+ ${colors.bold("forge-cli nginx-templates create")} - Create a new Nginx template
1440
+
1441
+ ${colors.bold("USAGE:")}
1442
+ forge-cli nginx-templates create --server <server_id> --name <name> --content <content> [options]
1443
+
1444
+ ${colors.bold("OPTIONS:")}
1445
+ --server <id> Server ID (required)
1446
+ --name <name> Template name (required)
1447
+ --content <text> Template content (required)
1448
+ -f, --format <fmt> Output format: json, human, table
1449
+ `);
1450
+ else if (subcommand === "update") console.log(`
1451
+ ${colors.bold("forge-cli nginx-templates update")} - Update an Nginx template
1452
+
1453
+ ${colors.bold("USAGE:")}
1454
+ forge-cli nginx-templates update <template_id> --server <server_id> [options]
1455
+
1456
+ ${colors.bold("OPTIONS:")}
1457
+ --server <id> Server ID (required)
1458
+ --name <name> New template name (optional)
1459
+ --content <text> New template content (optional)
1460
+ -f, --format <fmt> Output format: json, human, table
1461
+ `);
1462
+ else if (subcommand === "delete") console.log(`
1463
+ ${colors.bold("forge-cli nginx-templates delete")} - Delete an Nginx template
1464
+
1465
+ ${colors.bold("USAGE:")}
1466
+ forge-cli nginx-templates delete <template_id> --server <server_id> [options]
1467
+ `);
1468
+ else console.log(`
1469
+ ${colors.bold("forge-cli nginx-templates")} - Manage Nginx templates
1470
+
1471
+ ${colors.bold("USAGE:")}
1472
+ forge-cli nginx-templates <subcommand> [options]
1473
+
1474
+ ${colors.bold("SUBCOMMANDS:")}
1475
+ list, ls List Nginx templates
1476
+ get <id> Get Nginx template details
1477
+ create Create a new Nginx template
1478
+ update <id> Update an Nginx template
1479
+ delete <id> Delete an Nginx template
1480
+
1481
+ ${colors.bold("OPTIONS:")}
1482
+ --server <id> Server ID (required)
1483
+ -f, --format <fmt> Output format: json, human, table
1484
+
1485
+ Run ${colors.cyan("forge-cli nginx-templates <subcommand> --help")} for details.
1486
+ `);
1487
+ }
1488
+ async function recipesList(ctx) {
1489
+ await runCommand(async () => {
1490
+ const token = ctx.getToken();
1491
+ const result = await listRecipes({}, ctx.createExecutorContext(token));
1492
+ ctx.formatter.outputList(result.data, [
1493
+ "id",
1494
+ "name",
1495
+ "user",
1496
+ "created_at"
1497
+ ], "No recipes found.", (r) => `${String(r.id).padEnd(8)} ${r.name.padEnd(40)} ${r.user.padEnd(16)} ${r.created_at}`);
1498
+ }, ctx.formatter);
1499
+ }
1500
+ async function recipesGet(args, ctx) {
1501
+ const [id] = args;
1502
+ if (!id) exitWithValidationError("id", "forge-cli recipes get <recipe_id>", ctx.formatter);
1503
+ await runCommand(async () => {
1504
+ const token = ctx.getToken();
1505
+ const execCtx = ctx.createExecutorContext(token);
1506
+ const result = await getRecipe({ id }, execCtx);
1507
+ ctx.formatter.outputOne(result.data, [
1508
+ "id",
1509
+ "name",
1510
+ "user",
1511
+ "created_at",
1512
+ "script"
1513
+ ]);
1514
+ }, ctx.formatter);
1515
+ }
1516
+ async function recipesRun(args, ctx) {
1517
+ const [id] = args;
1518
+ const serversRaw = String(ctx.options.servers ?? "");
1519
+ if (!id) exitWithValidationError("id", "forge-cli recipes run <recipe_id> --servers <ids>", ctx.formatter);
1520
+ if (!serversRaw) exitWithValidationError("servers", "forge-cli recipes run <recipe_id> --servers <server_id1,server_id2,...>", ctx.formatter);
1521
+ const servers = serversRaw.split(",").map((s) => Number(s.trim())).filter((n) => !isNaN(n));
1522
+ await runCommand(async () => {
1523
+ const token = ctx.getToken();
1524
+ const execCtx = ctx.createExecutorContext(token);
1525
+ await runRecipe({
1526
+ id,
1527
+ servers
1528
+ }, execCtx);
1529
+ ctx.formatter.success(`Recipe ${id} dispatched to ${servers.length} server(s).`);
1530
+ }, ctx.formatter);
1531
+ }
1532
+ const handleRecipesCommand = createCommandRouter({
1533
+ resource: "recipes",
1534
+ handlers: {
1535
+ list: recipesList,
1536
+ ls: recipesList,
1537
+ get: [recipesGet, "args"],
1538
+ run: [recipesRun, "args"]
1539
+ }
1540
+ });
1541
+ function showRecipesHelp(subcommand) {
1542
+ if (subcommand === "list" || subcommand === "ls") console.log(`
1543
+ ${colors.bold("forge-cli recipes list")} - List all recipes
1544
+
1545
+ ${colors.bold("USAGE:")}
1546
+ forge-cli recipes list [options]
1547
+ `);
1548
+ else if (subcommand === "get") console.log(`
1549
+ ${colors.bold("forge-cli recipes get")} - Get recipe details
1550
+
1551
+ ${colors.bold("USAGE:")}
1552
+ forge-cli recipes get <recipe_id>
1553
+ `);
1554
+ else if (subcommand === "run") console.log(`
1555
+ ${colors.bold("forge-cli recipes run")} - Run a recipe on servers
1556
+
1557
+ ${colors.bold("USAGE:")}
1558
+ forge-cli recipes run <recipe_id> --servers <server_id1,server_id2,...>
1559
+
1560
+ ${colors.bold("OPTIONS:")}
1561
+ --servers <ids> Comma-separated server IDs to run recipe on
1562
+ `);
1563
+ else console.log(`
1564
+ ${colors.bold("forge-cli recipes")} - Manage recipes
1565
+
1566
+ ${colors.bold("USAGE:")}
1567
+ forge-cli recipes <subcommand> [options]
1568
+
1569
+ ${colors.bold("SUBCOMMANDS:")}
1570
+ list, ls List all recipes
1571
+ get <id> Get recipe details
1572
+ run <id> Run a recipe on servers
1573
+
1574
+ ${colors.bold("OPTIONS:")}
1575
+ -f, --format <fmt> Output format: json, human, table
1576
+
1577
+ Run ${colors.cyan("forge-cli recipes <subcommand> --help")} for details.
1578
+ `);
1579
+ }
1580
+ function requireServerAndSiteRaw$1(ctx, usage) {
1581
+ const server = String(ctx.options.server ?? "");
1582
+ const site = String(ctx.options.site ?? "");
1583
+ if (!server) exitWithValidationError("server_id", usage, ctx.formatter);
1584
+ if (!site) exitWithValidationError("site_id", usage, ctx.formatter);
1585
+ return {
1586
+ server,
1587
+ site
1588
+ };
1589
+ }
1590
+ async function redirectRulesList(ctx) {
1591
+ const { server, site } = requireServerAndSiteRaw$1(ctx, "forge-cli redirect-rules list --server <server_id> --site <site_id>");
1592
+ await runCommand(async () => {
1593
+ const token = ctx.getToken();
1594
+ const execCtx = ctx.createExecutorContext(token);
1595
+ const server_id = await resolveServerId(server, execCtx);
1596
+ const result = await listRedirectRules({
1597
+ server_id,
1598
+ site_id: await resolveSiteId(site, server_id, execCtx)
1599
+ }, execCtx);
1600
+ ctx.formatter.outputList(result.data, [
1601
+ "id",
1602
+ "from",
1603
+ "to",
1604
+ "type"
1605
+ ], "No redirect rules found.", (r) => `${String(r.id).padEnd(8)} ${r.from.padEnd(30)} ${r.to.padEnd(30)} ${r.type}`);
1606
+ }, ctx.formatter);
1607
+ }
1608
+ async function redirectRulesGet(args, ctx) {
1609
+ const [id] = args;
1610
+ const usage = "forge-cli redirect-rules get <rule_id> --server <server_id> --site <site_id>";
1611
+ if (!id) exitWithValidationError("rule_id", usage, ctx.formatter);
1612
+ const { server, site } = requireServerAndSiteRaw$1(ctx, usage);
1613
+ await runCommand(async () => {
1614
+ const token = ctx.getToken();
1615
+ const execCtx = ctx.createExecutorContext(token);
1616
+ const server_id = await resolveServerId(server, execCtx);
1617
+ const result = await getRedirectRule({
1618
+ server_id,
1619
+ site_id: await resolveSiteId(site, server_id, execCtx),
1620
+ id
1621
+ }, execCtx);
1622
+ ctx.formatter.outputOne(result.data, [
1623
+ "id",
1624
+ "from",
1625
+ "to",
1626
+ "type",
1627
+ "created_at"
1628
+ ]);
1629
+ }, ctx.formatter);
1630
+ }
1631
+ async function redirectRulesCreate(ctx) {
1632
+ const usage = "forge-cli redirect-rules create --server <server_id> --site <site_id> --from <from> --to <to>";
1633
+ const { server, site } = requireServerAndSiteRaw$1(ctx, usage);
1634
+ const from = String(ctx.options.from ?? "");
1635
+ const to = String(ctx.options.to ?? "");
1636
+ if (!from) exitWithValidationError("from", usage, ctx.formatter);
1637
+ if (!to) exitWithValidationError("to", usage, ctx.formatter);
1638
+ await runCommand(async () => {
1639
+ const token = ctx.getToken();
1640
+ const execCtx = ctx.createExecutorContext(token);
1641
+ const server_id = await resolveServerId(server, execCtx);
1642
+ const result = await createRedirectRule({
1643
+ server_id,
1644
+ site_id: await resolveSiteId(site, server_id, execCtx),
1645
+ from,
1646
+ to
1647
+ }, execCtx);
1648
+ ctx.formatter.outputOne(result.data, [
1649
+ "id",
1650
+ "from",
1651
+ "to",
1652
+ "type",
1653
+ "created_at"
1654
+ ]);
1655
+ }, ctx.formatter);
1656
+ }
1657
+ async function redirectRulesDelete(args, ctx) {
1658
+ const [id] = args;
1659
+ const usage = "forge-cli redirect-rules delete <rule_id> --server <server_id> --site <site_id>";
1660
+ if (!id) exitWithValidationError("rule_id", usage, ctx.formatter);
1661
+ const { server, site } = requireServerAndSiteRaw$1(ctx, usage);
1662
+ await runCommand(async () => {
1663
+ const token = ctx.getToken();
1664
+ const execCtx = ctx.createExecutorContext(token);
1665
+ const server_id = await resolveServerId(server, execCtx);
1666
+ await deleteRedirectRule({
1667
+ server_id,
1668
+ site_id: await resolveSiteId(site, server_id, execCtx),
1669
+ id
1670
+ }, execCtx);
1671
+ ctx.formatter.success(`Redirect rule ${id} deleted.`);
1672
+ }, ctx.formatter);
1673
+ }
1674
+ const handleRedirectRulesCommand = createCommandRouter({
1675
+ resource: "redirect-rules",
1676
+ handlers: {
1677
+ list: redirectRulesList,
1678
+ ls: redirectRulesList,
1679
+ get: [redirectRulesGet, "args"],
1680
+ create: redirectRulesCreate,
1681
+ delete: [redirectRulesDelete, "args"]
1682
+ }
1683
+ });
1684
+ function showRedirectRulesHelp(subcommand) {
1685
+ if (subcommand === "list" || subcommand === "ls") console.log(`
1686
+ ${colors.bold("forge-cli redirect-rules list")} - List redirect rules for a site
1687
+
1688
+ ${colors.bold("USAGE:")}
1689
+ forge-cli redirect-rules list --server <server_id> --site <site_id> [options]
1690
+
1691
+ ${colors.bold("OPTIONS:")}
1692
+ --server <id> Server ID (required)
1693
+ --site <id> Site ID (required)
1694
+ -f, --format <fmt> Output format: json, human, table
1695
+ `);
1696
+ else if (subcommand === "get") console.log(`
1697
+ ${colors.bold("forge-cli redirect-rules get")} - Get redirect rule details
1698
+
1699
+ ${colors.bold("USAGE:")}
1700
+ forge-cli redirect-rules get <rule_id> --server <server_id> --site <site_id> [options]
1701
+ `);
1702
+ else if (subcommand === "create") console.log(`
1703
+ ${colors.bold("forge-cli redirect-rules create")} - Create a new redirect rule
1704
+
1705
+ ${colors.bold("USAGE:")}
1706
+ forge-cli redirect-rules create --server <server_id> --site <site_id> --from <from> --to <to> [options]
1707
+
1708
+ ${colors.bold("OPTIONS:")}
1709
+ --server <id> Server ID (required)
1710
+ --site <id> Site ID (required)
1711
+ --from <path> Source path to redirect from (required)
1712
+ --to <path> Destination path to redirect to (required)
1713
+ -f, --format <fmt> Output format: json, human, table
1714
+ `);
1715
+ else if (subcommand === "delete") console.log(`
1716
+ ${colors.bold("forge-cli redirect-rules delete")} - Delete a redirect rule
1717
+
1718
+ ${colors.bold("USAGE:")}
1719
+ forge-cli redirect-rules delete <rule_id> --server <server_id> --site <site_id> [options]
1720
+ `);
1721
+ else console.log(`
1722
+ ${colors.bold("forge-cli redirect-rules")} - Manage site redirect rules
1723
+
1724
+ ${colors.bold("USAGE:")}
1725
+ forge-cli redirect-rules <subcommand> [options]
1726
+
1727
+ ${colors.bold("SUBCOMMANDS:")}
1728
+ list, ls List redirect rules
1729
+ get <id> Get redirect rule details
1730
+ create Create a new redirect rule
1731
+ delete <id> Delete a redirect rule
1732
+
1733
+ ${colors.bold("OPTIONS:")}
1734
+ --server <id> Server ID (required)
1735
+ --site <id> Site ID (required)
1736
+ -f, --format <fmt> Output format: json, human, table
1737
+
1738
+ Run ${colors.cyan("forge-cli redirect-rules <subcommand> --help")} for details.
1739
+ `);
1740
+ }
1741
+ async function scheduledJobsList(ctx) {
1742
+ const server = String(ctx.options.server ?? "");
1743
+ if (!server) exitWithValidationError("server_id", "forge-cli scheduled-jobs list --server <server_id>", ctx.formatter);
1744
+ await runCommand(async () => {
1745
+ const token = ctx.getToken();
1746
+ const execCtx = ctx.createExecutorContext(token);
1747
+ const result = await listScheduledJobs({ server_id: await resolveServerId(server, execCtx) }, execCtx);
1748
+ ctx.formatter.outputList(result.data, [
1749
+ "id",
1750
+ "command",
1751
+ "frequency",
1752
+ "status"
1753
+ ], "No scheduled jobs found.", (j) => `${String(j.id).padEnd(8)} ${j.command.padEnd(50)} ${j.frequency.padEnd(12)} ${j.status}`);
1754
+ }, ctx.formatter);
1755
+ }
1756
+ async function scheduledJobsGet(args, ctx) {
1757
+ const [id] = args;
1758
+ const server = String(ctx.options.server ?? "");
1759
+ if (!id) exitWithValidationError("job_id", "forge-cli scheduled-jobs get <job_id> --server <server_id>", ctx.formatter);
1760
+ if (!server) exitWithValidationError("server_id", "forge-cli scheduled-jobs get <job_id> --server <server_id>", ctx.formatter);
1761
+ await runCommand(async () => {
1762
+ const token = ctx.getToken();
1763
+ const execCtx = ctx.createExecutorContext(token);
1764
+ const result = await getScheduledJob({
1765
+ server_id: await resolveServerId(server, execCtx),
1766
+ id
1767
+ }, execCtx);
1768
+ ctx.formatter.outputOne(result.data, [
1769
+ "id",
1770
+ "command",
1771
+ "user",
1772
+ "frequency",
1773
+ "cron",
1774
+ "status",
1775
+ "created_at"
1776
+ ]);
1777
+ }, ctx.formatter);
1778
+ }
1779
+ async function scheduledJobsCreate(ctx) {
1780
+ const server = String(ctx.options.server ?? "");
1781
+ const command = String(ctx.options.command ?? "");
1782
+ if (!server) exitWithValidationError("server_id", "forge-cli scheduled-jobs create --server <server_id> --command <command>", ctx.formatter);
1783
+ if (!command) exitWithValidationError("command", "forge-cli scheduled-jobs create --server <server_id> --command <command>", ctx.formatter);
1784
+ await runCommand(async () => {
1785
+ const token = ctx.getToken();
1786
+ const execCtx = ctx.createExecutorContext(token);
1787
+ const result = await createScheduledJob({
1788
+ server_id: await resolveServerId(server, execCtx),
1789
+ command
1790
+ }, execCtx);
1791
+ ctx.formatter.outputOne(result.data, [
1792
+ "id",
1793
+ "command",
1794
+ "user",
1795
+ "frequency",
1796
+ "cron",
1797
+ "status",
1798
+ "created_at"
1799
+ ]);
1800
+ }, ctx.formatter);
1801
+ }
1802
+ async function scheduledJobsDelete(args, ctx) {
1803
+ const [id] = args;
1804
+ const server = String(ctx.options.server ?? "");
1805
+ if (!id) exitWithValidationError("job_id", "forge-cli scheduled-jobs delete <job_id> --server <server_id>", ctx.formatter);
1806
+ if (!server) exitWithValidationError("server_id", "forge-cli scheduled-jobs delete <job_id> --server <server_id>", ctx.formatter);
1807
+ await runCommand(async () => {
1808
+ const token = ctx.getToken();
1809
+ const execCtx = ctx.createExecutorContext(token);
1810
+ await deleteScheduledJob({
1811
+ server_id: await resolveServerId(server, execCtx),
1812
+ id
1813
+ }, execCtx);
1814
+ ctx.formatter.success(`Scheduled job ${id} deleted.`);
1815
+ }, ctx.formatter);
1816
+ }
1817
+ const handleScheduledJobsCommand = createCommandRouter({
1818
+ resource: "scheduled-jobs",
1819
+ handlers: {
1820
+ list: scheduledJobsList,
1821
+ ls: scheduledJobsList,
1822
+ get: [scheduledJobsGet, "args"],
1823
+ create: scheduledJobsCreate,
1824
+ delete: [scheduledJobsDelete, "args"]
1825
+ }
1826
+ });
1827
+ function showScheduledJobsHelp(subcommand) {
1828
+ if (subcommand === "list" || subcommand === "ls") console.log(`
1829
+ ${colors.bold("forge-cli scheduled-jobs list")} - List scheduled jobs on a server
1830
+
1831
+ ${colors.bold("USAGE:")}
1832
+ forge-cli scheduled-jobs list --server <server_id> [options]
1833
+
1834
+ ${colors.bold("OPTIONS:")}
1835
+ --server <id> Server ID (required)
1836
+ -f, --format <fmt> Output format: json, human, table
1837
+ `);
1838
+ else if (subcommand === "get") console.log(`
1839
+ ${colors.bold("forge-cli scheduled-jobs get")} - Get scheduled job details
1840
+
1841
+ ${colors.bold("USAGE:")}
1842
+ forge-cli scheduled-jobs get <job_id> --server <server_id> [options]
1843
+ `);
1844
+ else if (subcommand === "create") console.log(`
1845
+ ${colors.bold("forge-cli scheduled-jobs create")} - Create a new scheduled job
1846
+
1847
+ ${colors.bold("USAGE:")}
1848
+ forge-cli scheduled-jobs create --server <server_id> --command <command> [options]
1849
+
1850
+ ${colors.bold("OPTIONS:")}
1851
+ --server <id> Server ID (required)
1852
+ --command <cmd> Command to run (required)
1853
+ -f, --format <fmt> Output format: json, human, table
1854
+ `);
1855
+ else if (subcommand === "delete") console.log(`
1856
+ ${colors.bold("forge-cli scheduled-jobs delete")} - Delete a scheduled job
1857
+
1858
+ ${colors.bold("USAGE:")}
1859
+ forge-cli scheduled-jobs delete <job_id> --server <server_id> [options]
1860
+ `);
1861
+ else console.log(`
1862
+ ${colors.bold("forge-cli scheduled-jobs")} - Manage scheduled jobs (cron)
1863
+
1864
+ ${colors.bold("USAGE:")}
1865
+ forge-cli scheduled-jobs <subcommand> [options]
1866
+
1867
+ ${colors.bold("SUBCOMMANDS:")}
1868
+ list, ls List scheduled jobs
1869
+ get <id> Get scheduled job details
1870
+ create Create a new scheduled job
1871
+ delete <id> Delete a scheduled job
1872
+
1873
+ ${colors.bold("OPTIONS:")}
1874
+ --server <id> Server ID (required)
1875
+ -f, --format <fmt> Output format: json, human, table
1876
+
1877
+ Run ${colors.cyan("forge-cli scheduled-jobs <subcommand> --help")} for details.
1878
+ `);
1879
+ }
1880
+ function requireServerAndSiteRaw(ctx, usage) {
1881
+ const server = String(ctx.options.server ?? "");
1882
+ const site = String(ctx.options.site ?? "");
1883
+ if (!server) exitWithValidationError("server_id", usage, ctx.formatter);
1884
+ if (!site) exitWithValidationError("site_id", usage, ctx.formatter);
1885
+ return {
1886
+ server,
1887
+ site
1888
+ };
1889
+ }
1890
+ async function securityRulesList(ctx) {
1891
+ const { server, site } = requireServerAndSiteRaw(ctx, "forge-cli security-rules list --server <server_id> --site <site_id>");
1892
+ await runCommand(async () => {
1893
+ const token = ctx.getToken();
1894
+ const execCtx = ctx.createExecutorContext(token);
1895
+ const server_id = await resolveServerId(server, execCtx);
1896
+ const result = await listSecurityRules({
1897
+ server_id,
1898
+ site_id: await resolveSiteId(site, server_id, execCtx)
1899
+ }, execCtx);
1900
+ ctx.formatter.outputList(result.data, [
1901
+ "id",
1902
+ "name",
1903
+ "path"
1904
+ ], "No security rules found.", (r) => `${String(r.id).padEnd(8)} ${r.name.padEnd(30)} ${r.path ?? "—"}`);
1905
+ }, ctx.formatter);
1906
+ }
1907
+ async function securityRulesGet(args, ctx) {
1908
+ const [id] = args;
1909
+ const usage = "forge-cli security-rules get <rule_id> --server <server_id> --site <site_id>";
1910
+ if (!id) exitWithValidationError("rule_id", usage, ctx.formatter);
1911
+ const { server, site } = requireServerAndSiteRaw(ctx, usage);
1912
+ await runCommand(async () => {
1913
+ const token = ctx.getToken();
1914
+ const execCtx = ctx.createExecutorContext(token);
1915
+ const server_id = await resolveServerId(server, execCtx);
1916
+ const result = await getSecurityRule({
1917
+ server_id,
1918
+ site_id: await resolveSiteId(site, server_id, execCtx),
1919
+ id
1920
+ }, execCtx);
1921
+ ctx.formatter.outputOne(result.data, [
1922
+ "id",
1923
+ "name",
1924
+ "path",
1925
+ "credentials",
1926
+ "created_at"
1927
+ ]);
1928
+ }, ctx.formatter);
1929
+ }
1930
+ async function securityRulesCreate(ctx) {
1931
+ const usage = "forge-cli security-rules create --server <server_id> --site <site_id> --name <name>";
1932
+ const { server, site } = requireServerAndSiteRaw(ctx, usage);
1933
+ const name = String(ctx.options.name ?? "");
1934
+ if (!name) exitWithValidationError("name", usage, ctx.formatter);
1935
+ await runCommand(async () => {
1936
+ const token = ctx.getToken();
1937
+ const execCtx = ctx.createExecutorContext(token);
1938
+ const server_id = await resolveServerId(server, execCtx);
1939
+ const result = await createSecurityRule({
1940
+ server_id,
1941
+ site_id: await resolveSiteId(site, server_id, execCtx),
1942
+ name,
1943
+ credentials: []
1944
+ }, execCtx);
1945
+ ctx.formatter.outputOne(result.data, [
1946
+ "id",
1947
+ "name",
1948
+ "path",
1949
+ "created_at"
1950
+ ]);
1951
+ }, ctx.formatter);
1952
+ }
1953
+ async function securityRulesDelete(args, ctx) {
1954
+ const [id] = args;
1955
+ const usage = "forge-cli security-rules delete <rule_id> --server <server_id> --site <site_id>";
1956
+ if (!id) exitWithValidationError("rule_id", usage, ctx.formatter);
1957
+ const { server, site } = requireServerAndSiteRaw(ctx, usage);
1958
+ await runCommand(async () => {
1959
+ const token = ctx.getToken();
1960
+ const execCtx = ctx.createExecutorContext(token);
1961
+ const server_id = await resolveServerId(server, execCtx);
1962
+ await deleteSecurityRule({
1963
+ server_id,
1964
+ site_id: await resolveSiteId(site, server_id, execCtx),
1965
+ id
1966
+ }, execCtx);
1967
+ ctx.formatter.success(`Security rule ${id} deleted.`);
1968
+ }, ctx.formatter);
1969
+ }
1970
+ const handleSecurityRulesCommand = createCommandRouter({
1971
+ resource: "security-rules",
1972
+ handlers: {
1973
+ list: securityRulesList,
1974
+ ls: securityRulesList,
1975
+ get: [securityRulesGet, "args"],
1976
+ create: securityRulesCreate,
1977
+ delete: [securityRulesDelete, "args"]
1978
+ }
1979
+ });
1980
+ function showSecurityRulesHelp(subcommand) {
1981
+ if (subcommand === "list" || subcommand === "ls") console.log(`
1982
+ ${colors.bold("forge-cli security-rules list")} - List security rules for a site
1983
+
1984
+ ${colors.bold("USAGE:")}
1985
+ forge-cli security-rules list --server <server_id> --site <site_id> [options]
1986
+
1987
+ ${colors.bold("OPTIONS:")}
1988
+ --server <id> Server ID (required)
1989
+ --site <id> Site ID (required)
1990
+ -f, --format <fmt> Output format: json, human, table
1991
+ `);
1992
+ else if (subcommand === "get") console.log(`
1993
+ ${colors.bold("forge-cli security-rules get")} - Get security rule details
1994
+
1995
+ ${colors.bold("USAGE:")}
1996
+ forge-cli security-rules get <rule_id> --server <server_id> --site <site_id> [options]
1997
+ `);
1998
+ else if (subcommand === "create") console.log(`
1999
+ ${colors.bold("forge-cli security-rules create")} - Create a new security rule
2000
+
2001
+ ${colors.bold("USAGE:")}
2002
+ forge-cli security-rules create --server <server_id> --site <site_id> --name <name> [options]
2003
+
2004
+ ${colors.bold("OPTIONS:")}
2005
+ --server <id> Server ID (required)
2006
+ --site <id> Site ID (required)
2007
+ --name <name> Security rule name (required)
2008
+ -f, --format <fmt> Output format: json, human, table
2009
+ `);
2010
+ else if (subcommand === "delete") console.log(`
2011
+ ${colors.bold("forge-cli security-rules delete")} - Delete a security rule
2012
+
2013
+ ${colors.bold("USAGE:")}
2014
+ forge-cli security-rules delete <rule_id> --server <server_id> --site <site_id> [options]
2015
+ `);
2016
+ else console.log(`
2017
+ ${colors.bold("forge-cli security-rules")} - Manage site security rules
2018
+
2019
+ ${colors.bold("USAGE:")}
2020
+ forge-cli security-rules <subcommand> [options]
2021
+
2022
+ ${colors.bold("SUBCOMMANDS:")}
2023
+ list, ls List security rules
2024
+ get <id> Get security rule details
2025
+ create Create a new security rule
2026
+ delete <id> Delete a security rule
2027
+
2028
+ ${colors.bold("OPTIONS:")}
2029
+ --server <id> Server ID (required)
2030
+ --site <id> Site ID (required)
2031
+ -f, --format <fmt> Output format: json, human, table
2032
+
2033
+ Run ${colors.cyan("forge-cli security-rules <subcommand> --help")} for details.
2034
+ `);
2035
+ }
2036
+ /**
2037
+ * CLI handlers for servers commands.
2038
+ * Delegates all HTTP logic to forge-core executors.
2039
+ */
2040
+ /**
2041
+ * List all servers.
2042
+ */
2043
+ async function serversList(ctx) {
2044
+ await runCommand(async () => {
2045
+ const token = ctx.getToken();
2046
+ const result = await listServers({}, ctx.createExecutorContext(token));
2047
+ const format = String(ctx.options.format ?? ctx.options.f ?? "human");
2048
+ if (format === "table" || format === "json") ctx.formatter.output(result.data);
2049
+ else {
2050
+ if (result.data.length === 0) {
2051
+ ctx.formatter.info("No servers found.");
2052
+ return;
2053
+ }
2054
+ for (const server of result.data) console.log(`${String(server.id).padEnd(8)} ${server.name.padEnd(30)} ${server.ip_address.padEnd(16)} ${server.is_ready ? "ready" : "provisioning"}`);
2055
+ }
2056
+ }, ctx.formatter);
2057
+ }
2058
+ /**
2059
+ * Get a single server by ID or name.
2060
+ */
2061
+ async function serversGet(args, ctx) {
2062
+ const [server] = args;
2063
+ if (!server) exitWithValidationError("server_id", "forge-cli servers get <server_id>", ctx.formatter);
2064
+ await runCommand(async () => {
2065
+ const token = ctx.getToken();
2066
+ const execCtx = ctx.createExecutorContext(token);
2067
+ const result = await getServer({ server_id: await resolveServerId(server, execCtx) }, execCtx);
2068
+ ctx.formatter.output(result.data);
2069
+ }, ctx.formatter);
2070
+ }
2071
+ /**
2072
+ * Reboot a server by ID or name.
2073
+ */
2074
+ async function serversReboot(args, ctx) {
2075
+ const [server] = args;
2076
+ if (!server) exitWithValidationError("server_id", "forge-cli servers reboot <server_id>", ctx.formatter);
2077
+ await runCommand(async () => {
2078
+ const token = ctx.getToken();
2079
+ const execCtx = ctx.createExecutorContext(token);
2080
+ const server_id = await resolveServerId(server, execCtx);
2081
+ await rebootServer({ server_id }, execCtx);
2082
+ ctx.formatter.success(`Server ${server_id} reboot initiated.`);
2083
+ }, ctx.formatter);
2084
+ }
2085
+ const handleServersCommand = createCommandRouter({
2086
+ resource: "servers",
2087
+ handlers: {
2088
+ list: serversList,
2089
+ ls: serversList,
2090
+ get: [serversGet, "args"],
2091
+ reboot: [serversReboot, "args"]
2092
+ }
2093
+ });
2094
+ function showServersHelp(subcommand) {
2095
+ if (subcommand === "list" || subcommand === "ls") console.log(`
2096
+ ${colors.bold("forge-cli servers list")} - List all servers
2097
+
2098
+ ${colors.bold("USAGE:")}
2099
+ forge-cli servers list [options]
2100
+
2101
+ ${colors.bold("OPTIONS:")}
2102
+ --token <token> Forge API token
2103
+ -f, --format <fmt> Output format: json, human, table (default: human)
2104
+ --no-color Disable colored output
2105
+ -h, --help Show this help
2106
+
2107
+ ${colors.bold("EXAMPLES:")}
2108
+ forge-cli servers list
2109
+ forge-cli servers list --format json
2110
+ `);
2111
+ else if (subcommand === "get") console.log(`
2112
+ ${colors.bold("forge-cli servers get")} - Get server details
2113
+
2114
+ ${colors.bold("USAGE:")}
2115
+ forge-cli servers get <server_id> [options]
2116
+
2117
+ ${colors.bold("ARGUMENTS:")}
2118
+ <server_id> Server ID (required)
2119
+
2120
+ ${colors.bold("OPTIONS:")}
2121
+ -f, --format <fmt> Output format: json, human, table
2122
+
2123
+ ${colors.bold("EXAMPLES:")}
2124
+ forge-cli servers get 123
2125
+ forge-cli servers get 123 --format json
2126
+ `);
2127
+ else if (subcommand === "reboot") console.log(`
2128
+ ${colors.bold("forge-cli servers reboot")} - Reboot a server
2129
+
2130
+ ${colors.bold("USAGE:")}
2131
+ forge-cli servers reboot <server_id> [options]
2132
+
2133
+ ${colors.bold("ARGUMENTS:")}
2134
+ <server_id> Server ID (required)
2135
+
2136
+ ${colors.bold("EXAMPLES:")}
2137
+ forge-cli servers reboot 123
2138
+ `);
2139
+ else console.log(`
2140
+ ${colors.bold("forge-cli servers")} - Manage servers
2141
+
2142
+ ${colors.bold("USAGE:")}
2143
+ forge-cli servers <subcommand> [options]
2144
+
2145
+ ${colors.bold("ALIASES:")}
2146
+ forge-cli s
2147
+
2148
+ ${colors.bold("SUBCOMMANDS:")}
2149
+ list, ls List all servers
2150
+ get <id> Get server details
2151
+ reboot <id> Reboot a server
2152
+
2153
+ ${colors.bold("OPTIONS:")}
2154
+ -f, --format <fmt> Output format: json, human, table
2155
+ -h, --help Show help for a subcommand
2156
+
2157
+ ${colors.bold("EXAMPLES:")}
2158
+ forge-cli servers list
2159
+ forge-cli servers get 123
2160
+
2161
+ Run ${colors.cyan("forge-cli servers <subcommand> --help")} for subcommand details.
2162
+ `);
2163
+ }
2164
+ async function sitesList(ctx) {
2165
+ const server = String(ctx.options.server ?? "");
2166
+ if (!server) exitWithValidationError("server_id", "forge-cli sites list --server <server_id>", ctx.formatter);
2167
+ await runCommand(async () => {
2168
+ const token = ctx.getToken();
2169
+ const execCtx = ctx.createExecutorContext(token);
2170
+ const result = await listSites({ server_id: await resolveServerId(server, execCtx) }, execCtx);
2171
+ ctx.formatter.outputList(result.data, [
2172
+ "id",
2173
+ "name",
2174
+ "status",
2175
+ "php_version"
2176
+ ], "No sites found.", (s) => `${String(s.id).padEnd(8)} ${s.name.padEnd(40)} ${s.status.padEnd(12)} ${s.php_version}`);
2177
+ }, ctx.formatter);
2178
+ }
2179
+ async function sitesGet(args, ctx) {
2180
+ const [site_id] = args;
2181
+ const server = String(ctx.options.server ?? "");
2182
+ if (!site_id) exitWithValidationError("site_id", "forge-cli sites get <site_id> --server <server_id>", ctx.formatter);
2183
+ if (!server) exitWithValidationError("server_id", "forge-cli sites get <site_id> --server <server_id>", ctx.formatter);
2184
+ await runCommand(async () => {
2185
+ const token = ctx.getToken();
2186
+ const execCtx = ctx.createExecutorContext(token);
2187
+ const result = await getSite({
2188
+ server_id: await resolveServerId(server, execCtx),
2189
+ site_id
2190
+ }, execCtx);
2191
+ ctx.formatter.outputOne(result.data, [
2192
+ "id",
2193
+ "name",
2194
+ "status",
2195
+ "php_version",
2196
+ "project_type",
2197
+ "repository",
2198
+ "repository_branch",
2199
+ "quick_deploy",
2200
+ "deployment_status",
2201
+ "is_secured",
2202
+ "created_at"
2203
+ ]);
2204
+ }, ctx.formatter);
2205
+ }
2206
+ const handleSitesCommand = createCommandRouter({
2207
+ resource: "sites",
2208
+ handlers: {
2209
+ list: sitesList,
2210
+ ls: sitesList,
2211
+ get: [sitesGet, "args"]
2212
+ }
2213
+ });
2214
+ function showSitesHelp(subcommand) {
2215
+ if (subcommand === "list" || subcommand === "ls") console.log(`
2216
+ ${colors.bold("forge-cli sites list")} - List sites on a server
2217
+
2218
+ ${colors.bold("USAGE:")}
2219
+ forge-cli sites list --server <server_id> [options]
2220
+
2221
+ ${colors.bold("OPTIONS:")}
2222
+ --server <id> Server ID (required)
2223
+ -f, --format <fmt> Output format: json, human, table
2224
+
2225
+ ${colors.bold("EXAMPLES:")}
2226
+ forge-cli sites list --server 123
2227
+ forge-cli sites list --server 123 --format json
2228
+ `);
2229
+ else if (subcommand === "get") console.log(`
2230
+ ${colors.bold("forge-cli sites get")} - Get site details
2231
+
2232
+ ${colors.bold("USAGE:")}
2233
+ forge-cli sites get <site_id> --server <server_id> [options]
2234
+
2235
+ ${colors.bold("ARGUMENTS:")}
2236
+ <site_id> Site ID (required)
2237
+
2238
+ ${colors.bold("OPTIONS:")}
2239
+ --server <id> Server ID (required)
2240
+ -f, --format <fmt> Output format: json, human, table
2241
+ `);
2242
+ else console.log(`
2243
+ ${colors.bold("forge-cli sites")} - Manage sites
2244
+
2245
+ ${colors.bold("USAGE:")}
2246
+ forge-cli sites <subcommand> [options]
2247
+
2248
+ ${colors.bold("SUBCOMMANDS:")}
2249
+ list, ls List sites on a server
2250
+ get <id> Get site details
2251
+
2252
+ ${colors.bold("OPTIONS:")}
2253
+ --server <id> Server ID (required for most commands)
2254
+ -f, --format <fmt> Output format: json, human, table
2255
+ -h, --help Show help for a subcommand
2256
+
2257
+ ${colors.bold("EXAMPLES:")}
2258
+ forge-cli sites list --server 123
2259
+ forge-cli sites get 456 --server 123
2260
+
2261
+ Run ${colors.cyan("forge-cli sites <subcommand> --help")} for subcommand details.
2262
+ `);
2263
+ }
2264
+ async function sshKeysList(ctx) {
2265
+ const server = String(ctx.options.server ?? "");
2266
+ if (!server) exitWithValidationError("server_id", "forge-cli ssh-keys list --server <server_id>", ctx.formatter);
2267
+ await runCommand(async () => {
2268
+ const token = ctx.getToken();
2269
+ const execCtx = ctx.createExecutorContext(token);
2270
+ const result = await listSshKeys({ server_id: await resolveServerId(server, execCtx) }, execCtx);
2271
+ ctx.formatter.outputList(result.data, [
2272
+ "id",
2273
+ "name",
2274
+ "status"
2275
+ ], "No SSH keys found.", (k) => `${String(k.id).padEnd(8)} ${k.name.padEnd(30)} ${k.status}`);
2276
+ }, ctx.formatter);
2277
+ }
2278
+ async function sshKeysGet(args, ctx) {
2279
+ const [id] = args;
2280
+ const server = String(ctx.options.server ?? "");
2281
+ if (!id) exitWithValidationError("ssh_key_id", "forge-cli ssh-keys get <key_id> --server <server_id>", ctx.formatter);
2282
+ if (!server) exitWithValidationError("server_id", "forge-cli ssh-keys get <key_id> --server <server_id>", ctx.formatter);
2283
+ await runCommand(async () => {
2284
+ const token = ctx.getToken();
2285
+ const execCtx = ctx.createExecutorContext(token);
2286
+ const result = await getSshKey({
2287
+ server_id: await resolveServerId(server, execCtx),
2288
+ id
2289
+ }, execCtx);
2290
+ ctx.formatter.outputOne(result.data, [
2291
+ "id",
2292
+ "name",
2293
+ "status",
2294
+ "created_at"
2295
+ ]);
2296
+ }, ctx.formatter);
2297
+ }
2298
+ const handleSshKeysCommand = createCommandRouter({
2299
+ resource: "ssh-keys",
2300
+ handlers: {
2301
+ list: sshKeysList,
2302
+ ls: sshKeysList,
2303
+ get: [sshKeysGet, "args"]
2304
+ }
2305
+ });
2306
+ function showSshKeysHelp(subcommand) {
2307
+ if (subcommand === "list" || subcommand === "ls") console.log(`
2308
+ ${colors.bold("forge-cli ssh-keys list")} - List SSH keys on a server
2309
+
2310
+ ${colors.bold("USAGE:")}
2311
+ forge-cli ssh-keys list --server <server_id> [options]
2312
+ `);
2313
+ else if (subcommand === "get") console.log(`
2314
+ ${colors.bold("forge-cli ssh-keys get")} - Get SSH key details
2315
+
2316
+ ${colors.bold("USAGE:")}
2317
+ forge-cli ssh-keys get <key_id> --server <server_id>
2318
+ `);
2319
+ else console.log(`
2320
+ ${colors.bold("forge-cli ssh-keys")} - Manage SSH keys
2321
+
2322
+ ${colors.bold("USAGE:")}
2323
+ forge-cli ssh-keys <subcommand> [options]
2324
+
2325
+ ${colors.bold("SUBCOMMANDS:")}
2326
+ list, ls List SSH keys
2327
+ get <id> Get SSH key details
2328
+
2329
+ ${colors.bold("OPTIONS:")}
2330
+ --server <id> Server ID (required)
2331
+ -f, --format <fmt> Output format: json, human, table
2332
+
2333
+ Run ${colors.cyan("forge-cli ssh-keys <subcommand> --help")} for details.
2334
+ `);
2335
+ }
2336
+ async function userGet(ctx) {
2337
+ await runCommand(async () => {
2338
+ const token = ctx.getToken();
2339
+ const result = await getUser({}, ctx.createExecutorContext(token));
2340
+ ctx.formatter.output(result.data);
2341
+ }, ctx.formatter);
2342
+ }
2343
+ const handleUserCommand = createCommandRouter({
2344
+ resource: "user",
2345
+ handlers: { get: userGet }
2346
+ });
2347
+ function showUserHelp(subcommand) {
2348
+ if (subcommand === "get") console.log(`
2349
+ ${colors.bold("forge-cli user get")} - Get the authenticated user profile
2350
+
2351
+ ${colors.bold("USAGE:")}
2352
+ forge-cli user get [options]
2353
+
2354
+ ${colors.bold("OPTIONS:")}
2355
+ -f, --format <fmt> Output format: json, human, table
2356
+ `);
2357
+ else console.log(`
2358
+ ${colors.bold("forge-cli user")} - Display authenticated user profile
2359
+
2360
+ ${colors.bold("USAGE:")}
2361
+ forge-cli user [subcommand] [options]
2362
+
2363
+ ${colors.bold("SUBCOMMANDS:")}
2364
+ get Get the authenticated user profile
2365
+
2366
+ ${colors.bold("OPTIONS:")}
2367
+ -f, --format <fmt> Output format: json, human, table
2368
+
2369
+ Run ${colors.cyan("forge-cli user <subcommand> --help")} for details.
2370
+ `);
2371
+ }
2372
+ function parseArgs(argv = process.argv.slice(2)) {
2373
+ const options = {};
2374
+ const positional = [];
2375
+ const command = [];
2376
+ for (let i = 0; i < argv.length; i++) {
2377
+ const arg = argv[i];
2378
+ if (arg.startsWith("--")) {
2379
+ const equalIndex = arg.indexOf("=");
2380
+ if (equalIndex > -1) {
2381
+ const key = arg.slice(2, equalIndex);
2382
+ options[key] = arg.slice(equalIndex + 1);
2383
+ } else {
2384
+ const key = arg.slice(2);
2385
+ const nextArg = argv[i + 1];
2386
+ if (nextArg !== void 0 && !nextArg.startsWith("-")) {
2387
+ options[key] = nextArg;
2388
+ i++;
2389
+ } else options[key] = true;
2390
+ }
2391
+ } else if (arg.startsWith("-") && arg.length > 1) if (arg.length === 2) {
2392
+ const key = arg[1];
2393
+ const nextArg = argv[i + 1];
2394
+ if (nextArg !== void 0 && !nextArg.startsWith("-")) {
2395
+ options[key] = nextArg;
2396
+ i++;
2397
+ } else options[key] = true;
2398
+ } else for (let j = 1; j < arg.length; j++) options[arg[j]] = true;
2399
+ else if (command.length < 2) command.push(arg);
2400
+ else positional.push(arg);
2401
+ }
2402
+ return {
2403
+ command,
2404
+ options,
2405
+ positional
2406
+ };
2407
+ }
2408
+ var VERSION = "0.1.0";
2409
+ function showHelp() {
2410
+ console.log(`
2411
+ ${colors.bold("forge-cli")} v${VERSION}
2412
+
2413
+ ${colors.bold("USAGE:")}
2414
+ forge-cli <command> [subcommand] [options]
2415
+
2416
+ ${colors.bold("COMMANDS:")}
2417
+ config Manage CLI configuration
2418
+ set <token> Save API token
2419
+ get Show current token (masked)
2420
+ delete Delete stored token
2421
+
2422
+ servers, s Manage servers
2423
+ list, ls List all servers
2424
+ get <id> Get server details
2425
+ reboot <id> Reboot a server
2426
+
2427
+ sites Manage sites
2428
+ list, ls List sites (requires --server)
2429
+ get <id> Get site details (requires --server)
2430
+
2431
+ deployments, d Manage deployments
2432
+ list, ls List deployments (requires --server --site)
2433
+ deploy Trigger a deployment (requires --server --site)
2434
+
2435
+ databases, db Manage databases
2436
+ list, ls List databases (requires --server)
2437
+ get <id> Get database details (requires --server)
2438
+
2439
+ database-users Manage database users
2440
+ list, ls List database users (requires --server)
2441
+ get <id> Get database user details (requires --server)
2442
+ create Create a database user (requires --server --name --password)
2443
+ delete <id> Delete a database user (requires --server)
2444
+
2445
+ daemons Manage daemons
2446
+ list, ls List daemons (requires --server)
2447
+ get <id> Get daemon details (requires --server)
2448
+ restart <id> Restart a daemon (requires --server)
2449
+
2450
+ env Manage environment variables
2451
+ get Get env variables (requires --server --site)
2452
+ update Update env variables (requires --server --site --content)
2453
+
2454
+ nginx Manage nginx configuration
2455
+ get Get nginx config (requires --server --site)
2456
+ update Update nginx config (requires --server --site --content)
2457
+
2458
+ certificates, certs Manage SSL certificates
2459
+ list, ls List certificates (requires --server --site)
2460
+ get <id> Get certificate details (requires --server --site)
2461
+ activate <id> Activate a certificate (requires --server --site)
2462
+
2463
+ firewall-rules, fw Manage firewall rules
2464
+ list, ls List firewall rules (requires --server)
2465
+ get <id> Get firewall rule details (requires --server)
2466
+
2467
+ ssh-keys Manage SSH keys
2468
+ list, ls List SSH keys (requires --server)
2469
+ get <id> Get SSH key details (requires --server)
2470
+
2471
+ backups Manage backup configurations
2472
+ list, ls List backup configurations (requires --server)
2473
+ get <id> Get backup configuration details (requires --server)
2474
+ create Create a backup configuration (requires --server --provider --frequency)
2475
+ delete <id> Delete a backup configuration (requires --server)
2476
+
2477
+ commands Manage site commands
2478
+ list, ls List commands (requires --server --site)
2479
+ get <id> Get command details (requires --server --site)
2480
+ create Execute a command (requires --server --site --command)
2481
+
2482
+ scheduled-jobs Manage scheduled jobs (cron)
2483
+ list, ls List scheduled jobs (requires --server)
2484
+ get <id> Get scheduled job details (requires --server)
2485
+ create Create a scheduled job (requires --server --command)
2486
+ delete <id> Delete a scheduled job (requires --server)
2487
+
2488
+ user Display authenticated user profile
2489
+ get Get the authenticated user profile
2490
+
2491
+ monitors Manage server monitors
2492
+ list, ls List monitors (requires --server)
2493
+ get <id> Get monitor details (requires --server)
2494
+ create Create a monitor (requires --server --type --operator --threshold --minutes)
2495
+ delete <id> Delete a monitor (requires --server)
2496
+
2497
+ nginx-templates Manage Nginx templates
2498
+ list, ls List Nginx templates (requires --server)
2499
+ get <id> Get Nginx template details (requires --server)
2500
+ create Create a Nginx template (requires --server --name --content)
2501
+ update <id> Update a Nginx template (requires --server)
2502
+ delete <id> Delete a Nginx template (requires --server)
2503
+
2504
+ security-rules Manage site security rules
2505
+ list, ls List security rules (requires --server --site)
2506
+ get <id> Get security rule details (requires --server --site)
2507
+ create Create a security rule (requires --server --site --name)
2508
+ delete <id> Delete a security rule (requires --server --site)
2509
+
2510
+ redirect-rules Manage site redirect rules
2511
+ list, ls List redirect rules (requires --server --site)
2512
+ get <id> Get redirect rule details (requires --server --site)
2513
+ create Create a redirect rule (requires --server --site --from --to)
2514
+ delete <id> Delete a redirect rule (requires --server --site)
2515
+
2516
+ recipes Manage recipes
2517
+ list, ls List all recipes
2518
+ get <id> Get recipe details
2519
+ run <id> Run a recipe (requires --servers)
2520
+
2521
+ ${colors.bold("OPTIONS:")}
2522
+ --token <token> Forge API token (overrides config and env)
2523
+ --server <id> Server ID (required for server-scoped commands)
2524
+ --site <id> Site ID (required for site-scoped commands)
2525
+ -f, --format <fmt> Output format: json, human, table (default: human)
2526
+ --no-color Disable colored output
2527
+ -h, --help Show help
2528
+ -v, --version Show version
2529
+
2530
+ ${colors.bold("EXAMPLES:")}
2531
+ # Configure API token
2532
+ forge-cli config set YOUR_TOKEN
2533
+
2534
+ # Or pass token directly
2535
+ forge-cli servers list --token YOUR_TOKEN
2536
+
2537
+ # List all servers
2538
+ forge-cli servers list
2539
+
2540
+ # List sites on a server
2541
+ forge-cli sites list --server 123
2542
+
2543
+ # Deploy a site
2544
+ forge-cli deployments deploy --server 123 --site 456
2545
+
2546
+ # Get JSON output (for AI agents)
2547
+ forge-cli servers list --format json
2548
+
2549
+ ${colors.bold("CREDENTIAL PRIORITY:")}
2550
+ 1. CLI argument (--token)
2551
+ 2. Environment variable (FORGE_API_TOKEN)
2552
+ 3. Config file (~/.config/forge-tools/config.json)
2553
+
2554
+ ${colors.bold("ENVIRONMENT VARIABLES:")}
2555
+ FORGE_API_TOKEN API token
2556
+ NO_COLOR Disable colors
2557
+ XDG_CONFIG_HOME Config directory (respects XDG spec)
2558
+ `);
2559
+ }
2560
+ async function main() {
2561
+ const { command, options, positional } = parseArgs();
2562
+ const [mainCommand, subcommand] = command;
2563
+ const wantsHelp = options.help !== void 0 || options.h !== void 0;
2564
+ if (wantsHelp && !mainCommand) {
2565
+ showHelp();
2566
+ process.exit(0);
2567
+ }
2568
+ if (options.version || options.v) {
2569
+ console.log(VERSION);
2570
+ process.exit(0);
2571
+ }
2572
+ if (options["no-color"]) setColorEnabled(false);
2573
+ if (!mainCommand) {
2574
+ showHelp();
2575
+ process.exit(0);
2576
+ }
2577
+ try {
2578
+ switch (mainCommand) {
2579
+ case "config":
2580
+ if (wantsHelp) {
2581
+ showConfigHelp();
2582
+ process.exit(0);
2583
+ }
2584
+ handleConfigCommand(subcommand ?? "get", positional, options);
2585
+ break;
2586
+ case "servers":
2587
+ case "s":
2588
+ if (wantsHelp) {
2589
+ showServersHelp(subcommand);
2590
+ process.exit(0);
2591
+ }
2592
+ await handleServersCommand(subcommand ?? "list", positional, options);
2593
+ break;
2594
+ case "sites":
2595
+ if (wantsHelp) {
2596
+ showSitesHelp(subcommand);
2597
+ process.exit(0);
2598
+ }
2599
+ await handleSitesCommand(subcommand ?? "list", positional, options);
2600
+ break;
2601
+ case "deployments":
2602
+ case "d":
2603
+ if (wantsHelp) {
2604
+ showDeploymentsHelp(subcommand);
2605
+ process.exit(0);
2606
+ }
2607
+ await handleDeploymentsCommand(subcommand ?? "list", positional, options);
2608
+ break;
2609
+ case "databases":
2610
+ case "db":
2611
+ if (wantsHelp) {
2612
+ showDatabasesHelp(subcommand);
2613
+ process.exit(0);
2614
+ }
2615
+ await handleDatabasesCommand(subcommand ?? "list", positional, options);
2616
+ break;
2617
+ case "database-users":
2618
+ if (wantsHelp) {
2619
+ showDatabaseUsersHelp(subcommand);
2620
+ process.exit(0);
2621
+ }
2622
+ await handleDatabaseUsersCommand(subcommand ?? "list", positional, options);
2623
+ break;
2624
+ case "daemons":
2625
+ if (wantsHelp) {
2626
+ showDaemonsHelp(subcommand);
2627
+ process.exit(0);
2628
+ }
2629
+ await handleDaemonsCommand(subcommand ?? "list", positional, options);
2630
+ break;
2631
+ case "env":
2632
+ if (wantsHelp) {
2633
+ showEnvHelp(subcommand);
2634
+ process.exit(0);
2635
+ }
2636
+ await handleEnvCommand(subcommand ?? "get", positional, options);
2637
+ break;
2638
+ case "nginx":
2639
+ if (wantsHelp) {
2640
+ showNginxHelp(subcommand);
2641
+ process.exit(0);
2642
+ }
2643
+ await handleNginxCommand(subcommand ?? "get", positional, options);
2644
+ break;
2645
+ case "certificates":
2646
+ case "certs":
2647
+ if (wantsHelp) {
2648
+ showCertificatesHelp(subcommand);
2649
+ process.exit(0);
2650
+ }
2651
+ await handleCertificatesCommand(subcommand ?? "list", positional, options);
2652
+ break;
2653
+ case "firewall-rules":
2654
+ case "fw":
2655
+ if (wantsHelp) {
2656
+ showFirewallRulesHelp(subcommand);
2657
+ process.exit(0);
2658
+ }
2659
+ await handleFirewallRulesCommand(subcommand ?? "list", positional, options);
2660
+ break;
2661
+ case "ssh-keys":
2662
+ if (wantsHelp) {
2663
+ showSshKeysHelp(subcommand);
2664
+ process.exit(0);
2665
+ }
2666
+ await handleSshKeysCommand(subcommand ?? "list", positional, options);
2667
+ break;
2668
+ case "backups":
2669
+ if (wantsHelp) {
2670
+ showBackupsHelp(subcommand);
2671
+ process.exit(0);
2672
+ }
2673
+ await handleBackupsCommand(subcommand ?? "list", positional, options);
2674
+ break;
2675
+ case "commands":
2676
+ if (wantsHelp) {
2677
+ showCommandsHelp(subcommand);
2678
+ process.exit(0);
2679
+ }
2680
+ await handleCommandsCommand(subcommand ?? "list", positional, options);
2681
+ break;
2682
+ case "scheduled-jobs":
2683
+ if (wantsHelp) {
2684
+ showScheduledJobsHelp(subcommand);
2685
+ process.exit(0);
2686
+ }
2687
+ await handleScheduledJobsCommand(subcommand ?? "list", positional, options);
2688
+ break;
2689
+ case "user":
2690
+ if (wantsHelp) {
2691
+ showUserHelp(subcommand);
2692
+ process.exit(0);
2693
+ }
2694
+ await handleUserCommand(subcommand ?? "get", positional, options);
2695
+ break;
2696
+ case "monitors":
2697
+ if (wantsHelp) {
2698
+ showMonitorsHelp(subcommand);
2699
+ process.exit(0);
2700
+ }
2701
+ await handleMonitorsCommand(subcommand ?? "list", positional, options);
2702
+ break;
2703
+ case "nginx-templates":
2704
+ if (wantsHelp) {
2705
+ showNginxTemplatesHelp(subcommand);
2706
+ process.exit(0);
2707
+ }
2708
+ await handleNginxTemplatesCommand(subcommand ?? "list", positional, options);
2709
+ break;
2710
+ case "security-rules":
2711
+ if (wantsHelp) {
2712
+ showSecurityRulesHelp(subcommand);
2713
+ process.exit(0);
2714
+ }
2715
+ await handleSecurityRulesCommand(subcommand ?? "list", positional, options);
2716
+ break;
2717
+ case "redirect-rules":
2718
+ if (wantsHelp) {
2719
+ showRedirectRulesHelp(subcommand);
2720
+ process.exit(0);
2721
+ }
2722
+ await handleRedirectRulesCommand(subcommand ?? "list", positional, options);
2723
+ break;
2724
+ case "recipes":
2725
+ if (wantsHelp) {
2726
+ showRecipesHelp(subcommand);
2727
+ process.exit(0);
2728
+ }
2729
+ await handleRecipesCommand(subcommand ?? "list", positional, options);
2730
+ break;
2731
+ default:
2732
+ console.error(colors.red(`Unknown command: ${mainCommand}`));
2733
+ console.log(`Run ${colors.cyan("forge-cli --help")} for usage information`);
2734
+ process.exit(1);
2735
+ }
2736
+ } catch (error) {
2737
+ console.error(colors.red("Fatal error:"), error);
2738
+ process.exit(1);
2739
+ }
2740
+ }
2741
+ main().catch((error) => {
2742
+ console.error(colors.red("Unhandled error:"), error);
2743
+ process.exit(1);
2744
+ });
2745
+
2746
+ //# sourceMappingURL=cli.js.map