@layers/amba 4.1.3 → 4.1.4

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.
@@ -302,6 +302,12 @@ export interface FunctionDomain {
302
302
  project_id: string;
303
303
  function_name: string;
304
304
  hostname: string;
305
+ /** Required record at authoritative DNS. */
306
+ dns_record_type?: 'CNAME';
307
+ /** True for a registrable root rather than a subdomain. */
308
+ is_apex?: boolean;
309
+ /** Additive provider-neutral DNS warning, populated for apex hostnames. */
310
+ dns_note?: string | null;
305
311
  cert_status: 'pending_validation' | 'pending_issuance' | 'pending_deployment' | 'active' | 'error';
306
312
  ownership_status: 'pending' | 'active' | 'error';
307
313
  /** True only when certificate/ownership are active and routing reconciliation succeeded. */
@@ -674,6 +680,22 @@ export interface SiteDomainRow {
674
680
  provider_hostname_id: string | null;
675
681
  cert_status: 'pending_validation' | 'pending_issuance' | 'pending_deployment' | 'active' | 'error';
676
682
  ownership_status: 'pending' | 'active' | 'error';
683
+ /** Edge Worker route reconciliation. */
684
+ route_status: 'pending' | 'active' | 'error';
685
+ /** Host-to-site KV reconciliation. */
686
+ kv_status: 'pending' | 'active' | 'error';
687
+ /** Whether Amba owns and manages the authoritative DNS zone. */
688
+ dns_managed: boolean;
689
+ /** Managed-DNS reconciliation, or not_required for externally managed DNS. */
690
+ dns_status: 'not_required' | 'pending' | 'active' | 'error';
691
+ /**
692
+ * Authoritative reachability. True only when certificate, ownership,
693
+ * Worker route, KV, and (when managed) DNS are all active.
694
+ */
695
+ live: boolean;
696
+ dns_record_type?: 'CNAME';
697
+ is_apex?: boolean;
698
+ dns_note?: string | null;
677
699
  created_at: string;
678
700
  updated_at: string;
679
701
  }
@@ -731,8 +753,14 @@ export declare function addSiteDomainViaApi(projectId: string, siteName: string,
731
753
  provider_hostname_id: string;
732
754
  cert_status: SiteDomainRow["cert_status"];
733
755
  ownership_status: SiteDomainRow["ownership_status"];
756
+ route_status: SiteDomainRow["route_status"];
757
+ kv_status: SiteDomainRow["kv_status"];
758
+ dns_managed: SiteDomainRow["dns_managed"];
759
+ dns_status: SiteDomainRow["dns_status"];
760
+ live: boolean;
734
761
  dns_target: string;
735
762
  is_apex: boolean;
763
+ dns_record_type: "CNAME";
736
764
  dns_note: string | null;
737
765
  ssl_validation: DomainValidationRecord[];
738
766
  ownership_verification: DomainOwnershipVerification | null;
@@ -809,6 +837,12 @@ export interface DomainPurchaseResult {
809
837
  provisioned?: boolean;
810
838
  cert_status?: string;
811
839
  ownership_status?: string;
840
+ route_status?: string;
841
+ kv_status?: string;
842
+ dns_managed?: boolean;
843
+ dns_status?: string;
844
+ /** Authoritative reachability across provider, route, KV, and managed DNS. */
845
+ hostname_live?: boolean;
812
846
  dns_autoconfigured?: boolean;
813
847
  url?: string;
814
848
  public_url?: string;
@@ -825,7 +859,22 @@ export declare function purchaseDomainViaApi(projectId: string, body: {
825
859
  privacy?: boolean;
826
860
  years?: number;
827
861
  }): Promise<ApiResponse<DomainPurchaseResult>>;
828
- export declare function listPurchasedDomainsViaApi(projectId: string): Promise<ApiListResponse<Record<string, unknown>>>;
862
+ export interface PurchasedDomainStatus extends Record<string, unknown> {
863
+ domain: string;
864
+ status: string;
865
+ site?: string | null;
866
+ provisioned: boolean;
867
+ cert_status?: string;
868
+ ownership_status?: string;
869
+ route_status?: string;
870
+ kv_status?: string;
871
+ dns_managed?: boolean;
872
+ dns_status?: string;
873
+ /** Reachability; deliberately separate from purchase/payment completion. */
874
+ hostname_live: boolean;
875
+ aliases: Array<Record<string, unknown>>;
876
+ }
877
+ export declare function listPurchasedDomainsViaApi(projectId: string): Promise<ApiListResponse<PurchasedDomainStatus>>;
829
878
  /**
830
879
  * Delete a function entirely. Cascade: removes the deployed script
831
880
  * (backend 404 treated as success), then marks every historical
@@ -845,10 +894,19 @@ export declare function attachSiteDomain(projectId: string, siteName: string, in
845
894
  provider_hostname_id?: string;
846
895
  }): Promise<ApiResponse<SiteDomainRow>>;
847
896
  export declare function listSiteDomains(projectId: string, siteName: string): Promise<ApiListResponse<SiteDomainRow>>;
897
+ /** @deprecated Lifecycle is provider-owned; this delegates to authoritative refresh. */
848
898
  export declare function updateSiteDomain(projectId: string, siteName: string, hostname: string, patch: {
849
899
  cert_status: SiteDomainRow['cert_status'];
850
900
  provider_hostname_id?: string;
851
- }): Promise<ApiResponse<SiteDomainRow>>;
901
+ }): Promise<ApiResponse<SiteDomainRow & {
902
+ ssl_validation: DomainValidationRecord[];
903
+ ownership_verification: DomainOwnershipVerification | null;
904
+ ownership_verification_http: {
905
+ http_url?: string;
906
+ http_body?: string;
907
+ } | null;
908
+ note?: string;
909
+ }>>;
852
910
  export declare function detachSiteDomain(projectId: string, siteName: string, hostname: string): Promise<ApiResponse<{
853
911
  deleted: boolean;
854
912
  }>>;
@@ -36,7 +36,7 @@ export interface SitesDomainAddOptions {
36
36
  zoneId?: string;
37
37
  /** Skip the cert poll loop. Caller will check status later. */
38
38
  noWait?: boolean;
39
- /** Max seconds to wait for cert_status='active' before bailing. Default 600. */
39
+ /** Max seconds to wait for both ownership and certificate activation. Default 600. */
40
40
  timeout?: number;
41
41
  }
42
42
  export declare function sitesDomainAddCommand(hostname: string, options: SitesDomainAddOptions): Promise<void>;
@@ -74,6 +74,10 @@ declare function collectFiles(dir: string): Promise<SiteFile[]>;
74
74
  export declare class SitesStaticOnlyError extends Error {
75
75
  readonly code: "SITES_STATIC_ONLY";
76
76
  }
77
+ declare function isProviderTerminalDomainFailure(d: {
78
+ cert_status: string;
79
+ ownership_status: string;
80
+ }): boolean;
77
81
  /**
78
82
  * Test-only re-export of internal helpers. Tests reach in via this
79
83
  * namespace so the production module surface stays clean. Not a public
@@ -83,5 +87,6 @@ export declare const __testHelpers: {
83
87
  collectFiles: typeof collectFiles;
84
88
  BLOCKED_FILE_NAMES: Set<string>;
85
89
  BLOCKED_DIR_NAMES: Set<string>;
90
+ isProviderTerminalDomainFailure: typeof isProviderTerminalDomainFailure;
86
91
  };
87
92
  export {};
package/dist/index.js CHANGED
@@ -6477,6 +6477,7 @@ function printDnsInstructions(domain) {
6477
6477
  if ("cleanup_pending" in domain && domain.cleanup_pending === true) console.log(pc.yellow(" Routing cleanup is incomplete; retry with domains refresh."));
6478
6478
  if ("dns_autoconfigured" in domain && domain.dns_autoconfigured === true) console.log(pc.dim(` DNS records published automatically${domain.dns_records_published === void 0 ? "" : ` (${domain.dns_records_published})`}.`));
6479
6479
  if (domain.dns_target) console.log(pc.dim(` DNS target: CNAME ${domain.hostname} → ${domain.dns_target}`));
6480
+ if (domain.dns_note) console.log(pc.yellow(` DNS requirement: ${domain.dns_note}`));
6480
6481
  for (const record of domain.ssl_validation ?? []) if (record.txt_name && record.txt_value) console.log(pc.dim(` Certificate TXT: ${record.txt_name} = ${record.txt_value}`));
6481
6482
  else if (record.http_url && record.http_body) console.log(pc.dim(` Certificate HTTP: ${record.http_url} → ${record.http_body}`));
6482
6483
  const ownership = domain.ownership_verification;
@@ -9021,7 +9022,6 @@ async function sitesDomainAddCommand(hostname, options) {
9021
9022
  console.log(pc.green(" ✓") + ` Custom hostname registered`);
9022
9023
  const validation = res.data.ssl_validation ?? [];
9023
9024
  const ownership = res.data.ownership_verification;
9024
- const isApex = res.data.is_apex ?? false;
9025
9025
  if (validation.length > 0 || ownership) {
9026
9026
  console.log();
9027
9027
  console.log(pc.dim(" 1. Publish these DNS records to verify ownership:"));
@@ -9035,10 +9035,8 @@ async function sitesDomainAddCommand(hostname, options) {
9035
9035
  }
9036
9036
  console.log(` ${pc.bold("CNAME")} ${hostname} → ${pc.cyan(res.data.dns_target)}`);
9037
9037
  console.log();
9038
- if (isApex) {
9039
- console.log(pc.dim(` ${hostname} is a root domain — the CNAME above works only if your DNS provider`));
9040
- console.log(pc.dim(` flattens CNAME-at-root. If it doesn't, A-record apex support is coming soon;`));
9041
- console.log(pc.dim(` use a subdomain like app.${hostname} in the meantime.`));
9038
+ if (res.data.dns_note) {
9039
+ console.log(pc.yellow(` DNS requirement: ${res.data.dns_note}`));
9042
9040
  console.log();
9043
9041
  }
9044
9042
  if (options.noWait) {
@@ -9056,11 +9054,11 @@ async function sitesDomainAddCommand(hostname, options) {
9056
9054
  console.log(pc.dim(` ${line}`));
9057
9055
  lastLine = line;
9058
9056
  }
9059
- if (row.cert_status === "active" && row.ownership_status === "active") {
9057
+ if (row.live === true) {
9060
9058
  console.log(pc.green(" ✓") + ` ${hostname} is live with a valid certificate.`);
9061
9059
  return;
9062
9060
  }
9063
- if (row.cert_status === "error" || row.ownership_status === "error") throw new Error(`Domain setup failed for ${hostname}. Confirm the TXT record(s) above are published and the CNAME points at ${res.data.dns_target}.`);
9061
+ if (isProviderTerminalDomainFailure(row)) throw new Error(`Domain setup failed for ${hostname}. Confirm the TXT record(s) above are published and the CNAME points at ${res.data.dns_target}.`);
9064
9062
  await sleep(5e3);
9065
9063
  }
9066
9064
  console.log(pc.yellow(` ! Timed out after ${options.timeout ?? 600}s. Re-run \`amba sites describe ${options.site}\` to check status.`));
@@ -9193,12 +9191,18 @@ function formatCertStatus(s) {
9193
9191
  return pc.yellow(s);
9194
9192
  }
9195
9193
  /**
9196
- * Render BOTH lifecycle states for a domain row. A domain is only live when
9197
- * ownership AND cert are both `active`, so display paths must show both —
9198
- * showing cert alone reads "live" while ownership is still pending.
9194
+ * Render every serving plane. Only the server-derived `live` field is
9195
+ * authoritative; these states explain what is still converging.
9199
9196
  */
9200
9197
  function formatDomainStatus(d) {
9201
- return `ownership: ${formatCertStatus(d.ownership_status)} cert: ${formatCertStatus(d.cert_status)}`;
9198
+ const states = [`ownership: ${formatCertStatus(d.ownership_status)}`, `cert: ${formatCertStatus(d.cert_status)}`];
9199
+ if (d.route_status) states.push(`route: ${formatCertStatus(d.route_status)}`);
9200
+ if (d.kv_status) states.push(`kv: ${formatCertStatus(d.kv_status)}`);
9201
+ if (d.dns_managed && d.dns_status) states.push(`dns: ${formatCertStatus(d.dns_status)}`);
9202
+ return states.join(" ");
9203
+ }
9204
+ function isProviderTerminalDomainFailure(d) {
9205
+ return d.cert_status === "error" || d.ownership_status === "error";
9202
9206
  }
9203
9207
  function sleep(ms) {
9204
9208
  return new Promise((r) => setTimeout(r, ms));
@@ -9318,9 +9322,13 @@ async function domainsBuyCommand(domain, options) {
9318
9322
  }
9319
9323
  console.log(pc.green(" ✓") + ` Registered ${pc.bold(hostname)}`);
9320
9324
  if (r.provisioned) {
9321
- console.log(pc.green(" ✓") + ` Connected to ${options.site}${r.dns_autoconfigured ? " — no DNS setup needed" : ""}.`);
9322
- if (r.public_url) console.log(pc.green(" ✓") + ` Live at ${pc.underline(r.public_url)}`);
9323
- console.log(pc.dim(" The TLS certificate is issuing now (usually a minute or two)."));
9325
+ console.log(pc.green(" ✓") + ` Binding created for ${options.site}${r.dns_autoconfigured ? " — no DNS setup needed" : ""}.`);
9326
+ const hostnameLive = r.hostname_live === true;
9327
+ if (r.public_url && hostnameLive) console.log(pc.green(" ✓") + ` Live at ${pc.underline(r.public_url)}`);
9328
+ else if (r.public_url) {
9329
+ console.log(pc.yellow(" !") + ` Pending activation at ${pc.underline(r.public_url)}`);
9330
+ console.log(pc.dim(` certificate=${r.cert_status ?? "pending"}, ownership=${r.ownership_status ?? "pending"}, route=${r.route_status ?? "pending"}, kv=${r.kv_status ?? "pending"}, dns=${r.dns_status ?? "pending"}; wait for hostname_live=true.`));
9331
+ }
9324
9332
  } else if (r.message) console.log(pc.dim(` ${r.message}`));
9325
9333
  console.log();
9326
9334
  }
@@ -9687,7 +9695,7 @@ async function diffConfigCommand(file = DEFAULT_BUNDLE_FILE, options = {}) {
9687
9695
  //#endregion
9688
9696
  //#region src/index.ts
9689
9697
  const program = new Command();
9690
- program.name("amba").description("amba — agent-native backend-as-a-service for mobile apps.").version("4.1.3");
9698
+ program.name("amba").description("amba — agent-native backend-as-a-service for mobile apps.").version("4.1.4");
9691
9699
  program.option("--token <pat>", "Use a Personal Access Token for headless / CI / agent use (overrides ~/.amba/credentials.json + AMBA_PAT env)");
9692
9700
  program.hook("preAction", (thisCommand) => {
9693
9701
  setBearerOverride(resolveTokenSource({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layers/amba",
3
- "version": "4.1.3",
3
+ "version": "4.1.4",
4
4
  "description": "amba — agent-native backend-as-a-service. Functions, collections, storage, AI, email, queues, sites, and `amba ship` to take an Expo app live on the App Store + Google Play. `npx @layers/amba init` to start.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -49,8 +49,8 @@
49
49
  "tsdown": "^0.12.5",
50
50
  "typescript": "^5.8.3",
51
51
  "vitest": "^3.2.4",
52
- "@layers/amba-shared": "4.0.4",
53
- "@layers/amba-mcp": "4.0.7"
52
+ "@layers/amba-mcp": "4.0.8",
53
+ "@layers/amba-shared": "4.0.4"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsdown && tsc --emitDeclarationOnly",
@@ -40,6 +40,11 @@ Run user code in a sandbox triggered by HTTP, cron, or webhook. The function get
40
40
  | `amba_function_domains_attach` | Attach an exact hostname to a function and return DNS validation records. | `{ project_id, name: "feed", hostname: "feeds.example.com" }` |
41
41
  | `amba_function_domains_list` / `amba_function_domains_refresh` | Read or refresh ownership/certificate status. | `{ project_id, name: "feed", hostname: "feeds.example.com" }` |
42
42
  | `amba_function_domains_remove` | Detach an exact function hostname. | `{ project_id, name: "feed", hostname: "feeds.example.com" }` |
43
+
44
+ For every custom hostname, publish a real provider-stored CNAME to the returned target and heed
45
+ the unconditional `dns_note`. Do not substitute ALIAS, ANAME, or copied A/AAAA addresses. Treat Site and Function hostnames
46
+ as ready only when `live=true` (certificate and hostname activation both active), not from a
47
+ successful TLS handshake or `cert_status=active` alone.
43
48
  | `amba_functions_list` / `amba_list_functions` | List functions. | `{ project_id }` |
44
49
  | `amba_functions_get` / `amba_get_function` | Read function metadata. | `{ project_id, function_id }` |
45
50
  | `amba_functions_get_logs` / `amba_get_function_logs` | Recent invocation logs. | `{ project_id, function_id, limit: 100 }` |