@studiometa/forge-mcp 0.3.0 → 0.4.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 (57) hide show
  1. package/README.md +7 -6
  2. package/dist/formatters.d.ts +101 -36
  3. package/dist/formatters.d.ts.map +1 -1
  4. package/dist/handlers/backups.d.ts.map +1 -1
  5. package/dist/handlers/batch.d.ts.map +1 -1
  6. package/dist/handlers/certificates.d.ts.map +1 -1
  7. package/dist/handlers/commands.d.ts.map +1 -1
  8. package/dist/handlers/context.d.ts.map +1 -1
  9. package/dist/handlers/daemons.d.ts.map +1 -1
  10. package/dist/handlers/database-users.d.ts.map +1 -1
  11. package/dist/handlers/databases.d.ts.map +1 -1
  12. package/dist/handlers/deployments.d.ts.map +1 -1
  13. package/dist/handlers/env.d.ts.map +1 -1
  14. package/dist/handlers/factory.d.ts +7 -37
  15. package/dist/handlers/factory.d.ts.map +1 -1
  16. package/dist/handlers/firewall-rules.d.ts.map +1 -1
  17. package/dist/handlers/help.d.ts.map +1 -1
  18. package/dist/handlers/index.d.ts +1 -0
  19. package/dist/handlers/index.d.ts.map +1 -1
  20. package/dist/handlers/monitors.d.ts.map +1 -1
  21. package/dist/handlers/nginx-config.d.ts.map +1 -1
  22. package/dist/handlers/nginx-templates.d.ts.map +1 -1
  23. package/dist/handlers/recipes.d.ts.map +1 -1
  24. package/dist/handlers/redirect-rules.d.ts.map +1 -1
  25. package/dist/handlers/scheduled-jobs.d.ts.map +1 -1
  26. package/dist/handlers/security-rules.d.ts.map +1 -1
  27. package/dist/handlers/servers.d.ts.map +1 -1
  28. package/dist/handlers/sites.d.ts.map +1 -1
  29. package/dist/handlers/ssh-keys.d.ts.map +1 -1
  30. package/dist/handlers/user.d.ts.map +1 -1
  31. package/dist/handlers/utils.d.ts +1 -1
  32. package/dist/handlers/utils.d.ts.map +1 -1
  33. package/dist/hints.d.ts +1 -1
  34. package/dist/hints.d.ts.map +1 -1
  35. package/dist/{http-Cwp91mT-.js → http-K9wiprZX.js} +16 -12
  36. package/dist/http-K9wiprZX.js.map +1 -0
  37. package/dist/http.d.ts +1 -1
  38. package/dist/http.d.ts.map +1 -1
  39. package/dist/http.js +2 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +21 -10
  42. package/dist/index.js.map +1 -1
  43. package/dist/oauth.d.ts +1 -1
  44. package/dist/oauth.d.ts.map +1 -1
  45. package/dist/oauth.js +42 -41
  46. package/dist/oauth.js.map +1 -1
  47. package/dist/server.js +2 -2
  48. package/dist/sessions.d.ts.map +1 -1
  49. package/dist/stdio.d.ts +4 -2
  50. package/dist/stdio.d.ts.map +1 -1
  51. package/dist/tools.d.ts.map +1 -1
  52. package/dist/{version-CIiN0iJr.js → version-CHrJu_54.js} +922 -364
  53. package/dist/version-CHrJu_54.js.map +1 -0
  54. package/package.json +4 -4
  55. package/skills/SKILL.md +14 -12
  56. package/dist/http-Cwp91mT-.js.map +0 -1
  57. package/dist/version-CIiN0iJr.js.map +0 -1
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { HttpClient, isForgeApiError } from "@studiometa/forge-api";
5
- import { RESOURCES, activateCertificate, createAuditLogger, createBackupConfig, createCertificate, createCommand, createDaemon, createDatabase, createDatabaseUser, createFirewallRule, createMonitor, createNginxTemplate, createRecipe, createRedirectRule, createScheduledJob, createSecurityRule, createServer, createSite, createSshKey, deleteBackupConfig, deleteCertificate, deleteDaemon, deleteDatabase, deleteDatabaseUser, deleteFirewallRule, deleteMonitor, deleteNginxTemplate, deleteRecipe, deleteRedirectRule, deleteScheduledJob, deleteSecurityRule, deleteServer, deleteSite, deleteSshKey, deploySiteAndWait, getBackupConfig, getCertificate, getCommand, getDaemon, getDatabase, getDatabaseUser, getDeploymentOutput, getDeploymentScript, 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, resolveServers, resolveSites, restartDaemon, runRecipe, updateDeploymentScript, updateEnv, updateNginxConfig, updateNginxTemplate } from "@studiometa/forge-core";
5
+ import { RESOURCES, activateCertificate, createAuditLogger, createBackupConfig, createCertificate, createCommand, createDaemon, createDatabase, createDatabaseUser, createFirewallRule, createMonitor, createNginxTemplate, createRecipe, createRedirectRule, createScheduledJob, createSecurityRule, createServer, createSite, createSshKey, deleteBackupConfig, deleteCertificate, deleteDaemon, deleteDatabase, deleteDatabaseUser, deleteFirewallRule, deleteMonitor, deleteNginxTemplate, deleteRecipe, deleteRedirectRule, deleteScheduledJob, deleteSecurityRule, deleteServer, deleteSite, deleteSshKey, deploySiteAndWait, getBackupConfig, getCertificate, getCommand, getDaemon, getDatabase, getDatabaseUser, getDeploymentLog, getDeploymentScript, getEnv, getFirewallRule, getMonitor, getNginxConfig, getNginxTemplate, getRecipe, getRedirectRule, getScheduledJob, getSecurityRule, getServer, getSite, getSshKey, getUser, listBackupConfigs, listCommands, listDaemons, listDatabaseUsers, listDatabases, listDeployments, listFirewallRules, listMonitors, listNginxTemplates, listRecipes, listRedirectRules, listScheduledJobs, listSecurityRules, listServers, listSites, listSshKeys, rebootServer, resolveServers, resolveSites, restartDaemon, runRecipe, updateBackupConfig, updateDaemon, updateDatabaseUser, updateDeploymentScript, updateEnv, updateNginxConfig, updateNginxTemplate, updateRecipe, updateSecurityRule, updateSite } from "@studiometa/forge-core";
6
6
  /**
7
7
  * MCP Server Instructions
8
8
  *
@@ -25,6 +25,398 @@ function loadInstructions() {
25
25
  }
26
26
  }
27
27
  const INSTRUCTIONS = loadInstructions();
28
+ var store$4;
29
+ /**
30
+ * Returns the global configuration.
31
+ *
32
+ * @param config The config to merge.
33
+ *
34
+ * @returns The configuration.
35
+ */
36
+ /* @__NO_SIDE_EFFECTS__ */
37
+ function getGlobalConfig(config$1) {
38
+ return {
39
+ lang: config$1?.lang ?? store$4?.lang,
40
+ message: config$1?.message,
41
+ abortEarly: config$1?.abortEarly ?? store$4?.abortEarly,
42
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$4?.abortPipeEarly
43
+ };
44
+ }
45
+ var store$3;
46
+ /**
47
+ * Returns a global error message.
48
+ *
49
+ * @param lang The language of the message.
50
+ *
51
+ * @returns The error message.
52
+ */
53
+ /* @__NO_SIDE_EFFECTS__ */
54
+ function getGlobalMessage(lang) {
55
+ return store$3?.get(lang);
56
+ }
57
+ var store$2;
58
+ /**
59
+ * Returns a schema error message.
60
+ *
61
+ * @param lang The language of the message.
62
+ *
63
+ * @returns The error message.
64
+ */
65
+ /* @__NO_SIDE_EFFECTS__ */
66
+ function getSchemaMessage(lang) {
67
+ return store$2?.get(lang);
68
+ }
69
+ var store$1;
70
+ /**
71
+ * Returns a specific error message.
72
+ *
73
+ * @param reference The identifier reference.
74
+ * @param lang The language of the message.
75
+ *
76
+ * @returns The error message.
77
+ */
78
+ /* @__NO_SIDE_EFFECTS__ */
79
+ function getSpecificMessage(reference, lang) {
80
+ return store$1?.get(reference)?.get(lang);
81
+ }
82
+ /**
83
+ * Stringifies an unknown input to a literal or type string.
84
+ *
85
+ * @param input The unknown input.
86
+ *
87
+ * @returns A literal or type string.
88
+ *
89
+ * @internal
90
+ */
91
+ /* @__NO_SIDE_EFFECTS__ */
92
+ function _stringify(input) {
93
+ const type = typeof input;
94
+ if (type === "string") return `"${input}"`;
95
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
96
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
97
+ return type;
98
+ }
99
+ /**
100
+ * Adds an issue to the dataset.
101
+ *
102
+ * @param context The issue context.
103
+ * @param label The issue label.
104
+ * @param dataset The input dataset.
105
+ * @param config The configuration.
106
+ * @param other The optional props.
107
+ *
108
+ * @internal
109
+ */
110
+ function _addIssue(context, label, dataset, config$1, other) {
111
+ const input = other && "input" in other ? other.input : dataset.value;
112
+ const expected = other?.expected ?? context.expects ?? null;
113
+ const received = other?.received ?? /* @__PURE__ */ _stringify(input);
114
+ const issue = {
115
+ kind: context.kind,
116
+ type: context.type,
117
+ input,
118
+ expected,
119
+ received,
120
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
121
+ requirement: context.requirement,
122
+ path: other?.path,
123
+ issues: other?.issues,
124
+ lang: config$1.lang,
125
+ abortEarly: config$1.abortEarly,
126
+ abortPipeEarly: config$1.abortPipeEarly
127
+ };
128
+ const isSchema = context.kind === "schema";
129
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage(issue.lang);
130
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
131
+ if (isSchema) dataset.typed = false;
132
+ if (dataset.issues) dataset.issues.push(issue);
133
+ else dataset.issues = [issue];
134
+ }
135
+ /**
136
+ * Returns the Standard Schema properties.
137
+ *
138
+ * @param context The schema context.
139
+ *
140
+ * @returns The Standard Schema properties.
141
+ */
142
+ /* @__NO_SIDE_EFFECTS__ */
143
+ function _getStandardProps(context) {
144
+ return {
145
+ version: 1,
146
+ vendor: "valibot",
147
+ validate(value$1) {
148
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig());
149
+ }
150
+ };
151
+ }
152
+ /**
153
+ * Returns the fallback value of the schema.
154
+ *
155
+ * @param schema The schema to get it from.
156
+ * @param dataset The output dataset if available.
157
+ * @param config The config if available.
158
+ *
159
+ * @returns The fallback value.
160
+ */
161
+ /* @__NO_SIDE_EFFECTS__ */
162
+ function getFallback(schema, dataset, config$1) {
163
+ return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
164
+ }
165
+ /**
166
+ * Returns the default value of the schema.
167
+ *
168
+ * @param schema The schema to get it from.
169
+ * @param dataset The input dataset if available.
170
+ * @param config The config if available.
171
+ *
172
+ * @returns The default value.
173
+ */
174
+ /* @__NO_SIDE_EFFECTS__ */
175
+ function getDefault(schema, dataset, config$1) {
176
+ return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
177
+ }
178
+ /* @__NO_SIDE_EFFECTS__ */
179
+ function array(item, message$1) {
180
+ return {
181
+ kind: "schema",
182
+ type: "array",
183
+ reference: array,
184
+ expects: "Array",
185
+ async: false,
186
+ item,
187
+ message: message$1,
188
+ get "~standard"() {
189
+ return /* @__PURE__ */ _getStandardProps(this);
190
+ },
191
+ "~run"(dataset, config$1) {
192
+ const input = dataset.value;
193
+ if (Array.isArray(input)) {
194
+ dataset.typed = true;
195
+ dataset.value = [];
196
+ for (let key = 0; key < input.length; key++) {
197
+ const value$1 = input[key];
198
+ const itemDataset = this.item["~run"]({ value: value$1 }, config$1);
199
+ if (itemDataset.issues) {
200
+ const pathItem = {
201
+ type: "array",
202
+ origin: "value",
203
+ input,
204
+ key,
205
+ value: value$1
206
+ };
207
+ for (const issue of itemDataset.issues) {
208
+ if (issue.path) issue.path.unshift(pathItem);
209
+ else issue.path = [pathItem];
210
+ dataset.issues?.push(issue);
211
+ }
212
+ if (!dataset.issues) dataset.issues = itemDataset.issues;
213
+ if (config$1.abortEarly) {
214
+ dataset.typed = false;
215
+ break;
216
+ }
217
+ }
218
+ if (!itemDataset.typed) dataset.typed = false;
219
+ dataset.value.push(itemDataset.value);
220
+ }
221
+ } else _addIssue(this, "type", dataset, config$1);
222
+ return dataset;
223
+ }
224
+ };
225
+ }
226
+ /* @__NO_SIDE_EFFECTS__ */
227
+ function nullable(wrapped, default_) {
228
+ return {
229
+ kind: "schema",
230
+ type: "nullable",
231
+ reference: nullable,
232
+ expects: `(${wrapped.expects} | null)`,
233
+ async: false,
234
+ wrapped,
235
+ default: default_,
236
+ get "~standard"() {
237
+ return /* @__PURE__ */ _getStandardProps(this);
238
+ },
239
+ "~run"(dataset, config$1) {
240
+ if (dataset.value === null) {
241
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault(this, dataset, config$1);
242
+ if (dataset.value === null) {
243
+ dataset.typed = true;
244
+ return dataset;
245
+ }
246
+ }
247
+ return this.wrapped["~run"](dataset, config$1);
248
+ }
249
+ };
250
+ }
251
+ /* @__NO_SIDE_EFFECTS__ */
252
+ function number(message$1) {
253
+ return {
254
+ kind: "schema",
255
+ type: "number",
256
+ reference: number,
257
+ expects: "number",
258
+ async: false,
259
+ message: message$1,
260
+ get "~standard"() {
261
+ return /* @__PURE__ */ _getStandardProps(this);
262
+ },
263
+ "~run"(dataset, config$1) {
264
+ if (typeof dataset.value === "number" && !isNaN(dataset.value)) dataset.typed = true;
265
+ else _addIssue(this, "type", dataset, config$1);
266
+ return dataset;
267
+ }
268
+ };
269
+ }
270
+ /* @__NO_SIDE_EFFECTS__ */
271
+ function object(entries$1, message$1) {
272
+ return {
273
+ kind: "schema",
274
+ type: "object",
275
+ reference: object,
276
+ expects: "Object",
277
+ async: false,
278
+ entries: entries$1,
279
+ message: message$1,
280
+ get "~standard"() {
281
+ return /* @__PURE__ */ _getStandardProps(this);
282
+ },
283
+ "~run"(dataset, config$1) {
284
+ const input = dataset.value;
285
+ if (input && typeof input === "object") {
286
+ dataset.typed = true;
287
+ dataset.value = {};
288
+ for (const key in this.entries) {
289
+ const valueSchema = this.entries[key];
290
+ if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== void 0) {
291
+ const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault(valueSchema);
292
+ const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
293
+ if (valueDataset.issues) {
294
+ const pathItem = {
295
+ type: "object",
296
+ origin: "value",
297
+ input,
298
+ key,
299
+ value: value$1
300
+ };
301
+ for (const issue of valueDataset.issues) {
302
+ if (issue.path) issue.path.unshift(pathItem);
303
+ else issue.path = [pathItem];
304
+ dataset.issues?.push(issue);
305
+ }
306
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
307
+ if (config$1.abortEarly) {
308
+ dataset.typed = false;
309
+ break;
310
+ }
311
+ }
312
+ if (!valueDataset.typed) dataset.typed = false;
313
+ dataset.value[key] = valueDataset.value;
314
+ } else if (valueSchema.fallback !== void 0) dataset.value[key] = /* @__PURE__ */ getFallback(valueSchema);
315
+ else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
316
+ _addIssue(this, "key", dataset, config$1, {
317
+ input: void 0,
318
+ expected: `"${key}"`,
319
+ path: [{
320
+ type: "object",
321
+ origin: "key",
322
+ input,
323
+ key,
324
+ value: input[key]
325
+ }]
326
+ });
327
+ if (config$1.abortEarly) break;
328
+ }
329
+ }
330
+ } else _addIssue(this, "type", dataset, config$1);
331
+ return dataset;
332
+ }
333
+ };
334
+ }
335
+ /* @__NO_SIDE_EFFECTS__ */
336
+ function optional(wrapped, default_) {
337
+ return {
338
+ kind: "schema",
339
+ type: "optional",
340
+ reference: optional,
341
+ expects: `(${wrapped.expects} | undefined)`,
342
+ async: false,
343
+ wrapped,
344
+ default: default_,
345
+ get "~standard"() {
346
+ return /* @__PURE__ */ _getStandardProps(this);
347
+ },
348
+ "~run"(dataset, config$1) {
349
+ if (dataset.value === void 0) {
350
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault(this, dataset, config$1);
351
+ if (dataset.value === void 0) {
352
+ dataset.typed = true;
353
+ return dataset;
354
+ }
355
+ }
356
+ return this.wrapped["~run"](dataset, config$1);
357
+ }
358
+ };
359
+ }
360
+ /* @__NO_SIDE_EFFECTS__ */
361
+ function string(message$1) {
362
+ return {
363
+ kind: "schema",
364
+ type: "string",
365
+ reference: string,
366
+ expects: "string",
367
+ async: false,
368
+ message: message$1,
369
+ get "~standard"() {
370
+ return /* @__PURE__ */ _getStandardProps(this);
371
+ },
372
+ "~run"(dataset, config$1) {
373
+ if (typeof dataset.value === "string") dataset.typed = true;
374
+ else _addIssue(this, "type", dataset, config$1);
375
+ return dataset;
376
+ }
377
+ };
378
+ }
379
+ /**
380
+ * Creates a unknown schema.
381
+ *
382
+ * @returns A unknown schema.
383
+ */
384
+ /* @__NO_SIDE_EFFECTS__ */
385
+ function unknown() {
386
+ return {
387
+ kind: "schema",
388
+ type: "unknown",
389
+ reference: unknown,
390
+ expects: "unknown",
391
+ async: false,
392
+ get "~standard"() {
393
+ return /* @__PURE__ */ _getStandardProps(this);
394
+ },
395
+ "~run"(dataset) {
396
+ dataset.typed = true;
397
+ return dataset;
398
+ }
399
+ };
400
+ }
401
+ /**
402
+ * Parses an unknown input based on a schema.
403
+ *
404
+ * @param schema The schema to be used.
405
+ * @param input The input to be parsed.
406
+ * @param config The parse configuration.
407
+ *
408
+ * @returns The parse result.
409
+ */
410
+ /* @__NO_SIDE_EFFECTS__ */
411
+ function safeParse(schema, input, config$1) {
412
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
413
+ return {
414
+ typed: dataset.typed,
415
+ success: !dataset.issues,
416
+ output: dataset.value,
417
+ issues: dataset.issues
418
+ };
419
+ }
28
420
  /**
29
421
  * Format a list of servers.
30
422
  */
@@ -52,7 +444,7 @@ function formatServer(server) {
52
444
  */
53
445
  function formatSiteList(sites, serverId) {
54
446
  if (sites.length === 0) return serverId ? `No sites on server ${serverId}.` : "No sites found.";
55
- const lines = sites.map((s) => `• ${s.name} (ID: ${s.id}) — ${s.project_type} — ${s.status}`);
447
+ const lines = sites.map((s) => `• ${s.name} (ID: ${s.id}) — ${s.app_type} — ${s.status}`);
56
448
  return `${serverId ? `${sites.length} site(s) on server ${serverId}:` : `${sites.length} site(s):`}\n${lines.join("\n")}`;
57
449
  }
58
450
  /**
@@ -61,10 +453,9 @@ function formatSiteList(sites, serverId) {
61
453
  function formatSite(site) {
62
454
  return [
63
455
  `Site: ${site.name} (ID: ${site.id})`,
64
- `Type: ${site.project_type}`,
65
- `Directory: ${site.directory}`,
66
- `Repository: ${site.repository ?? "none"}`,
67
- `Branch: ${site.repository_branch ?? "none"}`,
456
+ `Type: ${site.app_type}`,
457
+ `Directory: ${site.root_directory}`,
458
+ `Repository: ${site.repository?.url ?? "none"}`,
68
459
  `Status: ${site.status}`,
69
460
  `Deploy status: ${site.deployment_status ?? "none"}`,
70
461
  `Quick deploy: ${site.quick_deploy ? "enabled" : "disabled"}`,
@@ -101,7 +492,6 @@ function formatDatabaseUser(user) {
101
492
  return [
102
493
  `Database User: ${user.name} (ID: ${user.id})`,
103
494
  `Status: ${user.status}`,
104
- `Databases: ${user.databases.length > 0 ? user.databases.join(", ") : "none"}`,
105
495
  `Created: ${user.created_at}`
106
496
  ].join("\n");
107
497
  }
@@ -110,7 +500,7 @@ function formatDatabaseUser(user) {
110
500
  */
111
501
  function formatDeploymentList(deployments) {
112
502
  if (deployments.length === 0) return "No deployments found.";
113
- const lines = deployments.map((d) => `• #${d.id} — ${d.status} — ${d.commit_hash?.slice(0, 7) ?? "no commit"} — ${d.started_at}`);
503
+ const lines = deployments.map((d) => `• #${d.id} — ${d.status} — ${d.commit?.hash?.slice(0, 7) ?? "no commit"} — ${d.started_at}`);
114
504
  return `${deployments.length} deployment(s):\n${lines.join("\n")}`;
115
505
  }
116
506
  /**
@@ -148,16 +538,11 @@ function formatDeploymentScriptUpdated(siteId, serverId) {
148
538
  /**
149
539
  * Format a list of certificates.
150
540
  */
151
- function formatCertificateList(certificates) {
152
- if (certificates.length === 0) return "No certificates found.";
153
- const lines = certificates.map((c) => `• ${c.domain} (ID: ${c.id}) — ${c.type} — ${c.active ? "active" : "inactive"} — ${c.status}`);
154
- return `${certificates.length} certificate(s):\n${lines.join("\n")}`;
155
- }
156
541
  /**
157
- * Format a single certificate.
542
+ * Format a single certificate (v2: one certificate per domain).
158
543
  */
159
544
  function formatCertificate(cert) {
160
- return `Certificate: ${cert.domain} (ID: ${cert.id})\nType: ${cert.type}\nStatus: ${cert.status}\nActive: ${cert.active}`;
545
+ return `Certificate (ID: ${cert.id})\nType: ${cert.type}\nStatus: ${cert.status}\nRequest: ${cert.request_status}`;
161
546
  }
162
547
  /**
163
548
  * Format a list of daemons.
@@ -289,7 +674,7 @@ function formatNginxTemplate(template) {
289
674
  */
290
675
  function formatBackupConfigList(backups) {
291
676
  if (backups.length === 0) return "No backup configurations found.";
292
- const lines = backups.map((b) => `• ${b.provider_name} (ID: ${b.id}) — ${b.frequency} — ${b.status} — last: ${b.last_backup_time ?? "never"}`);
677
+ const lines = backups.map((b) => `• ${b.name} (ID: ${b.id}) — ${b.schedule} — ${b.status} — next: ${b.next_run_time ?? ""}`);
293
678
  return `${backups.length} backup config(s):\n${lines.join("\n")}`;
294
679
  }
295
680
  /**
@@ -297,12 +682,11 @@ function formatBackupConfigList(backups) {
297
682
  */
298
683
  function formatBackupConfig(backup) {
299
684
  return [
300
- `Backup Config: ${backup.provider_name} (ID: ${backup.id})`,
301
- `Frequency: ${backup.frequency}`,
685
+ `Backup Config: ${backup.name} (ID: ${backup.id})`,
686
+ `Schedule: ${backup.displayable_schedule}`,
302
687
  `Status: ${backup.status}`,
303
688
  `Retention: ${backup.retention} backups`,
304
- `Databases: ${backup.databases.map((d) => d.name).join(", ") || "none"}`,
305
- `Last backup: ${backup.last_backup_time ?? "never"}`
689
+ `Next run: ${backup.next_run_time ?? ""}`
306
690
  ].join("\n");
307
691
  }
308
692
  /**
@@ -324,7 +708,7 @@ function formatRecipe(recipe) {
324
708
  */
325
709
  function formatCommandList(commands) {
326
710
  if (commands.length === 0) return "No commands found.";
327
- const lines = commands.map((c) => `• #${c.id} — ${c.status} — ${c.user_name} — ${c.command.slice(0, 60)}`);
711
+ const lines = commands.map((c) => `• #${c.id} — ${c.status} — user ${c.user_id} — ${c.command.slice(0, 60)}`);
328
712
  return `${commands.length} command(s):\n${lines.join("\n")}`;
329
713
  }
330
714
  /**
@@ -335,7 +719,8 @@ function formatCommand(command) {
335
719
  `Command #${command.id}`,
336
720
  `Command: ${command.command}`,
337
721
  `Status: ${command.status}`,
338
- `User: ${command.user_name}`,
722
+ `User ID: ${command.user_id}`,
723
+ `Duration: ${command.duration}`,
339
724
  `Created: ${command.created_at}`
340
725
  ].join("\n");
341
726
  }
@@ -352,9 +737,7 @@ function formatUser(user) {
352
737
  return [
353
738
  `User: ${user.name} (ID: ${user.id})`,
354
739
  `Email: ${user.email}`,
355
- `GitHub: ${user.connected_to_github ? "connected" : "not connected"}`,
356
- `GitLab: ${user.connected_to_gitlab ? "connected" : "not connected"}`,
357
- `2FA: ${user.two_factor_enabled ? "enabled" : "disabled"}`
740
+ `Created: ${user.created_at}`
358
741
  ].join("\n");
359
742
  }
360
743
  /**
@@ -436,49 +819,39 @@ function errorResult(message) {
436
819
  };
437
820
  }
438
821
  /**
439
- * Create a resource handler from configuration.
440
- *
441
- * Returns a function that routes actions to the correct executor,
442
- * validates required fields, and formats results.
822
+ * Factory for creating Forge resource handlers.
443
823
  *
444
- * @example
445
- * ```typescript
446
- * export const handleDatabases = createResourceHandler({
447
- * resource: 'databases',
448
- * actions: ['list', 'get', 'create', 'delete'],
449
- * requiredFields: {
450
- * list: ['server_id'],
451
- * get: ['server_id', 'id'],
452
- * create: ['server_id', 'name'],
453
- * delete: ['server_id', 'id'],
454
- * },
455
- * executors: {
456
- * list: listDatabases,
457
- * get: getDatabase,
458
- * create: createDatabase,
459
- * delete: deleteDatabase,
460
- * },
461
- * formatResult: (action, data) => {
462
- * if (action === 'list') return formatDatabaseList(data);
463
- * if (action === 'get') return formatDatabase(data);
464
- * return 'Done.';
465
- * },
466
- * });
467
- * ```
824
+ * Encapsulates the repetitive list/get/create/update/delete pattern
825
+ * shared by all MCP resource handlers.
826
+ */
827
+ /**
828
+ * Format Valibot validation issues into field-level error messages.
829
+ */
830
+ function formatValidationIssues(issues) {
831
+ return issues.map((issue) => {
832
+ return `${issue.path?.map((p) => p.key).join(".") || "(root)"}: ${issue.message}`;
833
+ }).join(", ");
834
+ }
835
+ /**
836
+ * Create a resource handler from configuration.
468
837
  */
469
838
  function createResourceHandler(config) {
470
- const { resource, actions, requiredFields = {}, executors, hints, mapOptions, formatResult } = config;
839
+ const { resource, actions, inputSchemas = {}, executors, hints, mapOptions, formatResult } = config;
471
840
  return async (action, args, ctx) => {
472
841
  if (!actions.includes(action)) return errorResult(`Unknown action "${action}" for ${resource}. Valid actions: ${actions.join(", ")}.`);
473
- const required = requiredFields[action] ?? [];
474
- for (const field of required) if (!args[field]) return errorResult(`Missing required field: ${field}`);
842
+ const schema = inputSchemas[action];
843
+ if (schema) {
844
+ const parsed = /* @__PURE__ */ safeParse(schema, args);
845
+ if (!parsed.success) return errorResult(`Invalid input: ${formatValidationIssues(parsed.issues)}`);
846
+ }
475
847
  for (const field of [
476
848
  "id",
477
849
  "server_id",
478
850
  "site_id"
479
851
  ]) {
480
852
  const value = args[field];
481
- if (value !== void 0 && !sanitizeId(String(value))) return errorResult(`Invalid ${field}: "${value}". IDs must be alphanumeric.`);
853
+ const idStr = typeof value === "string" || typeof value === "number" ? String(value) : "";
854
+ if (value !== void 0 && !sanitizeId(idStr)) return errorResult(`Invalid ${field}: "${idStr}". IDs must be alphanumeric.`);
482
855
  }
483
856
  const executor = executors[action];
484
857
  if (!executor) return errorResult(`Action "${action}" is not yet implemented for ${resource}.`);
@@ -495,8 +868,8 @@ function createResourceHandler(config) {
495
868
  /* v8 ignore start */
496
869
  const id = args.id ?? args.server_id ?? "";
497
870
  return jsonResult({
498
- ...result.data,
499
- _hints: hints(result.data, String(id))
871
+ ...typeof result.data === "object" && result.data !== null ? result.data : {},
872
+ _hints: hints(result.data, String(id), args)
500
873
  });
501
874
  }
502
875
  return jsonResult(result.data);
@@ -508,31 +881,55 @@ const handleBackups = createResourceHandler({
508
881
  "list",
509
882
  "get",
510
883
  "create",
884
+ "update",
511
885
  "delete"
512
886
  ],
513
- requiredFields: {
514
- list: ["server_id"],
515
- get: ["server_id", "id"],
516
- create: [
517
- "server_id",
518
- "provider",
519
- "credentials",
520
- "frequency",
521
- "databases"
522
- ],
523
- delete: ["server_id", "id"]
887
+ inputSchemas: {
888
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
889
+ get: /* @__PURE__ */ object({
890
+ server_id: /* @__PURE__ */ string(),
891
+ id: /* @__PURE__ */ string()
892
+ }),
893
+ create: /* @__PURE__ */ object({
894
+ server_id: /* @__PURE__ */ string(),
895
+ provider: /* @__PURE__ */ string(),
896
+ frequency: /* @__PURE__ */ string(),
897
+ credentials: /* @__PURE__ */ unknown(),
898
+ databases: /* @__PURE__ */ unknown()
899
+ }),
900
+ update: /* @__PURE__ */ object({
901
+ server_id: /* @__PURE__ */ string(),
902
+ id: /* @__PURE__ */ string(),
903
+ storage_provider_id: /* @__PURE__ */ number(),
904
+ frequency: /* @__PURE__ */ string(),
905
+ retention: /* @__PURE__ */ number(),
906
+ database_ids: /* @__PURE__ */ array(/* @__PURE__ */ number()),
907
+ name: /* @__PURE__ */ optional(/* @__PURE__ */ nullable(/* @__PURE__ */ string())),
908
+ bucket: /* @__PURE__ */ optional(/* @__PURE__ */ nullable(/* @__PURE__ */ string())),
909
+ directory: /* @__PURE__ */ optional(/* @__PURE__ */ nullable(/* @__PURE__ */ string())),
910
+ day: /* @__PURE__ */ optional(/* @__PURE__ */ string()),
911
+ time: /* @__PURE__ */ optional(/* @__PURE__ */ string()),
912
+ cron: /* @__PURE__ */ optional(/* @__PURE__ */ string()),
913
+ notification_email: /* @__PURE__ */ optional(/* @__PURE__ */ nullable(/* @__PURE__ */ string()))
914
+ }),
915
+ delete: /* @__PURE__ */ object({
916
+ server_id: /* @__PURE__ */ string(),
917
+ id: /* @__PURE__ */ string()
918
+ })
524
919
  },
525
920
  executors: {
526
921
  list: listBackupConfigs,
527
922
  get: getBackupConfig,
528
923
  create: createBackupConfig,
924
+ update: updateBackupConfig,
529
925
  delete: deleteBackupConfig
530
926
  },
531
927
  formatResult: (action, data, args) => {
532
928
  switch (action) {
533
929
  case "list": return formatBackupConfigList(data);
534
930
  case "get": return formatBackupConfig(data);
535
- case "create": return formatBackupConfig(data);
931
+ case "create": return "Done.";
932
+ case "update": return "Done.";
536
933
  case "delete": return `Backup config ${args.id} deleted.`;
537
934
  default: return "Done.";
538
935
  }
@@ -740,13 +1137,9 @@ const TOOLS = [{
740
1137
  },
741
1138
  domain: {
742
1139
  type: "string",
743
- description: "Site domain name (e.g. example.com)"
744
- },
745
- project_type: {
746
- type: "string",
747
- description: "Site project type (e.g. php, html, symfony, laravel)"
1140
+ description: "Site domain name (alias for name, e.g. example.com)"
748
1141
  },
749
- directory: {
1142
+ web_directory: {
750
1143
  type: "string",
751
1144
  description: "Web directory relative to site root (e.g. /public)"
752
1145
  },
@@ -833,7 +1226,7 @@ function getTools(options) {
833
1226
  const STDIO_ONLY_TOOLS = [{
834
1227
  name: "forge_configure",
835
1228
  title: "Configure Forge",
836
- description: "Configure Laravel Forge API token. The token is stored locally in the XDG config directory.",
1229
+ description: "Configure Laravel Forge API credentials. The token is stored locally in the XDG config directory.",
837
1230
  annotations: {
838
1231
  title: "Configure Forge",
839
1232
  readOnlyHint: false,
@@ -843,11 +1236,16 @@ const STDIO_ONLY_TOOLS = [{
843
1236
  },
844
1237
  inputSchema: {
845
1238
  type: "object",
846
- properties: { apiToken: {
847
- type: "string",
848
- description: "Your Laravel Forge API token"
849
- } },
850
- required: ["apiToken"]
1239
+ properties: {
1240
+ apiToken: {
1241
+ type: "string",
1242
+ description: "Your Laravel Forge API token"
1243
+ },
1244
+ organizationSlug: {
1245
+ type: "string",
1246
+ description: "Default organization slug (e.g. 'studio-meta'). Required for all API calls."
1247
+ }
1248
+ }
851
1249
  },
852
1250
  outputSchema: {
853
1251
  type: "object",
@@ -860,6 +1258,10 @@ const STDIO_ONLY_TOOLS = [{
860
1258
  apiToken: {
861
1259
  type: "string",
862
1260
  description: "Masked API token (last 4 chars)"
1261
+ },
1262
+ organizationSlug: {
1263
+ type: "string",
1264
+ description: "Organization slug"
863
1265
  }
864
1266
  },
865
1267
  required: ["success"]
@@ -896,6 +1298,33 @@ const STDIO_ONLY_TOOLS = [{
896
1298
  }];
897
1299
  var MAX_OPERATIONS = 10;
898
1300
  /**
1301
+ * Validate and narrow an array element to a BatchOperation.
1302
+ * Returns an error message string on failure, or the typed operation on success.
1303
+ */
1304
+ function validateOperation(op, index) {
1305
+ if (!op || typeof op !== "object") return {
1306
+ ok: false,
1307
+ error: `Operation at index ${index} must be an object.`
1308
+ };
1309
+ const record = op;
1310
+ if (!record["resource"] || typeof record["resource"] !== "string") return {
1311
+ ok: false,
1312
+ error: `Operation at index ${index} is missing required field "resource".`
1313
+ };
1314
+ if (!record["action"] || typeof record["action"] !== "string") return {
1315
+ ok: false,
1316
+ error: `Operation at index ${index} is missing required field "action".`
1317
+ };
1318
+ if (!isReadAction(record["action"])) return {
1319
+ ok: false,
1320
+ error: `Operation at index ${index} has invalid action "${record["action"]}". Only read actions are allowed in batch: list, get, help, schema.`
1321
+ };
1322
+ return {
1323
+ ok: true,
1324
+ value: record
1325
+ };
1326
+ }
1327
+ /**
899
1328
  * Handle batch action — executes multiple read operations in parallel.
900
1329
  */
901
1330
  async function handleBatch(action, args, ctx, routeToHandler) {
@@ -904,14 +1333,13 @@ async function handleBatch(action, args, ctx, routeToHandler) {
904
1333
  if (operations === void 0 || operations === null) return errorResult("Missing required field: \"operations\". Provide an array of operations.");
905
1334
  if (!Array.isArray(operations)) return errorResult("\"operations\" must be an array of operation objects.");
906
1335
  if (operations.length > MAX_OPERATIONS) return errorResult(`Too many operations: ${operations.length}. Maximum is ${MAX_OPERATIONS} per batch.`);
1336
+ const validated = [];
907
1337
  for (let i = 0; i < operations.length; i++) {
908
- const op = operations[i];
909
- if (!op || typeof op !== "object") return errorResult(`Operation at index ${i} must be an object.`);
910
- if (!op["resource"] || typeof op["resource"] !== "string") return errorResult(`Operation at index ${i} is missing required field "resource".`);
911
- if (!op["action"] || typeof op["action"] !== "string") return errorResult(`Operation at index ${i} is missing required field "action".`);
912
- if (!isReadAction(op["action"])) return errorResult(`Operation at index ${i} has invalid action "${op["action"]}". Only read actions are allowed in batch: list, get, help, schema.`);
1338
+ const result = validateOperation(operations[i], i);
1339
+ if (!result.ok) return errorResult(result.error);
1340
+ validated.push(result.value);
913
1341
  }
914
- const settled = await Promise.allSettled(operations.map((op) => {
1342
+ const settled = await Promise.allSettled(validated.map((op) => {
915
1343
  const { resource, action: opAction, ...rest } = op;
916
1344
  return routeToHandler(resource, opAction, {
917
1345
  resource,
@@ -922,9 +1350,7 @@ async function handleBatch(action, args, ctx, routeToHandler) {
922
1350
  let succeeded = 0;
923
1351
  let failed = 0;
924
1352
  const results = settled.map((outcome, index) => {
925
- const op = operations[index];
926
- const resource = op["resource"];
927
- const opAction = op["action"];
1353
+ const { resource, action: opAction } = validated[index];
928
1354
  if (outcome.status === "fulfilled") {
929
1355
  const toolResult = outcome.value;
930
1356
  if (toolResult.isError) {
@@ -955,7 +1381,7 @@ async function handleBatch(action, args, ctx, routeToHandler) {
955
1381
  });
956
1382
  return jsonResult({
957
1383
  _batch: {
958
- total: operations.length,
1384
+ total: validated.length,
959
1385
  succeeded,
960
1386
  failed
961
1387
  },
@@ -1052,12 +1478,13 @@ function getSiteHints(serverId, siteId) {
1052
1478
  },
1053
1479
  {
1054
1480
  resource: "certificates",
1055
- description: "List SSL certificates",
1481
+ description: "Get certificate for a domain (requires domain_id)",
1056
1482
  example: {
1057
1483
  resource: "certificates",
1058
- action: "list",
1484
+ action: "get",
1059
1485
  server_id: serverId,
1060
- site_id: siteId
1486
+ site_id: siteId,
1487
+ domain_id: "<domain_id>"
1061
1488
  }
1062
1489
  },
1063
1490
  {
@@ -1176,16 +1603,16 @@ function getDaemonHints(serverId, daemonId) {
1176
1603
  /**
1177
1604
  * Hints after getting a certificate.
1178
1605
  */
1179
- function getCertificateHints(serverId, siteId, certificateId) {
1606
+ function getCertificateHints(serverId, siteId, domainId) {
1180
1607
  return {
1181
1608
  related_resources: [{
1182
- resource: "certificates",
1183
- description: "List all certificates for this site",
1609
+ resource: "sites",
1610
+ description: "Get the parent site",
1184
1611
  example: {
1185
- resource: "certificates",
1186
- action: "list",
1612
+ resource: "sites",
1613
+ action: "get",
1187
1614
  server_id: serverId,
1188
- site_id: siteId
1615
+ id: siteId
1189
1616
  }
1190
1617
  }],
1191
1618
  common_actions: [{
@@ -1195,7 +1622,7 @@ function getCertificateHints(serverId, siteId, certificateId) {
1195
1622
  action: "activate",
1196
1623
  server_id: serverId,
1197
1624
  site_id: siteId,
1198
- id: certificateId
1625
+ domain_id: domainId
1199
1626
  }
1200
1627
  }, {
1201
1628
  action: "Delete this certificate",
@@ -1204,7 +1631,7 @@ function getCertificateHints(serverId, siteId, certificateId) {
1204
1631
  action: "delete",
1205
1632
  server_id: serverId,
1206
1633
  site_id: siteId,
1207
- id: certificateId
1634
+ domain_id: domainId
1208
1635
  }
1209
1636
  }]
1210
1637
  };
@@ -1327,53 +1754,47 @@ function getNginxTemplateHints(serverId, templateId) {
1327
1754
  const handleCertificates = createResourceHandler({
1328
1755
  resource: "certificates",
1329
1756
  actions: [
1330
- "list",
1331
1757
  "get",
1332
1758
  "create",
1333
1759
  "delete",
1334
1760
  "activate"
1335
1761
  ],
1336
- requiredFields: {
1337
- list: ["server_id", "site_id"],
1338
- get: [
1339
- "server_id",
1340
- "site_id",
1341
- "id"
1342
- ],
1343
- create: [
1344
- "server_id",
1345
- "site_id",
1346
- "domain"
1347
- ],
1348
- delete: [
1349
- "server_id",
1350
- "site_id",
1351
- "id"
1352
- ],
1353
- activate: [
1354
- "server_id",
1355
- "site_id",
1356
- "id"
1357
- ]
1762
+ inputSchemas: {
1763
+ get: /* @__PURE__ */ object({
1764
+ server_id: /* @__PURE__ */ string(),
1765
+ site_id: /* @__PURE__ */ string(),
1766
+ domain_id: /* @__PURE__ */ string()
1767
+ }),
1768
+ create: /* @__PURE__ */ object({
1769
+ server_id: /* @__PURE__ */ string(),
1770
+ site_id: /* @__PURE__ */ string(),
1771
+ domain_id: /* @__PURE__ */ string(),
1772
+ type: /* @__PURE__ */ string()
1773
+ }),
1774
+ delete: /* @__PURE__ */ object({
1775
+ server_id: /* @__PURE__ */ string(),
1776
+ site_id: /* @__PURE__ */ string(),
1777
+ domain_id: /* @__PURE__ */ string()
1778
+ }),
1779
+ activate: /* @__PURE__ */ object({
1780
+ server_id: /* @__PURE__ */ string(),
1781
+ site_id: /* @__PURE__ */ string(),
1782
+ domain_id: /* @__PURE__ */ string()
1783
+ })
1358
1784
  },
1359
1785
  executors: {
1360
- list: listCertificates,
1361
1786
  get: getCertificate,
1362
1787
  create: createCertificate,
1363
1788
  delete: deleteCertificate,
1364
1789
  activate: activateCertificate
1365
1790
  },
1366
- hints: (data, id) => {
1367
- const cert = data;
1368
- return getCertificateHints(String(cert.server_id), String(cert.site_id), id);
1369
- },
1791
+ hints: (_data, _id, args) => getCertificateHints(String(args.server_id), String(args.site_id), String(args.domain_id)),
1370
1792
  formatResult: (action, data, args) => {
1371
1793
  switch (action) {
1372
- case "list": return formatCertificateList(data);
1373
1794
  case "get": return formatCertificate(data);
1374
1795
  case "create": return formatCertificate(data);
1375
- case "delete": return `Certificate ${args.id} deleted.`;
1376
- case "activate": return `Certificate ${args.id} activated.`;
1796
+ case "delete": return `Certificate for domain ${String(args.domain_id)} deleted.`;
1797
+ case "activate": return `Certificate for domain ${String(args.domain_id)} activated.`;
1377
1798
  default: return "Done.";
1378
1799
  }
1379
1800
  }
@@ -1385,18 +1806,21 @@ const handleCommands = createResourceHandler({
1385
1806
  "get",
1386
1807
  "create"
1387
1808
  ],
1388
- requiredFields: {
1389
- list: ["server_id", "site_id"],
1390
- get: [
1391
- "server_id",
1392
- "site_id",
1393
- "id"
1394
- ],
1395
- create: [
1396
- "server_id",
1397
- "site_id",
1398
- "command"
1399
- ]
1809
+ inputSchemas: {
1810
+ list: /* @__PURE__ */ object({
1811
+ server_id: /* @__PURE__ */ string(),
1812
+ site_id: /* @__PURE__ */ string()
1813
+ }),
1814
+ get: /* @__PURE__ */ object({
1815
+ server_id: /* @__PURE__ */ string(),
1816
+ site_id: /* @__PURE__ */ string(),
1817
+ id: /* @__PURE__ */ string()
1818
+ }),
1819
+ create: /* @__PURE__ */ object({
1820
+ server_id: /* @__PURE__ */ string(),
1821
+ site_id: /* @__PURE__ */ string(),
1822
+ command: /* @__PURE__ */ string()
1823
+ })
1400
1824
  },
1401
1825
  executors: {
1402
1826
  list: listCommands,
@@ -1407,7 +1831,7 @@ const handleCommands = createResourceHandler({
1407
1831
  switch (action) {
1408
1832
  case "list": return formatCommandList(data);
1409
1833
  case "get": return formatCommand(data);
1410
- case "create": return formatCommand(data);
1834
+ case "create": return "Done.";
1411
1835
  default: return "Done.";
1412
1836
  }
1413
1837
  }
@@ -1418,32 +1842,50 @@ const handleDaemons = createResourceHandler({
1418
1842
  "list",
1419
1843
  "get",
1420
1844
  "create",
1845
+ "update",
1421
1846
  "delete",
1422
1847
  "restart"
1423
1848
  ],
1424
- requiredFields: {
1425
- list: ["server_id"],
1426
- get: ["server_id", "id"],
1427
- create: ["server_id", "command"],
1428
- delete: ["server_id", "id"],
1429
- restart: ["server_id", "id"]
1849
+ inputSchemas: {
1850
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
1851
+ get: /* @__PURE__ */ object({
1852
+ server_id: /* @__PURE__ */ string(),
1853
+ id: /* @__PURE__ */ string()
1854
+ }),
1855
+ create: /* @__PURE__ */ object({
1856
+ server_id: /* @__PURE__ */ string(),
1857
+ command: /* @__PURE__ */ string()
1858
+ }),
1859
+ update: /* @__PURE__ */ object({
1860
+ server_id: /* @__PURE__ */ string(),
1861
+ id: /* @__PURE__ */ string(),
1862
+ name: /* @__PURE__ */ string(),
1863
+ config: /* @__PURE__ */ optional(/* @__PURE__ */ string())
1864
+ }),
1865
+ delete: /* @__PURE__ */ object({
1866
+ server_id: /* @__PURE__ */ string(),
1867
+ id: /* @__PURE__ */ string()
1868
+ }),
1869
+ restart: /* @__PURE__ */ object({
1870
+ server_id: /* @__PURE__ */ string(),
1871
+ id: /* @__PURE__ */ string()
1872
+ })
1430
1873
  },
1431
1874
  executors: {
1432
1875
  list: listDaemons,
1433
1876
  get: getDaemon,
1434
1877
  create: createDaemon,
1878
+ update: updateDaemon,
1435
1879
  delete: deleteDaemon,
1436
1880
  restart: restartDaemon
1437
1881
  },
1438
- hints: (data, id) => {
1439
- const daemon = data;
1440
- return getDaemonHints(String(daemon.server_id), id);
1441
- },
1882
+ hints: (_data, id, args) => getDaemonHints(String(args.server_id), id),
1442
1883
  formatResult: (action, data, args) => {
1443
1884
  switch (action) {
1444
1885
  case "list": return formatDaemonList(data);
1445
1886
  case "get": return formatDaemon(data);
1446
1887
  case "create": return formatDaemon(data);
1888
+ case "update": return formatDaemon(data);
1447
1889
  case "delete": return `Daemon ${args.id} deleted.`;
1448
1890
  case "restart": return `Daemon ${args.id} restarted.`;
1449
1891
  default: return "Done.";
@@ -1458,11 +1900,20 @@ const handleDatabases = createResourceHandler({
1458
1900
  "create",
1459
1901
  "delete"
1460
1902
  ],
1461
- requiredFields: {
1462
- list: ["server_id"],
1463
- get: ["server_id", "id"],
1464
- create: ["server_id", "name"],
1465
- delete: ["server_id", "id"]
1903
+ inputSchemas: {
1904
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
1905
+ get: /* @__PURE__ */ object({
1906
+ server_id: /* @__PURE__ */ string(),
1907
+ id: /* @__PURE__ */ string()
1908
+ }),
1909
+ create: /* @__PURE__ */ object({
1910
+ server_id: /* @__PURE__ */ string(),
1911
+ name: /* @__PURE__ */ string()
1912
+ }),
1913
+ delete: /* @__PURE__ */ object({
1914
+ server_id: /* @__PURE__ */ string(),
1915
+ id: /* @__PURE__ */ string()
1916
+ })
1466
1917
  },
1467
1918
  executors: {
1468
1919
  list: listDatabases,
@@ -1470,10 +1921,7 @@ const handleDatabases = createResourceHandler({
1470
1921
  create: createDatabase,
1471
1922
  delete: deleteDatabase
1472
1923
  },
1473
- hints: (data, id) => {
1474
- const db = data;
1475
- return getDatabaseHints(String(db.server_id), id);
1476
- },
1924
+ hints: (_data, id, args) => getDatabaseHints(String(args.server_id), id),
1477
1925
  formatResult: (action, data, args) => {
1478
1926
  switch (action) {
1479
1927
  case "list": return formatDatabaseList(data);
@@ -1490,33 +1938,45 @@ const handleDatabaseUsers = createResourceHandler({
1490
1938
  "list",
1491
1939
  "get",
1492
1940
  "create",
1941
+ "update",
1493
1942
  "delete"
1494
1943
  ],
1495
- requiredFields: {
1496
- list: ["server_id"],
1497
- get: ["server_id", "id"],
1498
- create: [
1499
- "server_id",
1500
- "name",
1501
- "password"
1502
- ],
1503
- delete: ["server_id", "id"]
1944
+ inputSchemas: {
1945
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
1946
+ get: /* @__PURE__ */ object({
1947
+ server_id: /* @__PURE__ */ string(),
1948
+ id: /* @__PURE__ */ string()
1949
+ }),
1950
+ create: /* @__PURE__ */ object({
1951
+ server_id: /* @__PURE__ */ string(),
1952
+ name: /* @__PURE__ */ string(),
1953
+ password: /* @__PURE__ */ string()
1954
+ }),
1955
+ update: /* @__PURE__ */ object({
1956
+ server_id: /* @__PURE__ */ string(),
1957
+ id: /* @__PURE__ */ string(),
1958
+ password: /* @__PURE__ */ optional(/* @__PURE__ */ nullable(/* @__PURE__ */ string())),
1959
+ database_ids: /* @__PURE__ */ optional(/* @__PURE__ */ array(/* @__PURE__ */ number()))
1960
+ }),
1961
+ delete: /* @__PURE__ */ object({
1962
+ server_id: /* @__PURE__ */ string(),
1963
+ id: /* @__PURE__ */ string()
1964
+ })
1504
1965
  },
1505
1966
  executors: {
1506
1967
  list: listDatabaseUsers,
1507
1968
  get: getDatabaseUser,
1508
1969
  create: createDatabaseUser,
1970
+ update: updateDatabaseUser,
1509
1971
  delete: deleteDatabaseUser
1510
1972
  },
1511
- hints: (data, id) => {
1512
- const user = data;
1513
- return getDatabaseUserHints(String(user.server_id), id);
1514
- },
1973
+ hints: (_data, id, args) => getDatabaseUserHints(String(args.server_id), id),
1515
1974
  formatResult: (action, data, args) => {
1516
1975
  switch (action) {
1517
1976
  case "list": return formatDatabaseUserList(data);
1518
1977
  case "get": return formatDatabaseUser(data);
1519
1978
  case "create": return formatDatabaseUser(data);
1979
+ case "update": return formatDatabaseUser(data);
1520
1980
  case "delete": return `Database user ${args.id} deleted.`;
1521
1981
  default: return "Done.";
1522
1982
  }
@@ -1550,7 +2010,7 @@ async function handleDeployments(action, args, ctx) {
1550
2010
  }
1551
2011
  case "get":
1552
2012
  if (args.id) {
1553
- const result = await getDeploymentOutput({
2013
+ const result = await getDeploymentLog({
1554
2014
  ...opts,
1555
2015
  deployment_id: args.id
1556
2016
  }, ctx.executorContext);
@@ -1570,13 +2030,16 @@ async function handleDeployments(action, args, ctx) {
1570
2030
  const handleEnv = createResourceHandler({
1571
2031
  resource: "env",
1572
2032
  actions: ["get", "update"],
1573
- requiredFields: {
1574
- get: ["server_id", "site_id"],
1575
- update: [
1576
- "server_id",
1577
- "site_id",
1578
- "content"
1579
- ]
2033
+ inputSchemas: {
2034
+ get: /* @__PURE__ */ object({
2035
+ server_id: /* @__PURE__ */ string(),
2036
+ site_id: /* @__PURE__ */ string()
2037
+ }),
2038
+ update: /* @__PURE__ */ object({
2039
+ server_id: /* @__PURE__ */ string(),
2040
+ site_id: /* @__PURE__ */ string(),
2041
+ content: /* @__PURE__ */ string()
2042
+ })
1580
2043
  },
1581
2044
  executors: {
1582
2045
  get: getEnv,
@@ -1603,15 +2066,21 @@ const handleFirewallRules = createResourceHandler({
1603
2066
  "create",
1604
2067
  "delete"
1605
2068
  ],
1606
- requiredFields: {
1607
- list: ["server_id"],
1608
- get: ["server_id", "id"],
1609
- create: [
1610
- "server_id",
1611
- "name",
1612
- "port"
1613
- ],
1614
- delete: ["server_id", "id"]
2069
+ inputSchemas: {
2070
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
2071
+ get: /* @__PURE__ */ object({
2072
+ server_id: /* @__PURE__ */ string(),
2073
+ id: /* @__PURE__ */ string()
2074
+ }),
2075
+ create: /* @__PURE__ */ object({
2076
+ server_id: /* @__PURE__ */ string(),
2077
+ name: /* @__PURE__ */ string(),
2078
+ port: /* @__PURE__ */ string()
2079
+ }),
2080
+ delete: /* @__PURE__ */ object({
2081
+ server_id: /* @__PURE__ */ string(),
2082
+ id: /* @__PURE__ */ string()
2083
+ })
1615
2084
  },
1616
2085
  executors: {
1617
2086
  list: listFirewallRules,
@@ -1619,15 +2088,12 @@ const handleFirewallRules = createResourceHandler({
1619
2088
  create: createFirewallRule,
1620
2089
  delete: deleteFirewallRule
1621
2090
  },
1622
- hints: (data, id) => {
1623
- const rule = data;
1624
- return getFirewallRuleHints(String(rule.server_id), id);
1625
- },
2091
+ hints: (_data, id, args) => getFirewallRuleHints(String(args.server_id), id),
1626
2092
  formatResult: (action, data, args) => {
1627
2093
  switch (action) {
1628
2094
  case "list": return formatFirewallRuleList(data);
1629
2095
  case "get": return formatFirewallRule(data);
1630
- case "create": return formatFirewallRule(data);
2096
+ case "create": return "Done.";
1631
2097
  case "delete": return `Firewall rule ${args.id} deleted.`;
1632
2098
  default: return "Done.";
1633
2099
  }
@@ -1703,17 +2169,17 @@ var RESOURCE_HELP = {
1703
2169
  actions: {
1704
2170
  list: "List all sites on a server",
1705
2171
  get: "Get a single site by ID",
1706
- create: "Create a new site (requires domain, project_type)",
2172
+ create: "Create a new site (requires type; optionally name, web_directory)",
1707
2173
  delete: "Delete a site by ID",
1708
- context: "Get full site context: site details + recent deployments (last 5) + certificates + redirect rules + security rules in one call",
2174
+ context: "Get full site context: site details + recent deployments (last 5) + redirect rules + security rules in one call",
1709
2175
  resolve: "Find sites by domain name (partial, case-insensitive match, requires server_id)"
1710
2176
  },
1711
2177
  fields: {
1712
2178
  id: "Site ID",
1713
2179
  server_id: "Parent server ID",
1714
2180
  name: "Domain name (e.g. example.com)",
1715
- project_type: "Project type (php, html, symfony, symfony_dev, symfony_four, laravel)",
1716
- directory: "Web root directory (e.g. /public)",
2181
+ type: "Site type (php, html, symfony, symfony_dev, symfony_four, laravel)",
2182
+ web_directory: "Web root directory (e.g. /public)",
1717
2183
  repository: "Git repository URL (if connected)",
1718
2184
  deployment_status: "Last deployment status (null, deploying, deployed, failed)"
1719
2185
  },
@@ -1741,13 +2207,13 @@ var RESOURCE_HELP = {
1741
2207
  resource: "sites",
1742
2208
  action: "create",
1743
2209
  server_id: "123",
1744
- domain: "app.example.com",
1745
- project_type: "php",
1746
- directory: "/public"
2210
+ type: "php",
2211
+ name: "app.example.com",
2212
+ web_directory: "/public"
1747
2213
  }
1748
2214
  },
1749
2215
  {
1750
- description: "Get full site context (site + deployments + certificates + rules)",
2216
+ description: "Get full site context (site + deployments + rules)",
1751
2217
  params: {
1752
2218
  resource: "sites",
1753
2219
  action: "context",
@@ -1868,23 +2334,23 @@ var RESOURCE_HELP = {
1868
2334
  }]
1869
2335
  },
1870
2336
  certificates: {
1871
- description: "Manage SSL/TLS certificates for a site — Let's Encrypt, custom, or cloned",
1872
- scope: "site (requires server_id + site_id)",
2337
+ description: "Manage SSL/TLS certificates per domain — Let's Encrypt, CSR, existing, or cloned (v2: one cert per domain)",
2338
+ scope: "domain (requires server_id + site_id + domain_id)",
1873
2339
  actions: {
1874
- list: "List certificates for a site",
1875
- get: "Get certificate details",
1876
- create: "Create/request a new certificate",
1877
- delete: "Delete a certificate",
1878
- activate: "Activate a certificate (make it the active cert for the site)"
2340
+ get: "Get certificate for a domain",
2341
+ create: "Create/request a new certificate for a domain",
2342
+ delete: "Delete a domain certificate",
2343
+ activate: "Activate a domain certificate"
1879
2344
  },
1880
2345
  examples: [
1881
2346
  {
1882
- description: "List certificates",
2347
+ description: "Get certificate for a domain",
1883
2348
  params: {
1884
2349
  resource: "certificates",
1885
- action: "list",
2350
+ action: "get",
1886
2351
  server_id: "123",
1887
- site_id: "456"
2352
+ site_id: "456",
2353
+ domain_id: "789"
1888
2354
  }
1889
2355
  },
1890
2356
  {
@@ -1894,8 +2360,8 @@ var RESOURCE_HELP = {
1894
2360
  action: "create",
1895
2361
  server_id: "123",
1896
2362
  site_id: "456",
1897
- domain: "example.com",
1898
- type: "new"
2363
+ domain_id: "789",
2364
+ type: "letsencrypt"
1899
2365
  }
1900
2366
  },
1901
2367
  {
@@ -1905,7 +2371,7 @@ var RESOURCE_HELP = {
1905
2371
  action: "activate",
1906
2372
  server_id: "123",
1907
2373
  site_id: "456",
1908
- id: "789"
2374
+ domain_id: "789"
1909
2375
  }
1910
2376
  }
1911
2377
  ]
@@ -2228,7 +2694,7 @@ var RESOURCE_HELP = {
2228
2694
  fields: {
2229
2695
  provider: "Backup provider (s3, spaces, custom)",
2230
2696
  credentials: "Provider credentials (keys, bucket, region)",
2231
- frequency: "Backup frequency (daily, weekly, custom)",
2697
+ schedule: "Backup schedule (daily, weekly, custom cron)",
2232
2698
  databases: "Array of database IDs to back up",
2233
2699
  retention: "Number of backups to retain"
2234
2700
  },
@@ -2318,7 +2784,7 @@ var RESOURCE_HELP = {
2318
2784
  user: {
2319
2785
  description: "Get the currently authenticated Forge user profile",
2320
2786
  scope: "global (no parent ID needed)",
2321
- actions: { get: "Get the authenticated user's profile (name, email, connected services)" },
2787
+ actions: { get: "Get the authenticated user's profile (name, email)" },
2322
2788
  examples: [{
2323
2789
  description: "Get user profile",
2324
2790
  params: {
@@ -2431,17 +2897,23 @@ const handleMonitors = createResourceHandler({
2431
2897
  "create",
2432
2898
  "delete"
2433
2899
  ],
2434
- requiredFields: {
2435
- list: ["server_id"],
2436
- get: ["server_id", "id"],
2437
- create: [
2438
- "server_id",
2439
- "type",
2440
- "operator",
2441
- "threshold",
2442
- "minutes"
2443
- ],
2444
- delete: ["server_id", "id"]
2900
+ inputSchemas: {
2901
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
2902
+ get: /* @__PURE__ */ object({
2903
+ server_id: /* @__PURE__ */ string(),
2904
+ id: /* @__PURE__ */ string()
2905
+ }),
2906
+ create: /* @__PURE__ */ object({
2907
+ server_id: /* @__PURE__ */ string(),
2908
+ type: /* @__PURE__ */ string(),
2909
+ operator: /* @__PURE__ */ string(),
2910
+ threshold: /* @__PURE__ */ string(),
2911
+ minutes: /* @__PURE__ */ string()
2912
+ }),
2913
+ delete: /* @__PURE__ */ object({
2914
+ server_id: /* @__PURE__ */ string(),
2915
+ id: /* @__PURE__ */ string()
2916
+ })
2445
2917
  },
2446
2918
  executors: {
2447
2919
  list: listMonitors,
@@ -2462,13 +2934,16 @@ const handleMonitors = createResourceHandler({
2462
2934
  const handleNginxConfig = createResourceHandler({
2463
2935
  resource: "nginx",
2464
2936
  actions: ["get", "update"],
2465
- requiredFields: {
2466
- get: ["server_id", "site_id"],
2467
- update: [
2468
- "server_id",
2469
- "site_id",
2470
- "content"
2471
- ]
2937
+ inputSchemas: {
2938
+ get: /* @__PURE__ */ object({
2939
+ server_id: /* @__PURE__ */ string(),
2940
+ site_id: /* @__PURE__ */ string()
2941
+ }),
2942
+ update: /* @__PURE__ */ object({
2943
+ server_id: /* @__PURE__ */ string(),
2944
+ site_id: /* @__PURE__ */ string(),
2945
+ content: /* @__PURE__ */ string()
2946
+ })
2472
2947
  },
2473
2948
  executors: {
2474
2949
  get: getNginxConfig,
@@ -2496,16 +2971,25 @@ const handleNginxTemplates = createResourceHandler({
2496
2971
  "update",
2497
2972
  "delete"
2498
2973
  ],
2499
- requiredFields: {
2500
- list: ["server_id"],
2501
- get: ["server_id", "id"],
2502
- create: [
2503
- "server_id",
2504
- "name",
2505
- "content"
2506
- ],
2507
- update: ["server_id", "id"],
2508
- delete: ["server_id", "id"]
2974
+ inputSchemas: {
2975
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
2976
+ get: /* @__PURE__ */ object({
2977
+ server_id: /* @__PURE__ */ string(),
2978
+ id: /* @__PURE__ */ string()
2979
+ }),
2980
+ create: /* @__PURE__ */ object({
2981
+ server_id: /* @__PURE__ */ string(),
2982
+ name: /* @__PURE__ */ string(),
2983
+ content: /* @__PURE__ */ string()
2984
+ }),
2985
+ update: /* @__PURE__ */ object({
2986
+ server_id: /* @__PURE__ */ string(),
2987
+ id: /* @__PURE__ */ string()
2988
+ }),
2989
+ delete: /* @__PURE__ */ object({
2990
+ server_id: /* @__PURE__ */ string(),
2991
+ id: /* @__PURE__ */ string()
2992
+ })
2509
2993
  },
2510
2994
  executors: {
2511
2995
  list: listNginxTemplates,
@@ -2514,10 +2998,7 @@ const handleNginxTemplates = createResourceHandler({
2514
2998
  update: updateNginxTemplate,
2515
2999
  delete: deleteNginxTemplate
2516
3000
  },
2517
- hints: (data, id) => {
2518
- const template = data;
2519
- return getNginxTemplateHints(String(template.server_id), id);
2520
- },
3001
+ hints: (_data, id, args) => getNginxTemplateHints(String(args.server_id), id),
2521
3002
  formatResult: (action, data, args) => {
2522
3003
  switch (action) {
2523
3004
  case "list": return formatNginxTemplateList(data);
@@ -2535,20 +3016,34 @@ const handleRecipes = createResourceHandler({
2535
3016
  "list",
2536
3017
  "get",
2537
3018
  "create",
3019
+ "update",
2538
3020
  "delete",
2539
3021
  "run"
2540
3022
  ],
2541
- requiredFields: {
2542
- get: ["id"],
2543
- create: ["name", "script"],
2544
- delete: ["id"],
2545
- run: ["id", "servers"]
3023
+ inputSchemas: {
3024
+ get: /* @__PURE__ */ object({ id: /* @__PURE__ */ string() }),
3025
+ create: /* @__PURE__ */ object({
3026
+ name: /* @__PURE__ */ string(),
3027
+ script: /* @__PURE__ */ string()
3028
+ }),
3029
+ delete: /* @__PURE__ */ object({ id: /* @__PURE__ */ string() }),
3030
+ update: /* @__PURE__ */ object({
3031
+ id: /* @__PURE__ */ string(),
3032
+ name: /* @__PURE__ */ optional(/* @__PURE__ */ string()),
3033
+ user: /* @__PURE__ */ optional(/* @__PURE__ */ string()),
3034
+ script: /* @__PURE__ */ optional(/* @__PURE__ */ string())
3035
+ }),
3036
+ run: /* @__PURE__ */ object({
3037
+ id: /* @__PURE__ */ string(),
3038
+ servers: /* @__PURE__ */ unknown()
3039
+ })
2546
3040
  },
2547
3041
  executors: {
2548
3042
  list: listRecipes,
2549
3043
  get: getRecipe,
2550
3044
  create: createRecipe,
2551
3045
  delete: deleteRecipe,
3046
+ update: updateRecipe,
2552
3047
  run: runRecipe
2553
3048
  },
2554
3049
  hints: (_data, id) => getRecipeHints(id),
@@ -2557,6 +3052,7 @@ const handleRecipes = createResourceHandler({
2557
3052
  case "list": return formatRecipeList(data);
2558
3053
  case "get": return formatRecipe(data);
2559
3054
  case "create": return formatRecipe(data);
3055
+ case "update": return formatRecipe(data);
2560
3056
  case "delete": return `Recipe ${args.id} deleted.`;
2561
3057
  case "run": {
2562
3058
  const servers = args.servers;
@@ -2575,24 +3071,27 @@ const handleRedirectRules = createResourceHandler({
2575
3071
  "create",
2576
3072
  "delete"
2577
3073
  ],
2578
- requiredFields: {
2579
- list: ["server_id", "site_id"],
2580
- get: [
2581
- "server_id",
2582
- "site_id",
2583
- "id"
2584
- ],
2585
- create: [
2586
- "server_id",
2587
- "site_id",
2588
- "from",
2589
- "to"
2590
- ],
2591
- delete: [
2592
- "server_id",
2593
- "site_id",
2594
- "id"
2595
- ]
3074
+ inputSchemas: {
3075
+ list: /* @__PURE__ */ object({
3076
+ server_id: /* @__PURE__ */ string(),
3077
+ site_id: /* @__PURE__ */ string()
3078
+ }),
3079
+ get: /* @__PURE__ */ object({
3080
+ server_id: /* @__PURE__ */ string(),
3081
+ site_id: /* @__PURE__ */ string(),
3082
+ id: /* @__PURE__ */ string()
3083
+ }),
3084
+ create: /* @__PURE__ */ object({
3085
+ server_id: /* @__PURE__ */ string(),
3086
+ site_id: /* @__PURE__ */ string(),
3087
+ from: /* @__PURE__ */ string(),
3088
+ to: /* @__PURE__ */ string()
3089
+ }),
3090
+ delete: /* @__PURE__ */ object({
3091
+ server_id: /* @__PURE__ */ string(),
3092
+ site_id: /* @__PURE__ */ string(),
3093
+ id: /* @__PURE__ */ string()
3094
+ })
2596
3095
  },
2597
3096
  executors: {
2598
3097
  list: listRedirectRules,
@@ -2604,7 +3103,7 @@ const handleRedirectRules = createResourceHandler({
2604
3103
  switch (action) {
2605
3104
  case "list": return formatRedirectRuleList(data);
2606
3105
  case "get": return formatRedirectRule(data);
2607
- case "create": return formatRedirectRule(data);
3106
+ case "create": return "Done.";
2608
3107
  case "delete": return `Redirect rule ${args.id} deleted.`;
2609
3108
  default: return "Done.";
2610
3109
  }
@@ -2618,11 +3117,26 @@ const handleScheduledJobs = createResourceHandler({
2618
3117
  "create",
2619
3118
  "delete"
2620
3119
  ],
2621
- requiredFields: {
2622
- list: ["server_id"],
2623
- get: ["server_id", "id"],
2624
- create: ["server_id", "command"],
2625
- delete: ["server_id", "id"]
3120
+ inputSchemas: {
3121
+ list: /* @__PURE__ */ object({
3122
+ server_id: /* @__PURE__ */ string(),
3123
+ site_id: /* @__PURE__ */ optional(/* @__PURE__ */ string())
3124
+ }),
3125
+ get: /* @__PURE__ */ object({
3126
+ server_id: /* @__PURE__ */ string(),
3127
+ id: /* @__PURE__ */ string(),
3128
+ site_id: /* @__PURE__ */ optional(/* @__PURE__ */ string())
3129
+ }),
3130
+ create: /* @__PURE__ */ object({
3131
+ server_id: /* @__PURE__ */ string(),
3132
+ command: /* @__PURE__ */ string(),
3133
+ site_id: /* @__PURE__ */ optional(/* @__PURE__ */ string())
3134
+ }),
3135
+ delete: /* @__PURE__ */ object({
3136
+ server_id: /* @__PURE__ */ string(),
3137
+ id: /* @__PURE__ */ string(),
3138
+ site_id: /* @__PURE__ */ optional(/* @__PURE__ */ string())
3139
+ })
2626
3140
  },
2627
3141
  executors: {
2628
3142
  list: listScheduledJobs,
@@ -2709,25 +3223,21 @@ var RESOURCE_SCHEMAS = {
2709
3223
  required: {
2710
3224
  list: ["server_id"],
2711
3225
  get: ["server_id", "id"],
2712
- create: [
2713
- "server_id",
2714
- "domain",
2715
- "project_type"
2716
- ],
3226
+ create: ["server_id", "type"],
2717
3227
  delete: ["server_id", "id"],
2718
3228
  context: ["server_id", "id"],
2719
3229
  resolve: ["server_id", "query"]
2720
3230
  },
2721
3231
  create: {
2722
- domain: {
2723
- required: true,
2724
- type: "string — e.g. example.com"
2725
- },
2726
- project_type: {
3232
+ type: {
2727
3233
  required: true,
2728
3234
  type: "string — php, html, symfony, etc."
2729
3235
  },
2730
- directory: {
3236
+ name: {
3237
+ required: false,
3238
+ type: "string — domain name (e.g. example.com)"
3239
+ },
3240
+ web_directory: {
2731
3241
  required: false,
2732
3242
  type: "string — web root (/public)"
2733
3243
  }
@@ -3343,31 +3853,44 @@ const handleSecurityRules = createResourceHandler({
3343
3853
  "list",
3344
3854
  "get",
3345
3855
  "create",
3856
+ "update",
3346
3857
  "delete"
3347
3858
  ],
3348
- requiredFields: {
3349
- list: ["server_id", "site_id"],
3350
- get: [
3351
- "server_id",
3352
- "site_id",
3353
- "id"
3354
- ],
3355
- create: [
3356
- "server_id",
3357
- "site_id",
3358
- "name",
3359
- "credentials"
3360
- ],
3361
- delete: [
3362
- "server_id",
3363
- "site_id",
3364
- "id"
3365
- ]
3859
+ inputSchemas: {
3860
+ list: /* @__PURE__ */ object({
3861
+ server_id: /* @__PURE__ */ string(),
3862
+ site_id: /* @__PURE__ */ string()
3863
+ }),
3864
+ get: /* @__PURE__ */ object({
3865
+ server_id: /* @__PURE__ */ string(),
3866
+ site_id: /* @__PURE__ */ string(),
3867
+ id: /* @__PURE__ */ string()
3868
+ }),
3869
+ create: /* @__PURE__ */ object({
3870
+ server_id: /* @__PURE__ */ string(),
3871
+ site_id: /* @__PURE__ */ string(),
3872
+ name: /* @__PURE__ */ string(),
3873
+ credentials: /* @__PURE__ */ unknown()
3874
+ }),
3875
+ update: /* @__PURE__ */ object({
3876
+ server_id: /* @__PURE__ */ string(),
3877
+ site_id: /* @__PURE__ */ string(),
3878
+ id: /* @__PURE__ */ string(),
3879
+ name: /* @__PURE__ */ string(),
3880
+ path: /* @__PURE__ */ optional(/* @__PURE__ */ nullable(/* @__PURE__ */ string())),
3881
+ credentials: /* @__PURE__ */ unknown()
3882
+ }),
3883
+ delete: /* @__PURE__ */ object({
3884
+ server_id: /* @__PURE__ */ string(),
3885
+ site_id: /* @__PURE__ */ string(),
3886
+ id: /* @__PURE__ */ string()
3887
+ })
3366
3888
  },
3367
3889
  executors: {
3368
3890
  list: listSecurityRules,
3369
3891
  get: getSecurityRule,
3370
3892
  create: createSecurityRule,
3893
+ update: updateSecurityRule,
3371
3894
  delete: deleteSecurityRule
3372
3895
  },
3373
3896
  formatResult: (action, data, args) => {
@@ -3375,6 +3898,7 @@ const handleSecurityRules = createResourceHandler({
3375
3898
  case "list": return formatSecurityRuleList(data);
3376
3899
  case "get": return formatSecurityRule(data);
3377
3900
  case "create": return formatSecurityRule(data);
3901
+ case "update": return formatSecurityRule(data);
3378
3902
  case "delete": return `Security rule ${args.id} deleted.`;
3379
3903
  default: return "Done.";
3380
3904
  }
@@ -3419,7 +3943,7 @@ async function handleSiteContext(args, ctx) {
3419
3943
  if (!serverId) return errorResult("Missing required field: server_id");
3420
3944
  if (!siteId) return errorResult("Missing required field: id");
3421
3945
  const execCtx = ctx.executorContext;
3422
- const [site, deployments, certificates, redirectRules, securityRules] = await Promise.all([
3946
+ const [site, deployments, redirectRules, securityRules] = await Promise.all([
3423
3947
  getSite({
3424
3948
  server_id: serverId,
3425
3949
  site_id: siteId
@@ -3428,10 +3952,6 @@ async function handleSiteContext(args, ctx) {
3428
3952
  server_id: serverId,
3429
3953
  site_id: siteId
3430
3954
  }, execCtx),
3431
- listCertificates({
3432
- server_id: serverId,
3433
- site_id: siteId
3434
- }, execCtx),
3435
3955
  listRedirectRules({
3436
3956
  server_id: serverId,
3437
3957
  site_id: siteId
@@ -3445,7 +3965,6 @@ async function handleSiteContext(args, ctx) {
3445
3965
  return jsonResult({
3446
3966
  site: site.data,
3447
3967
  deployments: recentDeployments,
3448
- certificates: certificates.data,
3449
3968
  redirect_rules: redirectRules.data,
3450
3969
  security_rules: securityRules.data
3451
3970
  });
@@ -3460,17 +3979,17 @@ var _handleServers = createResourceHandler({
3460
3979
  "reboot",
3461
3980
  "resolve"
3462
3981
  ],
3463
- requiredFields: {
3464
- get: ["id"],
3465
- create: [
3466
- "provider",
3467
- "name",
3468
- "type",
3469
- "region"
3470
- ],
3471
- delete: ["id"],
3472
- reboot: ["id"],
3473
- resolve: ["query"]
3982
+ inputSchemas: {
3983
+ get: /* @__PURE__ */ object({ id: /* @__PURE__ */ string() }),
3984
+ create: /* @__PURE__ */ object({
3985
+ provider: /* @__PURE__ */ string(),
3986
+ name: /* @__PURE__ */ string(),
3987
+ type: /* @__PURE__ */ string(),
3988
+ region: /* @__PURE__ */ string()
3989
+ }),
3990
+ delete: /* @__PURE__ */ object({ id: /* @__PURE__ */ string() }),
3991
+ reboot: /* @__PURE__ */ object({ id: /* @__PURE__ */ string() }),
3992
+ resolve: /* @__PURE__ */ object({ query: /* @__PURE__ */ string() })
3474
3993
  },
3475
3994
  executors: {
3476
3995
  list: listServers,
@@ -3529,27 +4048,42 @@ var _handleSites = createResourceHandler({
3529
4048
  "list",
3530
4049
  "get",
3531
4050
  "create",
4051
+ "update",
3532
4052
  "delete",
3533
4053
  "resolve"
3534
4054
  ],
3535
- requiredFields: {
3536
- list: ["server_id"],
3537
- get: ["server_id", "id"],
3538
- create: ["server_id", "domain"],
3539
- delete: ["server_id", "id"],
3540
- resolve: ["server_id", "query"]
4055
+ inputSchemas: {
4056
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
4057
+ get: /* @__PURE__ */ object({
4058
+ server_id: /* @__PURE__ */ string(),
4059
+ id: /* @__PURE__ */ string()
4060
+ }),
4061
+ create: /* @__PURE__ */ object({
4062
+ server_id: /* @__PURE__ */ string(),
4063
+ type: /* @__PURE__ */ string()
4064
+ }),
4065
+ update: /* @__PURE__ */ object({
4066
+ server_id: /* @__PURE__ */ string(),
4067
+ id: /* @__PURE__ */ string()
4068
+ }),
4069
+ delete: /* @__PURE__ */ object({
4070
+ server_id: /* @__PURE__ */ string(),
4071
+ id: /* @__PURE__ */ string()
4072
+ }),
4073
+ resolve: /* @__PURE__ */ object({
4074
+ server_id: /* @__PURE__ */ string(),
4075
+ query: /* @__PURE__ */ string()
4076
+ })
3541
4077
  },
3542
4078
  executors: {
3543
4079
  list: listSites,
3544
4080
  get: getSite,
3545
4081
  create: createSite,
4082
+ update: updateSite,
3546
4083
  delete: deleteSite,
3547
4084
  resolve: resolveSites
3548
4085
  },
3549
- hints: (data, id) => {
3550
- const site = data;
3551
- return getSiteHints(String(site.server_id), id);
3552
- },
4086
+ hints: (_data, id, args) => getSiteHints(String(args.server_id), id),
3553
4087
  mapOptions: (action, args) => {
3554
4088
  switch (action) {
3555
4089
  case "list": return { server_id: args.server_id };
@@ -3559,9 +4093,19 @@ var _handleSites = createResourceHandler({
3559
4093
  };
3560
4094
  case "create": return {
3561
4095
  server_id: args.server_id,
3562
- domain: args.domain,
3563
- project_type: args.project_type ?? "php",
3564
- directory: args.directory
4096
+ type: args.type ?? "php",
4097
+ name: args.name ?? args.domain,
4098
+ web_directory: args.web_directory ?? args.directory
4099
+ };
4100
+ case "update": return {
4101
+ server_id: args.server_id,
4102
+ site_id: args.id,
4103
+ root_path: args.root_path,
4104
+ directory: args.directory,
4105
+ type: args.type,
4106
+ php_version: args.php_version,
4107
+ push_to_deploy: args.push_to_deploy,
4108
+ repository_branch: args.repository_branch
3565
4109
  };
3566
4110
  case "delete": return {
3567
4111
  server_id: args.server_id,
@@ -3579,6 +4123,7 @@ var _handleSites = createResourceHandler({
3579
4123
  case "list": return formatSiteList(data, args.server_id);
3580
4124
  case "get": return formatSite(data);
3581
4125
  case "create": return formatSite(data);
4126
+ case "update": return formatSite(data);
3582
4127
  case "delete": return `Site ${args.id} deleted from server ${args.server_id}.`;
3583
4128
  case "resolve": {
3584
4129
  const result = data;
@@ -3606,15 +4151,21 @@ const handleSshKeys = createResourceHandler({
3606
4151
  "create",
3607
4152
  "delete"
3608
4153
  ],
3609
- requiredFields: {
3610
- list: ["server_id"],
3611
- get: ["server_id", "id"],
3612
- create: [
3613
- "server_id",
3614
- "name",
3615
- "key"
3616
- ],
3617
- delete: ["server_id", "id"]
4154
+ inputSchemas: {
4155
+ list: /* @__PURE__ */ object({ server_id: /* @__PURE__ */ string() }),
4156
+ get: /* @__PURE__ */ object({
4157
+ server_id: /* @__PURE__ */ string(),
4158
+ id: /* @__PURE__ */ string()
4159
+ }),
4160
+ create: /* @__PURE__ */ object({
4161
+ server_id: /* @__PURE__ */ string(),
4162
+ name: /* @__PURE__ */ string(),
4163
+ key: /* @__PURE__ */ string()
4164
+ }),
4165
+ delete: /* @__PURE__ */ object({
4166
+ server_id: /* @__PURE__ */ string(),
4167
+ id: /* @__PURE__ */ string()
4168
+ })
3618
4169
  },
3619
4170
  executors: {
3620
4171
  list: listSshKeys,
@@ -3622,15 +4173,12 @@ const handleSshKeys = createResourceHandler({
3622
4173
  create: createSshKey,
3623
4174
  delete: deleteSshKey
3624
4175
  },
3625
- hints: (data, id) => {
3626
- const key = data;
3627
- return getSshKeyHints(String(key.server_id), id);
3628
- },
4176
+ hints: (_data, id, args) => getSshKeyHints(String(args.server_id), id),
3629
4177
  formatResult: (action, data, args) => {
3630
4178
  switch (action) {
3631
4179
  case "list": return formatSshKeyList(data);
3632
4180
  case "get": return formatSshKey(data);
3633
- case "create": return formatSshKey(data);
4181
+ case "create": return "Done.";
3634
4182
  case "delete": return `SSH key ${args.id} deleted.`;
3635
4183
  default: return "Done.";
3636
4184
  }
@@ -3749,12 +4297,18 @@ function routeToHandler(resource, action, args, ctx) {
3749
4297
  * Validates that the action matches the tool (read vs write).
3750
4298
  */
3751
4299
  async function executeToolWithCredentials(name, args, credentials) {
3752
- const { resource, action, compact, ...rest } = args;
4300
+ const resource = typeof args.resource === "string" ? args.resource : void 0;
4301
+ const action = typeof args.action === "string" ? args.action : void 0;
4302
+ const compact = typeof args.compact === "boolean" ? args.compact : void 0;
4303
+ const { resource: _r, action: _a, compact: _c, ...rest } = args;
3753
4304
  if (!resource || !action) return errorResult("Missing required fields: \"resource\" and \"action\".");
3754
4305
  if (resource === "batch") {
3755
4306
  if (name !== "forge") return errorResult("The \"batch\" resource is read-only and must be used with the \"forge\" tool, not \"forge_write\".");
3756
4307
  const handlerContext = {
3757
- executorContext: { client: new HttpClient({ token: credentials.apiToken }) },
4308
+ executorContext: {
4309
+ client: new HttpClient({ token: credentials.apiToken }),
4310
+ organizationSlug: credentials.organizationSlug
4311
+ },
3758
4312
  compact: compact ?? true
3759
4313
  };
3760
4314
  return routeToHandler(resource, action, {
@@ -3772,7 +4326,11 @@ async function executeToolWithCredentials(name, args, credentials) {
3772
4326
  /* v8 ignore start */
3773
4327
  return resource ? handleSchema(resource) : handleSchemaOverview();
3774
4328
  if (!VALID_RESOURCES.includes(resource)) return errorResult(`Unknown resource "${resource}". Valid resources: ${VALID_RESOURCES.join(", ")}. Use action="help" for documentation.`);
3775
- const executorContext = { client: new HttpClient({ token: credentials.apiToken }) };
4329
+ if (resource !== "user" && !credentials.organizationSlug) return errorResult("organizationSlug is required — configure it via FORGE_ORG env var, config file, or pass it in the request.");
4330
+ const executorContext = {
4331
+ client: new HttpClient({ token: credentials.apiToken }),
4332
+ organizationSlug: credentials.organizationSlug
4333
+ };
3776
4334
  const handlerContext = {
3777
4335
  executorContext,
3778
4336
  compact: compact ?? action !== "get",
@@ -3813,7 +4371,7 @@ async function executeToolWithCredentials(name, args, credentials) {
3813
4371
  return errorResult(errorMessage);
3814
4372
  }
3815
4373
  }
3816
- const VERSION = "0.3.0";
4374
+ const VERSION = "0.4.0";
3817
4375
  export { INSTRUCTIONS as a, getTools as i, executeToolWithCredentials as n, STDIO_ONLY_TOOLS as r, VERSION as t };
3818
4376
 
3819
- //# sourceMappingURL=version-CIiN0iJr.js.map
4377
+ //# sourceMappingURL=version-CHrJu_54.js.map