@neta-art/cohub-cli 10.11.0 → 10.12.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.
package/README.md CHANGED
@@ -496,6 +496,15 @@ cohub apps versions <appId> --json
496
496
  cohub apps rm <appId> --yes
497
497
  ```
498
498
 
499
+ Bind a custom domain to a published public App. The binding costs a one-time $5, charged when the domain goes live; `add` prints the CNAME and TXT records to create at the DNS provider:
500
+
501
+ ```bash
502
+ cohub apps domains add <app> app.example.com --yes
503
+ cohub apps domains ls <app>
504
+ cohub apps domains check <app> app.example.com
505
+ cohub apps domains rm <app> app.example.com --yes
506
+ ```
507
+
499
508
  Resolve a published App by public identity:
500
509
 
501
510
  ```bash
@@ -9401,6 +9401,7 @@ function isBoardModelItem(item) {
9401
9401
  }
9402
9402
  //#endregion
9403
9403
  //#region ../sdk/src/board/model/asset-key.ts
9404
+ const COVER = "cover:";
9404
9405
  function imageAssetKey(item) {
9405
9406
  if (item.type === "image") {
9406
9407
  const source = parseBoardMediaSource$1(item.props.src);
@@ -9421,8 +9422,7 @@ function imageAssetKey(item) {
9421
9422
  return source ? `${source.kind}:${source.value}` : null;
9422
9423
  }
9423
9424
  const STILL_MEDIA = ["video", "model"];
9424
- function stillKey(media, src, mtimeMs) {
9425
- const source = parseBoardMediaSource$1(src);
9425
+ function stillKey(media, source, mtimeMs) {
9426
9426
  if (!source) return null;
9427
9427
  return source.kind === "url" ? `${media}-url:${encodeURIComponent(source.value)}` : `${media}:${encodeURIComponent(source.value)}:${mtimeMs ?? "unknown"}`;
9428
9428
  }
@@ -9436,8 +9436,8 @@ function taskPreviewKey(item) {
9436
9436
  }
9437
9437
  function boardAssetKey(item) {
9438
9438
  if (item.type === "task") return taskPreviewKey(item);
9439
- if (item.type === "video") return stillKey("video", item.props.src, item.props.snapshot?.mtimeMs);
9440
- if (item.type === "file" && isBoardModelItem(item)) return stillKey("model", item.props.src, item.props.snapshot?.mtimeMs);
9439
+ if (item.type === "video") return stillKey("video", parseBoardMediaSource$1(item.props.src), item.props.snapshot?.mtimeMs);
9440
+ if (item.type === "file" && isBoardModelItem(item)) return stillKey("model", parseBoardMediaSource$1(item.props.src), item.props.snapshot?.mtimeMs);
9441
9441
  return imageAssetKey(item);
9442
9442
  }
9443
9443
  function boardAssetKeySource(key) {
@@ -9447,6 +9447,14 @@ function boardAssetKeySource(key) {
9447
9447
  if (separator < 0) return null;
9448
9448
  base = key.slice(separator + 1);
9449
9449
  }
9450
+ if (!base.startsWith(COVER)) return sourceOf(base);
9451
+ const source = sourceOf(base.slice(6));
9452
+ return source?.media === "image" ? {
9453
+ ...source,
9454
+ fit: "cover"
9455
+ } : null;
9456
+ }
9457
+ function sourceOf(base) {
9450
9458
  try {
9451
9459
  for (const media of STILL_MEDIA) {
9452
9460
  if (base.startsWith(`${media}-url:`)) return {
@@ -9476,12 +9484,6 @@ function boardAssetKeySource(key) {
9476
9484
  media: "image",
9477
9485
  value: base.slice(4)
9478
9486
  };
9479
- if (base.startsWith("cover-url:")) return {
9480
- kind: "url",
9481
- media: "image",
9482
- value: base.slice(10),
9483
- fit: "cover"
9484
- };
9485
9487
  return null;
9486
9488
  }
9487
9489
  function boardTexturePixels(item, scale) {
@@ -10106,12 +10108,12 @@ const BOARD_THEME_DATA = {
10106
10108
  colorScheme: "dark",
10107
10109
  palette: {
10108
10110
  bg: 329221,
10109
- surface: 2039072,
10110
- hover: 1842202,
10111
+ surface: 1249814,
10112
+ hover: 1973788,
10111
10113
  border: 1842202,
10112
10114
  brand: 3900150,
10113
- text: 15920612,
10114
- muted: 9999759,
10115
+ text: 16314853,
10116
+ muted: 7630695,
10115
10117
  rare: 9235170,
10116
10118
  epic: 7266776,
10117
10119
  legendary: 15449187
@@ -10120,47 +10122,47 @@ const BOARD_THEME_DATA = {
10120
10122
  brand: {
10121
10123
  stroke: 15524306,
10122
10124
  fill: 15524306,
10123
- label: 15920612
10125
+ label: 16314853
10124
10126
  },
10125
10127
  neutral: {
10126
- stroke: 15920612,
10127
- fill: 15920612,
10128
- label: 15920612
10128
+ stroke: 15524306,
10129
+ fill: 15524306,
10130
+ label: 16314853
10129
10131
  },
10130
10132
  black: {
10131
10133
  stroke: 0,
10132
10134
  fill: 0,
10133
- label: 15920612
10135
+ label: 16314853
10134
10136
  },
10135
10137
  white: {
10136
10138
  stroke: 16777215,
10137
10139
  fill: 16777215,
10138
- label: 15920612
10140
+ label: 16314853
10139
10141
  },
10140
10142
  blue: {
10141
10143
  stroke: 9235170,
10142
10144
  fill: 9235170,
10143
- label: 15920612
10145
+ label: 16314853
10144
10146
  },
10145
10147
  green: {
10146
10148
  stroke: 8177036,
10147
10149
  fill: 8177036,
10148
- label: 15920612
10150
+ label: 16314853
10149
10151
  },
10150
10152
  amber: {
10151
10153
  stroke: 15449187,
10152
10154
  fill: 15449187,
10153
- label: 15920612
10155
+ label: 16314853
10154
10156
  },
10155
10157
  violet: {
10156
10158
  stroke: 11971327,
10157
10159
  fill: 11971327,
10158
- label: 15920612
10160
+ label: 16314853
10159
10161
  },
10160
10162
  rose: {
10161
10163
  stroke: 15562341,
10162
10164
  fill: 15562341,
10163
- label: 15920612
10165
+ label: 16314853
10164
10166
  }
10165
10167
  },
10166
10168
  style: {
@@ -10173,8 +10175,8 @@ const BOARD_THEME_DATA = {
10173
10175
  palette: {
10174
10176
  bg: 16185078,
10175
10177
  surface: 16777215,
10176
- hover: 15461355,
10177
- border: 14935011,
10178
+ hover: 16185078,
10179
+ border: 15527149,
10178
10180
  brand: 1824230,
10179
10181
  text: 1447704,
10180
10182
  muted: 7566452,
@@ -10255,13 +10257,13 @@ const BOARD_THEME_DATA = {
10255
10257
  "neta-pixel-dark": {
10256
10258
  colorScheme: "dark",
10257
10259
  palette: {
10258
- bg: 1447704,
10259
- surface: 2631979,
10260
- hover: 3224115,
10261
- border: 3618875,
10260
+ bg: 1118739,
10261
+ surface: 1842463,
10262
+ hover: 2237220,
10263
+ border: 2763565,
10262
10264
  brand: 1824230,
10263
- text: 15921907,
10264
- muted: 9408656,
10265
+ text: 16185078,
10266
+ muted: 9079436,
10265
10267
  rare: 1824230,
10266
10268
  epic: 1824230,
10267
10269
  legendary: 16766287
@@ -10270,47 +10272,47 @@ const BOARD_THEME_DATA = {
10270
10272
  brand: {
10271
10273
  stroke: 16185078,
10272
10274
  fill: 16185078,
10273
- label: 15921907
10275
+ label: 16185078
10274
10276
  },
10275
10277
  neutral: {
10276
- stroke: 9408656,
10277
- fill: 9408656,
10278
- label: 15921907
10278
+ stroke: 9079436,
10279
+ fill: 9079436,
10280
+ label: 16185078
10279
10281
  },
10280
10282
  black: {
10281
10283
  stroke: 0,
10282
10284
  fill: 0,
10283
- label: 15921907
10285
+ label: 16185078
10284
10286
  },
10285
10287
  white: {
10286
10288
  stroke: 16777215,
10287
10289
  fill: 16777215,
10288
- label: 15921907
10290
+ label: 16185078
10289
10291
  },
10290
10292
  blue: {
10291
10293
  stroke: 6010853,
10292
10294
  fill: 6010853,
10293
- label: 15921907
10295
+ label: 16185078
10294
10296
  },
10295
10297
  green: {
10296
10298
  stroke: 9683613,
10297
10299
  fill: 9683613,
10298
- label: 15921907
10300
+ label: 16185078
10299
10301
  },
10300
10302
  amber: {
10301
10303
  stroke: 14264410,
10302
10304
  fill: 14264410,
10303
- label: 15921907
10305
+ label: 16185078
10304
10306
  },
10305
10307
  violet: {
10306
10308
  stroke: 11441117,
10307
10309
  fill: 11441117,
10308
- label: 15921907
10310
+ label: 16185078
10309
10311
  },
10310
10312
  rose: {
10311
10313
  stroke: 15043210,
10312
10314
  fill: 15043210,
10313
- label: 15921907
10315
+ label: 16185078
10314
10316
  }
10315
10317
  },
10316
10318
  style: {
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerAppDomains(appsCmd: Command): void;
@@ -0,0 +1,133 @@
1
+ import { normalizeAppDomainHostname } from "@neta-art/cohub";
2
+ import { createClient } from "../client.js";
3
+ import { getAppByRef } from "../app-ref.js";
4
+ import { error, handleHttp, json as outJson, jsonRequested, ok, table } from "../output.js";
5
+ async function confirm(question, opts, hint) {
6
+ if (opts.yes)
7
+ return;
8
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
9
+ return error("Confirmation required", hint);
10
+ process.stdout.write(`${question} [y/N] `);
11
+ const chunks = [];
12
+ for await (const chunk of process.stdin) {
13
+ chunks.push(chunk);
14
+ break;
15
+ }
16
+ const answer = Buffer.concat(chunks).toString().trim().toLowerCase();
17
+ if (answer !== "y" && answer !== "yes")
18
+ return error("Cancelled");
19
+ }
20
+ async function findDomain(client, appId, ref) {
21
+ const { domains } = await client.apps.listDomains(appId);
22
+ const target = normalizeAppDomainHostname(ref) ?? ref.trim().toLowerCase();
23
+ const domain = domains.find((item) => item.id === ref || item.hostname === target);
24
+ return domain ?? error(`Domain not found: ${ref}`, "Run `cohub apps domains ls <app>` to see the App's domains.");
25
+ }
26
+ const info = (message) => console.log(message);
27
+ function printDomain(domain) {
28
+ table([{
29
+ hostname: domain.hostname,
30
+ status: domain.status,
31
+ certificate: domain.certificateStatus ?? "-",
32
+ error: domain.error?.message ?? "",
33
+ }], [
34
+ { key: "hostname", label: "Hostname" },
35
+ { key: "status", label: "Status" },
36
+ { key: "certificate", label: "Certificate" },
37
+ { key: "error", label: "Error" },
38
+ ]);
39
+ if (domain.status === "active")
40
+ return;
41
+ info("Add these DNS records at your DNS provider:");
42
+ table(domain.dnsRecords.map((record) => ({
43
+ ...record,
44
+ state: record.satisfied === true ? "ok" : record.satisfied === false ? "missing" : "-",
45
+ })), [
46
+ { key: "type", label: "Type" },
47
+ { key: "name", label: "Name" },
48
+ { key: "value", label: "Value" },
49
+ { key: "state", label: "State" },
50
+ ]);
51
+ }
52
+ export function registerAppDomains(appsCmd) {
53
+ const domainsCmd = appsCmd.command("domains").description("Custom domains for published Apps");
54
+ domainsCmd
55
+ .command("list <app>")
56
+ .alias("ls")
57
+ .description("List the App's custom domains")
58
+ .option("--json", "Output as JSON")
59
+ .action(async (appRef, opts) => {
60
+ const client = createClient();
61
+ try {
62
+ const detail = await getAppByRef(client, appRef);
63
+ const result = await client.apps.listDomains(detail.app.id);
64
+ if (jsonRequested(opts))
65
+ return outJson(result);
66
+ if (!result.enabled && result.domains.length === 0)
67
+ return info("Custom domains are not enabled on this deployment.");
68
+ if (result.domains.length === 0)
69
+ return info(`No custom domain. Add one with \`cohub apps domains add ${appRef} <hostname>\` ($${result.priceUsd}, one-time).`);
70
+ result.domains.forEach(printDomain);
71
+ }
72
+ catch (e) {
73
+ handleHttp(e);
74
+ }
75
+ });
76
+ domainsCmd
77
+ .command("add <app> <hostname>")
78
+ .description("Bind a custom domain; charged once when it goes live")
79
+ .option("-y, --yes", "Confirm the one-time charge")
80
+ .option("--json", "Output as JSON")
81
+ .action(async (appRef, hostname, opts) => {
82
+ const client = createClient();
83
+ try {
84
+ const detail = await getAppByRef(client, appRef);
85
+ const { priceUsd } = await client.apps.listDomains(detail.app.id);
86
+ await confirm(`Binding ${hostname} costs $${priceUsd}, charged once when the domain goes live. Continue?`, opts, `Pass --yes to accept the one-time $${priceUsd} charge.`);
87
+ const { domain } = await client.apps.addDomain(detail.app.id, hostname);
88
+ if (jsonRequested(opts))
89
+ return outJson(domain);
90
+ ok(`Domain added: ${domain.hostname}`);
91
+ printDomain(domain);
92
+ }
93
+ catch (e) {
94
+ handleHttp(e);
95
+ }
96
+ });
97
+ domainsCmd
98
+ .command("check <app> <domain>")
99
+ .description("Check DNS and certificate status now")
100
+ .option("--json", "Output as JSON")
101
+ .action(async (appRef, domainRef, opts) => {
102
+ const client = createClient();
103
+ try {
104
+ const detail = await getAppByRef(client, appRef);
105
+ const target = await findDomain(client, detail.app.id, domainRef);
106
+ const { domain } = await client.apps.checkDomain(detail.app.id, target.id);
107
+ if (jsonRequested(opts))
108
+ return outJson(domain);
109
+ printDomain(domain);
110
+ }
111
+ catch (e) {
112
+ handleHttp(e);
113
+ }
114
+ });
115
+ domainsCmd
116
+ .command("rm <app> <domain>")
117
+ .alias("remove")
118
+ .description("Remove a custom domain")
119
+ .option("-y, --yes", "Confirm removal")
120
+ .action(async (appRef, domainRef, opts) => {
121
+ const client = createClient();
122
+ try {
123
+ const detail = await getAppByRef(client, appRef);
124
+ const target = await findDomain(client, detail.app.id, domainRef);
125
+ await confirm(`Remove ${target.hostname}? The one-time charge is not refunded.`, opts, "Pass --yes to remove the domain.");
126
+ const result = await client.apps.removeDomain(detail.app.id, target.id);
127
+ ok(result.domain ? `Removing ${target.hostname}; its certificate is being released` : `Domain removed: ${target.hostname}`);
128
+ }
129
+ catch (e) {
130
+ handleHttp(e);
131
+ }
132
+ });
133
+ }
@@ -9,6 +9,7 @@ import { getAppByRef, parseAppRef } from "../app-ref.js";
9
9
  import { checkAppTarget } from "../app-target.js";
10
10
  import { putBytes, putLocalFile } from "../http-put.js";
11
11
  import { registerAppCommerce } from "./app-commerce.js";
12
+ import { registerAppDomains } from "./app-domains.js";
12
13
  import { collectPublicUpload } from "./public.js";
13
14
  const APP_STATUSES = ["published", "disabled"];
14
15
  const APP_VISIBILITIES = ["public", "space"];
@@ -722,6 +723,7 @@ export function registerApps(program) {
722
723
  }
723
724
  });
724
725
  registerAppCommerce(appsCmd);
726
+ registerAppDomains(appsCmd);
725
727
  // ── Viewer grants ─────────────────────────────────────────────────────────
726
728
  appsCmd
727
729
  .command("authorize <app>")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "10.11.0",
3
+ "version": "10.12.0",
4
4
  "description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -18,7 +18,7 @@
18
18
  "NOTICE"
19
19
  ],
20
20
  "dependencies": {
21
- "@neta-art/cohub": "12.3.0",
21
+ "@neta-art/cohub": "12.5.0",
22
22
  "@neta-art/generation": "^0.1.40",
23
23
  "commander": "^15.0.0",
24
24
  "sharp": "^0.35.4",