unoverse 0.1.156 → 0.1.158

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/bin/unoverse.mjs CHANGED
@@ -96,8 +96,11 @@ const HELP = [
96
96
  "",
97
97
  ` ${bold("After that")}`,
98
98
  row("studio", "Design components, nodes and agent skills"),
99
- row("publish", "Ship your work to your universe"),
100
- row("login", "Sign in to a universe (publish asks by itself when needed)"),
99
+ row("deploy", "Ship it"),
100
+ ` ${dim("deploy studio your components, nodes and skills your universe")}`,
101
+ ` ${dim("deploy aws your universe → AWS")}`,
102
+ ` ${dim("deploy digitalocean your universe → DigitalOcean")}`,
103
+ row("login", "Sign in to a universe (deploy asks by itself when needed)"),
101
104
  row("update", "Update this CLI"),
102
105
  "",
103
106
  ].join("\n");
@@ -230,19 +233,38 @@ switch (cmd) {
230
233
  break;
231
234
  }
232
235
 
236
+ case "deploy": {
237
+ // ONE VERB FOR THE REMOTE DEVELOPER, the target says what ships. `deploy studio`
238
+ // ships this design workspace's items to the signed-in universe (lint gates it);
239
+ // any other target is the operator's cloud deploy, unchanged. `publish` is the
240
+ // PLATFORM OWNER's word (the release) and stays out of this lane on purpose.
241
+ if (args[0] === "studio") {
242
+ const { findWorkspace } = await import("../lib/workspace.mjs");
243
+ const ws = findWorkspace();
244
+ if (ws?.kind !== "assets") {
245
+ console.log(`\n deploy studio ships a design workspace, and there is none above ${process.cwd()}.\n Start one with: unoverse create\n`);
246
+ process.exit(1);
247
+ }
248
+ const { publish } = await import("../lib/publish.mjs");
249
+ await publish(args.slice(1));
250
+ break;
251
+ }
252
+ if (UNIVERSE) operator(UNIVERSE, ["deploy", ...args]);
253
+ console.log(`\n '${["deploy", args[0]].filter(Boolean).join(" ")}' operates a universe, and this folder is not one.\n cd into your universe — or did you mean: unoverse deploy studio\n`);
254
+ process.exit(1);
255
+ }
256
+
233
257
  case "publish": {
234
- // ONE VERB, the folder decides (LOCAL_STUDIO.md §Publish is terminal-only).
235
- // Nearest marker up from cwd: a design home publish this workspace's items;
236
- // a docker-compose.yml publish is the operator's (the platform release lane).
258
+ // THE PLATFORM OWNER'S VERB. A remote developer never publishes: their word is
259
+ // `deploy studio`, and this says so instead of quietly doing it under the wrong
260
+ // name. In a universe checkout the operator's publish lane runs as before (on a
261
+ // starter kit that lane refuses by itself and names the owner's script).
237
262
  const { findWorkspace } = await import("../lib/workspace.mjs");
238
263
  const ws = findWorkspace();
239
- if (ws?.kind === "assets") {
240
- const { publish } = await import("../lib/publish.mjs");
241
- await publish(args);
242
- } else if (ws?.kind === "universe") {
264
+ if (ws?.kind === "universe") {
243
265
  operator({ root: ws.root, script: OPERATOR }, ["publish", ...args]);
244
266
  } else {
245
- console.log(`\n Nothing to publish here: no design/ workspace and no universe above ${process.cwd()}.\n Start one with: unoverse create\n`);
267
+ console.log(`\n publish is the platform owner's release. To ship your work: unoverse deploy studio\n`);
246
268
  process.exit(1);
247
269
  }
248
270
  break;
package/lib/publish.mjs CHANGED
@@ -86,17 +86,17 @@ export async function publish(args) {
86
86
  let universe = opt("to") ?? config.universe;
87
87
  if (!universe) {
88
88
  if (!process.stdin.isTTY) die("no universe. Pass --to https://your-universe.example, or commit it in unoverse.yaml");
89
- const typed = await ask(`\n Where does this workspace publish to? ${c.dim}(e.g. universe.example.com)${c.off} `);
89
+ const typed = await ask(`\n Where does this workspace deploy to? ${c.dim}(e.g. universe.example.com)${c.off} `);
90
90
  if (!typed) die("no universe given");
91
91
  universe = normaliseUrl(typed);
92
92
  await writeConfig(ws.root, { universe });
93
- console.log(` ${c.green}✓${c.off} saved to unoverse.yaml ${c.dim}(commit it: the whole team publishes there)${c.off}`);
93
+ console.log(` ${c.green}✓${c.off} saved to unoverse.yaml ${c.dim}(commit it: the whole team deploys there)${c.off}`);
94
94
  } else {
95
95
  universe = normaliseUrl(universe);
96
96
  }
97
97
 
98
98
  // ── 1. lint. Nothing is sent if this fails ──────────────────────────────────
99
- console.log(`\n ${c.bold}publish ${project}${c.off} ${c.dim}→ ${universe}${c.off}\n`);
99
+ console.log(`\n ${c.bold}deploy ${project}${c.off} ${c.dim}→ ${universe}${c.off}\n`);
100
100
  process.stdout.write(` ${c.dim}checking…${c.off}`);
101
101
  const { problems, errors } = await lintForPublish(designRoot, project);
102
102
  process.stdout.write("\r \r");
@@ -148,7 +148,7 @@ export async function publish(args) {
148
148
 
149
149
  // ── 4. confirm, unless told not to ──────────────────────────────────────────
150
150
  if (!flag("yes")) {
151
- const answer = await ask(`\n ${toSend} item(s) → ${universe}. Publish? [y/N] `);
151
+ const answer = await ask(`\n ${toSend} item(s) → ${universe}. Deploy? [y/N] `);
152
152
  if (!/^y(es)?$/i.test(answer)) {
153
153
  console.log(` ${c.dim}cancelled${c.off}\n`);
154
154
  process.exit(0);
@@ -161,7 +161,7 @@ export async function publish(args) {
161
161
  for (const s of sent) console.log(` ${c.green}✓${c.off} ${s.mode ?? "sent"} ${c.dim}${s.kind}/${s.name}${c.off}`);
162
162
  for (const f of failed) console.log(` ${c.red}✗${c.off} ${f.kind}/${f.name} ${f.why}`);
163
163
  console.log(
164
- `\n ${failed.length ? c.red + "✗" : c.green + "✓"}${c.off} ${sent.length} published` +
164
+ `\n ${failed.length ? c.red + "✗" : c.green + "✓"}${c.off} ${sent.length} deployed` +
165
165
  (failed.length ? `, ${failed.length} failed` : "") + "\n",
166
166
  );
167
167
  process.exit(failed.length ? 1 : 0);
@@ -605,7 +605,13 @@ resource "aws_cognito_user_pool_client" "spa" {
605
605
  # canvas_entry is the same expression the invitation email and the canvas_url output use,
606
606
  # so the address a person is sent to is by construction an address they may return to.
607
607
  # Anything else in the variable (localhost for dev, a second front end) is kept.
608
- callback_urls = distinct(concat([local.canvas_entry, local.studio_entry], var.oauth_callback_urls))
608
+ #
609
+ # THE CLI'S LOOPBACK IS ALWAYS ALLOWED, same rule as the app's own address: every
610
+ # universe is published to by `unoverse login` + `unoverse publish`, and its fixed
611
+ # callback is a platform constant, not an operator decision. Baked here so no tfvars
612
+ # ever needs it and an updated ground picks it up on the next deploy.
613
+ # (Callback only, not logout: the CLI never runs a logout redirect.)
614
+ callback_urls = distinct(concat([local.canvas_entry, local.studio_entry, "http://127.0.0.1:4109/callback"], var.oauth_callback_urls))
609
615
  logout_urls = distinct(concat([local.canvas_entry, local.studio_entry], var.oauth_callback_urls))
610
616
  supported_identity_providers = ["COGNITO"]
611
617
  }
@@ -29,6 +29,9 @@ canvas_public = true
29
29
  auth_issuer = "https://your-tenant.auth0.com"
30
30
  auth_client_id = "your-spa-client-id"
31
31
  auth_audience = "gravity-api"
32
+ # In that client's Allowed Callback URLs, include http://127.0.0.1:4109/callback —
33
+ # it is how `unoverse login` signs developers in from the terminal. (On AWS this is
34
+ # automatic: the ground owns Cognito and always allows it.)
32
35
 
33
36
  # Service secrets — with these set, the rendered .env.production is COMPLETE.
34
37
  docr_token = "dop_v1_..." # registry token (pulls platform images)
@@ -313,8 +313,8 @@ openai_api_key = "FILL_ME"
313
313
  oauth_callback_urls = [
314
314
  $( if [ -n "$zone_domain" ]; then echo " \"https://canvas.$zone_domain\","; fi )
315
315
  "http://localhost:5173", # local Studio/Canvas dev against this universe
316
- "http://127.0.0.1:4109/callback", # unoverse login (the CLI's loopback; IdPs match exactly)
317
316
  # Add your Canvas URL here once a domain is set (https://canvas.<domain>).
317
+ # (No need to list the CLI's login callback — main.tf always allows it.)
318
318
  ]
319
319
 
320
320
  # RBAC roles (noun:verb). Each becomes a Cognito group; put a user in the group
@@ -3,8 +3,10 @@
3
3
  #
4
4
  # Printed by the npm CLI when you are standing in a universe folder. It lists what you
5
5
  # can do to a universe, and nothing else. Commands that still exist but are not for
6
- # day-to-day operating (ground, dev, build, publish) are deliberately absent: a list you
7
- # can read in one glance is worth more than a complete one.
6
+ # day-to-day operating (ground, dev, build) are deliberately absent: a list you can read
7
+ # in one glance is worth more than a complete one. `publish` is listed under Anywhere:
8
+ # it is one verb whose lane the folder decides (a design workspace publishes assets, the
9
+ # platform monorepo root releases the platform — LOCAL_STUDIO.md §Publish is terminal-only).
8
10
 
9
11
  cmd_help() {
10
12
 
@@ -14,6 +16,8 @@ cmd_help() {
14
16
  echo -e " ${BOLD}Anywhere${NC}"
15
17
  echo -e " ${GREEN}create${NC} Start something new, here"
16
18
  echo -e " ${GREEN}studio${NC} Author components, nodes, agent skills"
19
+ echo -e " ${GREEN}publish${NC} Ship your work ${DIM}(run it in your design workspace; signs you in if needed)${NC}"
20
+ echo -e " ${GREEN}login${NC} Sign in to a universe ${DIM}(publish asks by itself; this is for up front)${NC}"
17
21
  echo -e " ${GREEN}where${NC} Links to your universe ${DIM}(Canvas, API), checked live${NC}"
18
22
  echo -e " ${GREEN}update${NC} Update this CLI"
19
23
  echo ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unoverse",
3
- "version": "0.1.156",
3
+ "version": "0.1.158",
4
4
  "description": "The Unoverse front door — create a Studio project, a universe, or a client app, and launch Studio.",
5
5
  "license": "SEE LICENSE IN README.md",
6
6
  "type": "module",