insta 0.1.7 → 0.1.8

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.
@@ -590,9 +590,25 @@ export async function serviceAlwaysOn(type, mode, serviceName, opts) {
590
590
  info(`${type} ${res.body.service?.name ?? svc.name}: always-on ${on ? 'ENABLED — machines stay warm (no cold starts; idle RAM bills at actual usage)' : 'disabled — scales to zero when idle'}`);
591
591
  }
592
592
  export const computeAlwaysOn = (mode, serviceName, opts) => serviceAlwaysOn('compute', mode, serviceName, opts);
593
+ const INSTANCE_ID = /^inst-[a-f0-9]{12}$/;
594
+ // A lone positional is the service even when it is all digits: service names may be.
595
+ export function removeTarget(count, service, opts) {
596
+ if (!INSTANCE_ID.test(opts.remove))
597
+ throw new Error(`invalid instance id: ${opts.remove} (expected inst-<12 hex digits>, the \`instance\` field of \`insta compute logs --json\`)`);
598
+ if (opts.region !== undefined)
599
+ throw new Error('--region does not apply to --remove');
600
+ if (service !== undefined) {
601
+ throw new Error(count !== undefined && count.trim() !== '' && Number.isFinite(Number(count)) ? '--remove lowers the replica count by one; pass no count' : `unexpected argument: ${service} (usage: insta compute scale [service] --remove <instance>)`);
602
+ }
603
+ return count;
604
+ }
593
605
  // `insta compute scale <count> [service] [--region <r>]` — POST /services/:id/scale. Count is
594
606
  // validated locally (1..10); the paid-plan gate is the backend's and its 403 flows verbatim.
595
607
  export async function computeScale(count, serviceName, opts) {
608
+ if (opts.remove !== undefined)
609
+ return computeRemoveInstance(removeTarget(count, serviceName, { remove: opts.remove, region: opts.region }), opts.remove, opts);
610
+ if (count === undefined)
611
+ throw new Error('a replica count is required (or --remove <instance> to drop one named instance)');
596
612
  const machineCount = parseCount(count);
597
613
  const api = await ApiClient.load();
598
614
  const p = await requireProject();
@@ -606,6 +622,20 @@ export async function computeScale(count, serviceName, opts) {
606
622
  return printJson(res.body.service);
607
623
  info(`scaled compute ${svc.name} to ${machineCount} replica(s)${opts.region ? ` in ${opts.region}` : ''}`);
608
624
  }
625
+ // `insta compute scale [service] --remove <instance>` — DELETE /services/:id/instances/:instance.
626
+ async function computeRemoveInstance(serviceName, instance, opts) {
627
+ const api = await ApiClient.load();
628
+ const p = await requireProject();
629
+ const branch = opts.branch ?? p.branch;
630
+ const { services } = await api.request('GET', `/projects/${p.projectId}/services${q(branch)}`);
631
+ const svc = resolveSoleService(services, 'compute', serviceName);
632
+ const res = await api.rawRequest('DELETE', `/projects/${p.projectId}/services/${svc.id}/instances/${instance}`);
633
+ if (handleApproval(res, opts.json))
634
+ return;
635
+ if (opts.json)
636
+ return printJson(res.body.service);
637
+ info(`removed ${instance} from compute ${svc.name}; ${res.body.service?.machine_count ?? '?'} replica(s) remain`);
638
+ }
609
639
  // ---- limits (the resource ceiling; paid plans) ----
610
640
  // Parse a human memory value into MB: "512", "512mb", "1gb", "2g", "1.5gb".
611
641
  // Exported for unit tests — this is the only place a user-typed size becomes a number.
package/dist/index.js CHANGED
@@ -365,8 +365,9 @@ compute.command('restart [service]').description("Restart a compute service by r
365
365
  .option('--json').option('--branch <branch>', 'branch (default: current)').action(guard((service, o) => computeCmd.computeRestart(service, o)));
366
366
  compute.command('status [service]').description("Show a compute service's desired vs. live state")
367
367
  .option('--json').option('--branch <branch>', 'branch (default: current)').action(guard((service, o) => computeCmd.computeStatus(service, o)));
368
- compute.command('scale <count> [service]').description('Set a compute service same-region replica count, 1 to 10 (paid plans only)')
368
+ compute.command('scale [count] [service]').description('Set a compute service same-region replica count, 1 to 10 (paid plans only). --remove <instance> instead drops that one instance and runs one fewer; instance ids are the `instance` field of `insta compute logs --json`. The last instance cannot be removed')
369
369
  .option('--region <region>', 'region to scale in (default: the service region)')
370
+ .option('--remove <instance>', 'drop this instance (inst-<12 hex>) and run one fewer replica')
370
371
  .option('--json').option('--branch <branch>', 'branch (default: current)')
371
372
  .action(guard((count, service, o) => computeCmd.computeScale(count, service, o)));
372
373
  compute.command('limits [service]').description("Show or set a compute service's resource ceiling (any plan within the free cap; raising above it needs a paid plan). --memory is the dial; cpu derives from it unless --cpu is given. Billing is actual usage — the ceiling caps what the app may burn, it is not a price")
package/dist/telemetry.js CHANGED
@@ -34,7 +34,7 @@ const SAFE_ARGS = {
34
34
  'env use': { 0: ENV }, 'project link': { 0: ID },
35
35
  'service add': { 0: SERVICE }, 'service remove': { 0: SERVICE }, 'service rename': { 0: SERVICE },
36
36
  'storage set-access': { 0: oneOf(['public', 'private']) },
37
- 'compute scale': { 0: NUMBER },
37
+ 'compute scale': { 0: (v) => /^\d+$/.test(v) },
38
38
  'compute always-on': { 0: ON_OFF }, 'postgres always-on': { 0: ON_OFF },
39
39
  'redis always-on': { 0: ON_OFF }, 'mysql always-on': { 0: ON_OFF }, 'mongodb always-on': { 0: ON_OFF },
40
40
  'template info': { 0: SLUG }, 'billing subscribe': { 0: oneOf(['pro', 'team']) },
@@ -69,8 +69,9 @@ export function redactOptions(opts) {
69
69
  }
70
70
  return out;
71
71
  }
72
- export function redactArgs(command, args) {
73
- const checks = SAFE_ARGS[command] ?? {};
72
+ export function redactArgs(command, args, options = {}) {
73
+ // with --remove the count's slot holds the service name
74
+ const checks = command === 'compute scale' && options.remove !== undefined ? {} : SAFE_ARGS[command] ?? {};
74
75
  return args.map((a, i) => (a === undefined ? null : typeof a === 'string' && checks[i]?.(a) ? a : REDACTED));
75
76
  }
76
77
  /** The deployment a `login --env|--api-url` targets. It is persisted only when the login succeeds, so
@@ -125,7 +126,7 @@ export function buildCommandEvent(command, args, options, outcome, ctx) {
125
126
  timestamp: new Date().toISOString(),
126
127
  properties: {
127
128
  command,
128
- args: redactArgs(command, args),
129
+ args: redactArgs(command, args, options),
129
130
  options: redactOptions(options),
130
131
  success: !cancelled && (outcome.exitCode === 0 || ranChild),
131
132
  cancelled,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insta",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "InstaCloud CLI — a thin client of the platform control-plane API.",
6
6
  "keywords": [