insta 0.1.25 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -229,7 +229,7 @@ build never reaches a production installer.
229
229
  | `insta branch` | `create` · `list` · `switch` · `delete` · `merge` |
230
230
  | `insta service` (`services`, `svc`) | `add` · `list` · `remove` · `rename` |
231
231
  | `insta secrets` | Write `.env`, plus `list` · `set` · `unset` · `bind` · `unbind` · `bindings` · `sources` · `tree` |
232
- | `insta domain` | Bought or bring-your-own: `attach` · `check` · `detach`; buy through InstaCloud: `search` · `buy` · `list` · `status` · `records …` |
232
+ | `insta domain` | Bought or bring-your-own: `attach` · `check` · `detach`. Domains already bought through InstaCloud: `list` · `status` · `records …` |
233
233
  | `insta compute` | `start` · `stop` · `suspend` · `restart` · `status` · `scale` · `limits` · `volume` · `always-on` · `exec` · `ssh` · `repo` · `connect-repo` · `watch-paths` · `disconnect-repo` · `logs` · `metrics` |
234
234
  | `insta postgres` | `url` (print the DSN) · `connect` (psql) · `stats` · `limits` · `restart` · `volume` · `always-on` · `private-access` · `public-access` · `logs` · `metrics` — every verb takes `[service]` |
235
235
  | `insta redis` · `mysql` · `mongodb` | `query` · `status` · `limits` · `volume` · `always-on` · `logs` · `metrics` |
@@ -165,11 +165,11 @@ export function jobShowLines(j, serviceName) {
165
165
  // decrypts it. Saying so beats an empty "value" column that reads as "this header is set to
166
166
  // nothing". Secret-backed headers get a row each, because the secret NAME is readable and is the
167
167
  // thing an operator needs when a rotation or a missing secret is the question.
168
- const refs = Object.entries(refsOf(j.request));
169
- const literal = j.request.headerNames.filter((n) => findHeader(refsOf(j.request), n) === undefined);
168
+ const refs = refsOf(j.request);
169
+ const literal = j.request.headerNames.filter((n) => findHeader(refs, n) === undefined);
170
170
  const rows = [
171
171
  ...(literal.length ? [`${literal.join(', ')} (names only — values are encrypted at rest and never returned)`] : []),
172
- ...refs.map(([h, s]) => `${h} ← secret ${s} (resolved at send time)`),
172
+ ...Object.entries(refs).map(([h, s]) => `${h} ← secret ${s} (resolved at send time)`),
173
173
  ];
174
174
  if (!rows.length)
175
175
  lines.push(' headers (none)');
@@ -30,9 +30,6 @@ export async function domainSearch(keyword, opts, deps) {
30
30
  return printJson(r);
31
31
  for (const line of searchLines(r.results))
32
32
  info(line);
33
- const buyable = r.results.find((x) => x.purchasable);
34
- if (buyable)
35
- info(`buy one: insta domain buy ${buyable.domainName}`);
36
33
  }
37
34
  export async function domainBuy(name, opts, deps) {
38
35
  const years = whole('--years', opts.years);
@@ -117,10 +114,10 @@ function domainLines(d, linked = true) {
117
114
  out.push(` ${h.hostname.padEnd(w)} ${h.state}${h.service ? ` → ${h.service}` : ''}${h.reason ? ` — ${h.reason}` : ''}`);
118
115
  return out;
119
116
  }
120
- function orderStatusLines(o, linked = true) {
117
+ function orderStatusLines(o) {
121
118
  const out = [`order ${o.id}: ${o.domainName} — ${o.status}${o.failedReason ? ` — ${o.failedReason}` : ''}`];
122
- if (linked && o.status === 'canceled')
123
- out.push(` the checkout closed without payment — order again: insta domain buy ${o.domainName}`);
119
+ if (o.status === 'canceled')
120
+ out.push(' the checkout closed without payment');
124
121
  return out;
125
122
  }
126
123
  export async function domainList(opts, deps) {
@@ -129,7 +126,7 @@ export async function domainList(opts, deps) {
129
126
  if (opts.json)
130
127
  return printJson(r);
131
128
  if (!r.items.length)
132
- return info('no domains bought through InstaCloud in this org (search: insta domain search <keyword>)');
129
+ return info('no domains bought through InstaCloud in this org');
133
130
  for (const d of r.items)
134
131
  for (const line of domainLines(d, !opts.org))
135
132
  info(line);
@@ -147,7 +144,7 @@ export async function domainStatus(name, opts, deps) {
147
144
  die(`${host} was not bought through this org`);
148
145
  if (opts.json)
149
146
  return printJson({ domain, order });
150
- for (const line of domain ? domainLines(domain, !opts.org) : orderStatusLines(order, !opts.org))
147
+ for (const line of domain ? domainLines(domain, !opts.org) : orderStatusLines(order))
151
148
  info(line);
152
149
  }
153
150
  // ---- nameservers and transferring out ----
@@ -369,10 +366,9 @@ export function zoneRecordLines(records) {
369
366
  * into the zone itself — apexes included — instead of printing them for hand-copying. The zone is
370
367
  * SEEDED by the provider's record scan, which is a heuristic: the review-then-switch contract
371
368
  * (printed, and enforced by nothing else) is to compare `zone records` against the domain's current
372
- * DNS and add what is missing at the CURRENT provider — re-running delegate re-imports — before
373
- * re-pointing. A domain carrying live MX records is refused outright: a DNS move that can drop
374
- * mail is never done implicitly. 202 approval_required in agent mode (zone.delegate); org admin
375
- * either way.
369
+ * DNS before re-pointing. A domain carrying live MX records is refused outright: a DNS move that
370
+ * can drop mail is never done implicitly. 202 approval_required in agent mode (zone.delegate); org
371
+ * admin either way.
376
372
  */
377
373
  const orgArgOf = (opts) => (opts.org ? ` --org ${opts.org}` : '');
378
374
  export async function zoneDelegate(domainName, opts, deps) {
@@ -391,7 +387,7 @@ export async function zoneDelegate(domainName, opts, deps) {
391
387
  const z = res.body;
392
388
  for (const line of zoneLines(z, opts.org))
393
389
  info(line);
394
- info(`the zone was seeded by a provider scan — a heuristic. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, add anything missing at your CURRENT provider (re-running delegate re-imports), and only then switch the nameservers.`);
390
+ info(`the zone was seeded by a provider scan, which can miss records. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, and only then switch the nameservers.`);
395
391
  }
396
392
  export async function zoneList(opts, deps) {
397
393
  const { api, orgId } = await orgDeps(opts, deps);
@@ -412,7 +408,7 @@ export async function zoneList(opts, deps) {
412
408
  if (opts.json)
413
409
  return printJson(r);
414
410
  if (!r.items.length)
415
- return info('no delegated zones — start one: insta domain zone delegate <domain>');
411
+ return info('no delegated zones');
416
412
  for (const z of r.items)
417
413
  for (const line of zoneLines(z, opts.org))
418
414
  info(line);
@@ -425,7 +421,7 @@ export async function zoneRecords(domainName, opts, deps) {
425
421
  for (const line of zoneRecordLines(r.items))
426
422
  info(line);
427
423
  if (r.items.length)
428
- info(`every type shows here (the scan is a heuristic) — add anything missing at your current DNS provider and re-run \`insta domain zone delegate ${domainName.trim().toLowerCase()}${orgArgOf(opts)}\` to re-import before switching nameservers`);
424
+ info('every type shows here. The provider scan can miss records, so compare them against your current DNS before switching nameservers');
429
425
  }
430
426
  /**
431
427
  * Release a delegated zone: the platform prunes the records it published and deletes the managed
@@ -344,6 +344,9 @@ async function promptVariable(v) {
344
344
  throw new CliCancel();
345
345
  return answer.trim();
346
346
  }
347
+ // The poll route is keyed by deployment id, not project: name the project so agent mode signs
348
+ // with the project-bound session (a bootstrap session is rejected as "for a different project").
349
+ const readDeployment = (api, projectId) => (id) => api.request('GET', `/template-deployments/${id}`, undefined, { projectId });
347
350
  export async function templateDeploy(target, opts = {}, deps = {}) {
348
351
  const given = parseSetFlags(opts.set ?? []); // a typo'd --set fails before any network access
349
352
  // --json asked for parseable output: every human progress line is suppressed so stdout carries
@@ -424,9 +427,7 @@ export async function templateDeploy(target, opts = {}, deps = {}) {
424
427
  for (const line of publicBucketLines(services))
425
428
  info(line);
426
429
  }
427
- // The poll route is keyed by deployment id, not project: name the project so agent mode signs
428
- // with the project-bound session (a bootstrap session is rejected as "for a different project").
429
- const dep = await watchDeployment((id) => api.request('GET', `/template-deployments/${id}`, undefined, { projectId: p.projectId }), deploymentId, quiet ? () => { } : info, deps.wait);
430
+ const dep = await watchDeployment(readDeployment(api, p.projectId), deploymentId, quiet ? () => { } : info, deps.wait);
430
431
  if (opts.json)
431
432
  return printJson(source ? { source, ...dep } : dep);
432
433
  info(`template ${codeLabel} deployed to branch ${branchName}`);
@@ -526,10 +527,10 @@ export async function templateUpgrade(service, opts = {}, deps = {}) {
526
527
  const branchName = opts.branch ?? p.branch;
527
528
  const ask = deps.ask ?? promptVariable;
528
529
  const target = await deploymentOfService(api, p.projectId, branchName, service, 'upgrade');
529
- // The poll and preview routes are keyed by deployment id, not project: name the project so agent
530
- // mode signs with the project-bound session, exactly as templateDeploy does.
531
- // The preview is a GET and raises coded 409s of its own (template_superseded among them), so it
532
- // gets the same reading as the POST rather than the raw "<sentence> (HTTP 409)" the guard prints.
530
+ // The preview route is keyed by deployment id, not project, so it names the project as
531
+ // readDeployment does. It is a GET and raises coded 409s of its own (template_superseded among
532
+ // them), so it gets the same reading as the POST rather than the raw "<sentence> (HTTP 409)" the
533
+ // guard prints.
533
534
  let plan;
534
535
  try {
535
536
  ;
@@ -576,7 +577,7 @@ export async function templateUpgrade(service, opts = {}, deps = {}) {
576
577
  const deploymentId = res.body.deploymentId ?? (res.body.deployment ?? res.body).id;
577
578
  if (!quiet)
578
579
  info(`upgrading ${service} to ${plan.to_version} (${deploymentId})`);
579
- const dep = await watchDeployment((id) => api.request('GET', `/template-deployments/${id}`, undefined, { projectId: p.projectId }), deploymentId, quiet ? () => { } : info, deps.wait);
580
+ const dep = await watchDeployment(readDeployment(api, p.projectId), deploymentId, quiet ? () => { } : info, deps.wait);
580
581
  if (quiet)
581
582
  return printJson(dep);
582
583
  info(`${service} is on ${plan.to_version}`);
@@ -635,7 +636,7 @@ export async function templateRollback(service, opts = {}, deps = {}) {
635
636
  return;
636
637
  const deploymentId = res.body.deploymentId ?? (res.body.deployment ?? res.body).id;
637
638
  out(`rolling ${service} back (${deploymentId})`);
638
- const dep = await watchDeployment((id) => api.request('GET', `/template-deployments/${id}`, undefined, { projectId: p.projectId }), deploymentId, out, deps.wait);
639
+ const dep = await watchDeployment(readDeployment(api, p.projectId), deploymentId, out, deps.wait);
639
640
  if (quiet)
640
641
  return printJson(dep);
641
642
  info(`${service} is back on ${(dep.deployment ?? dep).template_version ?? 'its previous version'}`);
package/dist/index.js CHANGED
@@ -273,11 +273,12 @@ cron.command('runs <name>').description('Run history, most recent first: status,
273
273
  cron.command('preview <expression>').description('Validate an expression and print its next five UTC fire times — answered by the same parser the scheduler uses. Exits 1 when the expression is invalid')
274
274
  .option('--json').action(guard((expression, o) => cronCmd.cronPreview(expression, o)));
275
275
  // ---- domain (bought here, or bring your own; hostnames on compute services; DNS of bought zones) ----
276
- const dom = program.command('domain').description('Domains: buy through InstaCloud or bring your own — attach / check / detach hostnames on compute services; DNS records of bought domains');
277
- dom.command('search <keyword>').description('Search purchasable names with prices (a label like "myapp" or a full name like "myapp.com")')
276
+ const dom = program.command('domain').description('Domains: attach, check and detach hostnames on compute services, and manage domains already bought through InstaCloud');
277
+ // search, buy and zone delegate are hidden while the platform pauses new orders and BYO delegation.
278
+ dom.command('search <keyword>', { hidden: true }).description('Search purchasable names with prices (a label like "myapp" or a full name like "myapp.com")')
278
279
  .option('--tlds <list>', 'comma-separated TLDs to include').option('--org <id>', "target org (default: linked project's org)").option('--json')
279
280
  .action(guard((keyword, o) => domainCmd.domainSearch(keyword, o)));
280
- dom.command('buy <name>').description('Buy a domain — pay at the printed Stripe Checkout link. It serves nothing until you attach it (gated: domain.purchase)')
281
+ dom.command('buy <name>', { hidden: true }).description('Buy a domain — pay at the printed Stripe Checkout link. It serves nothing until you attach it (gated: domain.purchase)')
281
282
  .option('--years <n>', 'registration term in years (default 1)')
282
283
  .option('--no-open', 'print the checkout URL instead of opening a browser').option('--json')
283
284
  .action(guard((name, o) => domainCmd.domainBuy(name, o)));
@@ -309,14 +310,14 @@ ns.command('set <domain> <nameservers...>').description("Delegate the zone to na
309
310
  ns.command('reset <domain>').description("Put the zone back on the registrar's own nameservers — from your own delegation or from an InstaCloud-managed zone alike; a hostname `set` took down is re-attached with `insta domain attach`, one a managed zone was serving re-verifies on its own")
310
311
  .option('--org <id>', "target org (default: linked project's org)").option('--json')
311
312
  .action(guard((domain, o) => domainCmd.domainNameserversReset(domain, o)));
312
- const zone = dom.command('zone').description("Bring-your-own domains served from an InstaCloud-managed zone (nameserver delegation): point the domain's registrar at the pair `zone delegate` answers, and every attach's records — apexes included — are published for you. The alternative stays available: `insta domain attach` alone prints records to paste into your own zone");
313
- zone.command('delegate <domain>').description("Delegate a domain you own elsewhere: builds its managed zone (seeded by a provider record scan — a HEURISTIC, so review `zone records` and add anything missing at your current provider BEFORE switching nameservers; re-running delegate re-imports) and answers the two nameservers to set at your registrar. A domain carrying live MX records is refused — move mail first or keep the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
313
+ const zone = dom.command('zone').description("Bring-your-own domains already delegated to an InstaCloud-managed zone: list them, review their records, or release them. For a hostname under a domain you own elsewhere, `insta domain attach` prints the records to add in your own DNS");
314
+ zone.command('delegate <domain>', { hidden: true }).description("Delegate a domain you own elsewhere: builds its managed zone (seeded by a provider record scan — a HEURISTIC, so review `zone records` against your current DNS BEFORE switching nameservers) and answers the two nameservers to set at your registrar. A domain carrying live MX records is refused — move mail first or keep the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
314
315
  .option('--org <id>', "target org (default: linked project's org)").option('--json')
315
316
  .action(guard((domain, o) => domainCmd.zoneDelegate(domain, o)));
316
317
  zone.command('list').description("The org's delegated zones: which are still waiting for the nameserver switch and which are serving")
317
318
  .option('--org <id>', "target org (default: linked project's org)").option('--json')
318
319
  .action(guard((o) => domainCmd.zoneList(o)));
319
- zone.command('records <domain>').description("Every record in the delegated zone — the pre-switch review. The scan seeds common records but is not exhaustive: compare against your current DNS and add what is missing at your CURRENT provider, then re-run `zone delegate` to re-import")
320
+ zone.command('records <domain>').description("Every record in the delegated zone, for review before you switch nameservers. The provider scan seeds common records but is not exhaustive, so compare it against your current DNS first")
320
321
  .option('--org <id>', "target org (default: linked project's org)").option('--json')
321
322
  .action(guard((domain, o) => domainCmd.zoneRecords(domain, o)));
322
323
  zone.command('release <domain>').description("Release a delegated zone: the platform's records are pruned and the zone deleted. Point the nameservers back at your own provider; hostnames re-verify on the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insta",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "type": "module",
5
5
  "description": "InstaCloud CLI — a thin client of the platform control-plane API.",
6
6
  "keywords": [