dockup-cli 1.1.0 → 1.2.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.
@@ -38,6 +38,8 @@ __export(agent_exports, {
38
38
  logsView: () => logsView,
39
39
  registryClear: () => registryClear,
40
40
  registrySet: () => registrySet,
41
+ resourcesSet: () => resourcesSet,
42
+ resourcesView: () => resourcesView,
41
43
  servicesList: () => servicesList,
42
44
  statusView: () => statusView
43
45
  });
@@ -326,6 +328,78 @@ async function registryClear(arg, opts) {
326
328
  (0, import_output.fail)(e.message);
327
329
  }
328
330
  }
331
+ const CPU_PER_MONTH = 463e-6 * 43200;
332
+ const MEM_PER_GB_PER_MONTH = 231e-6 * 43200;
333
+ const monthlyCost = (cpu, memMB) => Math.round((cpu * CPU_PER_MONTH + memMB / 1024 * MEM_PER_GB_PER_MONTH) * 100) / 100;
334
+ async function resourcesView(arg, opts) {
335
+ applyJson(opts);
336
+ requireAuth();
337
+ const t = target(arg);
338
+ try {
339
+ let memMB, cpu, name, kind;
340
+ if (opts?.db) {
341
+ const d = await import_api.api.getDatabaseDetail(t.projectSlug, t.serviceSlug);
342
+ const s = d.settings || d.database || d;
343
+ memMB = s.memoryLimitMB ?? 1024;
344
+ cpu = s.cpuLimit ?? 1;
345
+ name = d.database?.name || s.name || t.serviceSlug;
346
+ kind = "database";
347
+ } else {
348
+ const s = await import_api.api.getServiceStatus(t.projectSlug, t.serviceSlug);
349
+ memMB = s.memoryLimit ?? 2048;
350
+ cpu = s.cpuLimit ?? 2;
351
+ name = s.name || t.serviceSlug;
352
+ kind = "service";
353
+ }
354
+ const data = {
355
+ target: `${t.projectSlug}/${t.serviceSlug}`,
356
+ kind,
357
+ name,
358
+ cpu,
359
+ memoryMB: memMB,
360
+ estMonthlyUsd: monthlyCost(cpu, memMB),
361
+ limits: { maxCpu: 24, maxMemMB: 24576 }
362
+ };
363
+ (0, import_output.emit)(data, () => {
364
+ console.log(import_chalk.default.cyan(`
365
+ ${import_chalk.default.bold(name)} ${import_chalk.default.dim(data.target)} ${import_chalk.default.dim("(" + kind + ")")}
366
+ `));
367
+ console.log(` ${import_chalk.default.dim("CPU:")} ${cpu} vCPU`);
368
+ console.log(` ${import_chalk.default.dim("RAM:")} ${memMB >= 1024 ? memMB / 1024 + " GB" : memMB + " MB"}`);
369
+ console.log(` ${import_chalk.default.dim("Est.:")} $${data.estMonthlyUsd.toFixed(2)}/mo ${import_chalk.default.dim("+ disk")}`);
370
+ console.log();
371
+ });
372
+ } catch (e) {
373
+ (0, import_output.fail)(e.message);
374
+ }
375
+ }
376
+ async function resourcesSet(arg, opts) {
377
+ applyJson(opts);
378
+ requireAuth();
379
+ const t = target(arg);
380
+ const cpu = opts?.cpu !== void 0 ? parseFloat(opts.cpu) : void 0;
381
+ const mem = opts?.memory !== void 0 ? parseInt(opts.memory, 10) : void 0;
382
+ if (cpu === void 0 && mem === void 0) {
383
+ return (0, import_output.fail)("Provide --cpu <vCPU> and/or --memory <MB>.", { code: "bad_args" });
384
+ }
385
+ if (cpu !== void 0 && (isNaN(cpu) || cpu <= 0)) return (0, import_output.fail)("--cpu must be a positive number (max 24).", { code: "bad_args" });
386
+ if (mem !== void 0 && (isNaN(mem) || mem <= 0)) return (0, import_output.fail)("--memory must be MB (e.g. 4096, max 24576).", { code: "bad_args" });
387
+ try {
388
+ if (opts?.db) {
389
+ const res = await import_api.api.setDatabaseResources(t.projectSlug, t.serviceSlug, mem ?? 1024, cpu ?? 1);
390
+ (0, import_output.ok)({ target: `${t.projectSlug}/${t.serviceSlug}`, kind: "database", memoryMB: res.memoryLimitMB, cpu: res.cpuLimit, applied: res.applied, estMonthlyUsd: monthlyCost(res.cpuLimit, res.memoryLimitMB) }, () => {
391
+ console.log(import_chalk.default.green(`\u2713 ${res.cpuLimit} vCPU / ${res.memoryLimitMB} MB`) + import_chalk.default.dim(` ${res.applied ? "(applied)" : "(start db to apply)"} ~$${monthlyCost(res.cpuLimit, res.memoryLimitMB).toFixed(2)}/mo`));
392
+ });
393
+ } else {
394
+ const res = await import_api.api.setServiceResources(t.projectSlug, t.serviceSlug, mem ?? 2048, cpu ?? 2);
395
+ (0, import_output.ok)({ target: `${t.projectSlug}/${t.serviceSlug}`, kind: "service", memoryMB: res.memoryLimit, cpu: res.cpuLimit, applied: res.applied, estMonthlyUsd: monthlyCost(res.cpuLimit, res.memoryLimit) }, () => {
396
+ console.log(import_chalk.default.green(`\u2713 ${res.cpuLimit} vCPU / ${res.memoryLimit} MB`) + import_chalk.default.dim(` ${res.applied ? "(applied)" : "(redeploy to apply)"} ~$${monthlyCost(res.cpuLimit, res.memoryLimit).toFixed(2)}/mo`));
397
+ });
398
+ }
399
+ } catch (e) {
400
+ (0, import_output.fail)(e.message);
401
+ }
402
+ }
329
403
  async function deployTrigger(arg, opts) {
330
404
  applyJson(opts);
331
405
  requireAuth();
@@ -351,6 +425,8 @@ async function deployTrigger(arg, opts) {
351
425
  logsView,
352
426
  registryClear,
353
427
  registrySet,
428
+ resourcesSet,
429
+ resourcesView,
354
430
  servicesList,
355
431
  statusView
356
432
  });
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ var import_agent = require("./commands/agent");
32
32
  var import_workspace = require("./commands/workspace");
33
33
  var import_database = require("./commands/database");
34
34
  const program = new import_commander.Command();
35
- program.name("dockup").description("Dockup CLI - Deploy from your terminal").version("1.0.0");
35
+ program.name("dockup").description("Dockup CLI - Deploy from your terminal").version("1.2.0");
36
36
  program.command("login").description("Authenticate with Dockup").option("-t, --token <token>", "Use API token directly").action(import_auth.login);
37
37
  program.command("logout").description("Log out from Dockup").action(import_auth.logout);
38
38
  program.command("whoami").description("Show current logged in user").action(import_auth.whoami);
@@ -52,6 +52,8 @@ program.command("deployments [project/service]").alias("history").description("D
52
52
  const registryCmd = program.command("registry").description("Private registry auth for a private FROM base image");
53
53
  registryCmd.command("set [project/service]").description("Set registry credentials (used at build time to pull a private base image)").requiredOption("--url <registry>", "Registry host, e.g. ghcr.io").requiredOption("--user <username>", "Registry username").requiredOption("--token <token>", "PAT / password").option("-j, --json", "Machine-readable JSON output").action(import_agent.registrySet);
54
54
  registryCmd.command("clear [project/service]").description("Remove registry credentials").option("-j, --json", "Machine-readable JSON output").action(import_agent.registryClear);
55
+ const resourcesCmd = program.command("resources [project/target]").alias("res").description("View reserved CPU/RAM (use `resources set` to change; --db for databases)").option("--db", "Target a database instead of a service").option("-j, --json", "Machine-readable JSON output").action(import_agent.resourcesView);
56
+ resourcesCmd.command("set [project/target]").description("Set reserved CPU/RAM \u2014 PRO (max 24 vCPU / 24576 MB)").option("--cpu <vcpu>", "vCPU to reserve, e.g. 2").option("--memory <mb>", "RAM in MB to reserve, e.g. 4096").option("--db", "Target a database instead of a service").option("-j, --json", "Machine-readable JSON output").action(import_agent.resourcesSet);
55
57
  program.command("deploy [project/service]").description("Trigger a deployment (no git push; use `push` for git+deploy)").option("-b, --branch <branch>", "Branch to deploy").option("-j, --json", "Machine-readable JSON output").action(import_agent.deployTrigger);
56
58
  program.command("open").description("Open service in browser").option("-d, --dashboard", "Open dashboard instead of live URL").action(import_open.open);
57
59
  const workspaceCmd = program.command("workspace").alias("ws").description("Manage workspaces");
package/dist/lib/api.js CHANGED
@@ -238,6 +238,19 @@ class ApiClient {
238
238
  async clearRegistry(projectSlug, serviceSlug) {
239
239
  return this.delete(`/projects/${projectSlug}/services/${serviceSlug}/registry`);
240
240
  }
241
+ // ==================== RESOURCES (reserved CPU/RAM) ====================
242
+ // Database detail (project-scoped) — includes memoryLimitMB / cpuLimit.
243
+ async getDatabaseDetail(projectSlug, dbSlug) {
244
+ return this.get(`/projects/${projectSlug}/databases/${dbSlug}`);
245
+ }
246
+ // Set reserved CPU/RAM for a service (PRO). Applied by recreating the container.
247
+ async setServiceResources(projectSlug, serviceSlug, memoryLimit, cpuLimit) {
248
+ return this.post(`/projects/${projectSlug}/services/${serviceSlug}/resources`, { memoryLimit, cpuLimit });
249
+ }
250
+ // Set reserved CPU/RAM for a database (PRO, main-server only).
251
+ async setDatabaseResources(projectSlug, dbSlug, memoryLimitMB, cpuLimit) {
252
+ return this.post(`/projects/${projectSlug}/databases/${dbSlug}/resources`, { memoryLimitMB, cpuLimit });
253
+ }
241
254
  }
242
255
  const api = new ApiClient();
243
256
  // Annotate the CommonJS export names for ESM import in node:
package/dockupcli.md CHANGED
@@ -135,6 +135,32 @@ dockup registry clear my-project/my-api --json
135
135
  - `dockup info --json` shows `registryUrl`, `registryUsername` and
136
136
  `hasRegistryAuth` (the password is never exposed).
137
137
 
138
+ ### Resources (reserved CPU & RAM — PRO)
139
+
140
+ Pick how much CPU and RAM a service or database reserves. Billing follows the
141
+ allocation (≈ $20/vCPU + $10/GB per month + disk). Max **24 vCPU / 24576 MB**.
142
+
143
+ ```bash
144
+ # View current allocation + estimated monthly cost
145
+ dockup resources my-project/my-api --json
146
+ # → {"target","kind":"service","name","cpu","memoryMB","estMonthlyUsd","limits":{"maxCpu":24,"maxMemMB":24576}}
147
+
148
+ # Set (service). Applied live by recreating the container (no rebuild; data persists).
149
+ dockup resources set my-project/my-api --cpu 2 --memory 4096 --json
150
+ # → {"ok":true,"target","kind":"service","cpu","memoryMB","applied","estMonthlyUsd"}
151
+
152
+ # Databases: add --db (main-server databases only; recreates the container, volume kept)
153
+ dockup resources my-project/my-db --db --json
154
+ dockup resources set my-project/my-db --db --cpu 1 --memory 2048 --json
155
+ ```
156
+
157
+ - `--cpu` is vCPU (e.g. `0.5`, `2`, `8`); `--memory` is MB (e.g. `512`, `4096`).
158
+ - Values out of range are clamped to `[0.5 vCPU, 512 MB] … [24 vCPU, 24576 MB]`.
159
+ - PRO only — non-PRO accounts get `403 Upgrade required`.
160
+ - `applied:true` means a running container was recreated immediately; otherwise
161
+ it applies on the next deploy (service) / start (database).
162
+ - `dockup info --json` also surfaces a service's current `memoryLimit`/`cpuLimit`.
163
+
138
164
  ---
139
165
 
140
166
  ## Agent recipes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dockup-cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Dockup CLI - deploy, env, logs, registry & more from your terminal (agent-friendly --json)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {