unoverse 0.1.28 → 0.1.30

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
@@ -1,19 +1,35 @@
1
1
  # unoverse
2
2
 
3
- The Unoverse front door.
3
+ The Unoverse front door. Build agent-powered apps: interfaces as data, workflows
4
+ on a canvas, agent skills in plain markdown.
4
5
 
5
6
  ```bash
6
- npm create unoverse@latest # what are you building?
7
+ npm install -g unoverse
8
+ unoverse create # asks what you're building, then sets it up
7
9
  ```
8
10
 
9
- - **A Studio project** — author components, templates, nodes, and agent skills.
10
- Most people start (and stay) here. Launches [Unoverse Studio](https://www.npmjs.com/package/@unoverse-platform/studio).
11
- - **A universe** — run the full platform on your own infrastructure. For
12
- operators: requires a registry access token from your Unoverse admin.
13
- - **A client app** — a website or embed talking to an existing universe.
11
+ - **Studio** — author components, templates, nodes, and agent skills. Most
12
+ people start (and stay) here.
13
+ - **Universe** — run the platform yourself, on your own infrastructure. Needs a
14
+ registry access token from your Unoverse admin.
15
+ - **Client** — a client accelerator that talks to unoverse.
14
16
 
15
- Already have a universe? Its operator CLI lives inside it: `cd <universe> && ./unoverse`.
17
+ Standing in a universe folder, the same command operates it:
16
18
 
17
19
  ```bash
18
- unoverse studio # launch Studio any time
20
+ unoverse start # run it (--pull for the latest images)
21
+ unoverse check # is it healthy
22
+ unoverse logs # what is it doing
23
+ unoverse where # its addresses, local and deployed, probed live
24
+ unoverse deploy # ship it to your server
25
+ unoverse stop
19
26
  ```
27
+
28
+ ## Build with Claude
29
+
30
+ Creating a universe also installs a Claude Code skill (`.claude/skills/unoverse-create`)
31
+ and registers the `canvas` MCP (`.mcp.json`). Open the folder in Claude Code and ask for
32
+ what you want: components, templates, custom nodes, agent skills, or whole workflows
33
+ built live on your Canvas. `unoverse update` keeps the CLI and the skill current.
34
+
35
+ Documentation: https://github.com/unoverse-platform/docs
package/lib/create.mjs CHANGED
@@ -214,6 +214,22 @@ export async function create(nameArg) {
214
214
  }
215
215
 
216
216
  if (choice === "2") {
217
+ // ALREADY A UNIVERSE? Then this is a re-run, not a conflict. The first create
218
+ // scaffolds the files; if setup was interrupted (Ctrl-C at any question), running
219
+ // create again used to refuse with "this folder is not empty" — which reads as
220
+ // broken when the folder holds exactly what create itself put there. A universe is
221
+ // recognized by its compose file; resume setup instead.
222
+ const target = nameArg || ".";
223
+ if (existsSync(`${target}/docker-compose.yml`)) {
224
+ console.log(`\n ${dim(`${target === "." ? "This folder" : `./${target}`} is already a universe. Picking setup back up.`)}\n`);
225
+ rl.close();
226
+ const here2 = dirname(fileURLToPath(import.meta.url));
227
+ const vend = resolve(here2, "../operator/operator.sh");
228
+ const op = existsSync(vend) ? vend : resolve(here2, "../../../scripts/operator.sh");
229
+ const r2 = spawnSync("bash", [op, "init"], { stdio: "inherit", cwd: target });
230
+ process.exit(r2.status ?? 0);
231
+ }
232
+
217
233
  // TARGET FIRST, credential second. This used to ask for the registry token, make a
218
234
  // network round-trip to validate it, and only then discover the folder was not
219
235
  // empty. Never ask for a credential you are about to throw away.
@@ -77,22 +77,19 @@ cmd_init() {
77
77
  echo ""
78
78
  fi
79
79
 
80
- # Check for existing .env
81
- if [ -f "$ROOT/.env" ]; then
82
- echo ""
83
- read -r -p " .env already exists. Overwrite? [y/N] " REPLY
84
- echo ""
85
- if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
86
- info "Keeping existing .env"
87
- cmd_login
88
- cmd_pull
89
- return
90
- fi
91
- fi
80
+ # RE-RUNNING EDITS. There is no "overwrite? [y/N]" gate any more: every existing value
81
+ # becomes its question's default, so Enter keeps a setting and typing replaces it.
82
+ # Walking through changes only what you change — which makes this the way to change
83
+ # one env var, not a destructive restart.
84
+ _env_cur() { grep "^$1=" "$ROOT/.env" 2>/dev/null | head -1 | cut -d= -f2-; }
92
85
 
93
86
  echo ""
94
87
  echo -e " ${BOLD}Configure your environment:${NC}"
95
- echo -e " ${DIM}(Press Enter to use defaults)${NC}"
88
+ if [ -f "$ROOT/.env" ]; then
89
+ echo -e " ${DIM}(Existing .env found. Enter keeps each current value)${NC}"
90
+ else
91
+ echo -e " ${DIM}(Press Enter to use defaults)${NC}"
92
+ fi
96
93
  echo ""
97
94
 
98
95
  # DOCR Token. `unoverse create` has already asked for this and VALIDATED it against
@@ -102,8 +99,15 @@ cmd_init() {
102
99
  DOCR_TOKEN="$UNOVERSE_DOCR_TOKEN"
103
100
  ok "Registry token carried over from create"
104
101
  else
102
+ local cur_token
103
+ cur_token=$(_env_cur DOCR_TOKEN)
105
104
  while true; do
106
- read -p " DOCR Token (from your Unoverse admin): " DOCR_TOKEN || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
105
+ if [ -n "$cur_token" ]; then
106
+ read -p " DOCR Token [keep current]: " DOCR_TOKEN || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
107
+ DOCR_TOKEN="${DOCR_TOKEN:-$cur_token}"
108
+ else
109
+ read -p " DOCR Token (from your Unoverse admin): " DOCR_TOKEN || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
110
+ fi
107
111
  if [[ "$DOCR_TOKEN" == dop_v1_* ]]; then
108
112
  break
109
113
  fi
@@ -114,7 +118,8 @@ cmd_init() {
114
118
  # A DEFAULT, because "from your admin" is meaningless when you are the admin. The
115
119
  # platform ships no database (docker-compose has no postgres), so this points at one
116
120
  # you run. Enter takes the conventional local one.
117
- DB_DEFAULT="postgres://postgres:postgres@localhost:5432/unoverse"
121
+ DB_DEFAULT=$(_env_cur DATABASE_URL)
122
+ DB_DEFAULT="${DB_DEFAULT:-postgres://postgres:postgres@localhost:5432/unoverse}"
118
123
  while true; do
119
124
  read -p " DATABASE_URL [${DB_DEFAULT}]: " DATABASE_URL || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
120
125
  DATABASE_URL="${DATABASE_URL:-$DB_DEFAULT}"
@@ -139,17 +144,27 @@ cmd_init() {
139
144
  fi
140
145
  fi
141
146
 
142
- # Redis
143
- read -p " REDIS_HOST [host.docker.internal]: " REDIS_HOST
144
- REDIS_HOST="${REDIS_HOST:-host.docker.internal}"
147
+ # Redis, current values as defaults
148
+ local rd
149
+ rd=$(_env_cur REDIS_HOST); rd="${rd:-host.docker.internal}"
150
+ read -p " REDIS_HOST [$rd]: " REDIS_HOST
151
+ REDIS_HOST="${REDIS_HOST:-$rd}"
145
152
 
146
- read -p " REDIS_PORT [6379]: " REDIS_PORT
147
- REDIS_PORT="${REDIS_PORT:-6379}"
153
+ rd=$(_env_cur REDIS_PORT); rd="${rd:-6379}"
154
+ read -p " REDIS_PORT [$rd]: " REDIS_PORT
155
+ REDIS_PORT="${REDIS_PORT:-$rd}"
148
156
 
149
- read -p " REDIS_PASSWORD (blank for none): " REDIS_PASSWORD
157
+ rd=$(_env_cur REDIS_PASSWORD)
158
+ if [ -n "$rd" ]; then
159
+ read -p " REDIS_PASSWORD [keep current]: " REDIS_PASSWORD
160
+ REDIS_PASSWORD="${REDIS_PASSWORD:-$rd}"
161
+ else
162
+ read -p " REDIS_PASSWORD (blank for none): " REDIS_PASSWORD
163
+ fi
150
164
 
151
- read -p " REDIS_TLS [false]: " REDIS_TLS
152
- REDIS_TLS="${REDIS_TLS:-false}"
165
+ rd=$(_env_cur REDIS_TLS); rd="${rd:-false}"
166
+ read -p " REDIS_TLS [$rd]: " REDIS_TLS
167
+ REDIS_TLS="${REDIS_TLS:-$rd}"
153
168
 
154
169
  # Auth (required — from admin)
155
170
  # ASK BEFORE DEMANDING. A developer trying the platform locally has no identity
@@ -160,13 +175,28 @@ cmd_init() {
160
175
  # rather than trusting this wizard: authConfig.ts refuses to start with auth off when
161
176
  # NODE_ENV=production. So answering "no" here cannot produce an unprotected deployment.
162
177
  echo ""
163
- read -r -p " Do you have an identity provider (Auth0/OIDC) to connect? [y/N] " HAS_IDP
178
+ local cur_auth idp_prompt
179
+ cur_auth=$(_env_cur AUTH_ENABLED)
180
+ idp_prompt="[y/N]"
181
+ [ "$cur_auth" = "true" ] && idp_prompt="[Y/n]"
182
+ read -r -p " Do you have an identity provider (Auth0/OIDC) to connect? $idp_prompt " HAS_IDP
164
183
  echo ""
184
+ if [ -z "$HAS_IDP" ] && [ "$cur_auth" = "true" ]; then HAS_IDP=y; fi
165
185
 
166
186
  if [[ "$HAS_IDP" =~ ^[Yy]$ ]]; then
167
187
  AUTH_ENABLED=true
188
+ local cur_iss cur_cid cur_aud
189
+ cur_iss=$(_env_cur AUTH_ISSUER)
190
+ cur_cid=$(_env_cur AUTH_CLIENT_ID)
191
+ cur_aud=$(_env_cur AUTH_AUDIENCE)
192
+
168
193
  while true; do
169
- read -p " AUTH_ISSUER (e.g. https://your-tenant.auth0.com): " AUTH_ISSUER || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
194
+ if [ -n "$cur_iss" ]; then
195
+ read -p " AUTH_ISSUER [$cur_iss]: " AUTH_ISSUER || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
196
+ AUTH_ISSUER="${AUTH_ISSUER:-$cur_iss}"
197
+ else
198
+ read -p " AUTH_ISSUER (e.g. https://your-tenant.auth0.com): " AUTH_ISSUER || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
199
+ fi
170
200
  if [ -n "$AUTH_ISSUER" ] && [[ "$AUTH_ISSUER" == https://* ]]; then
171
201
  break
172
202
  fi
@@ -174,15 +204,21 @@ cmd_init() {
174
204
  done
175
205
 
176
206
  while true; do
177
- read -p " AUTH_CLIENT_ID: " AUTH_CLIENT_ID || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
207
+ if [ -n "$cur_cid" ]; then
208
+ read -p " AUTH_CLIENT_ID [$cur_cid]: " AUTH_CLIENT_ID || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
209
+ AUTH_CLIENT_ID="${AUTH_CLIENT_ID:-$cur_cid}"
210
+ else
211
+ read -p " AUTH_CLIENT_ID: " AUTH_CLIENT_ID || { fail "no input (end of stream). Run unoverse init interactively"; exit 1; }
212
+ fi
178
213
  if [ -n "$AUTH_CLIENT_ID" ] && [[ "$AUTH_CLIENT_ID" != *"your-"* ]]; then
179
214
  break
180
215
  fi
181
216
  fail "AUTH_CLIENT_ID is required"
182
217
  done
183
218
 
184
- read -p " AUTH_AUDIENCE [gravity-api]: " AUTH_AUDIENCE
185
- AUTH_AUDIENCE="${AUTH_AUDIENCE:-gravity-api}"
219
+ cur_aud="${cur_aud:-gravity-api}"
220
+ read -p " AUTH_AUDIENCE [$cur_aud]: " AUTH_AUDIENCE
221
+ AUTH_AUDIENCE="${AUTH_AUDIENCE:-$cur_aud}"
186
222
  else
187
223
  AUTH_ENABLED=false
188
224
  AUTH_ISSUER=""
@@ -207,7 +243,14 @@ cmd_init() {
207
243
  ok "Redis namespace: ${BOLD}${REDIS_NAMESPACE}${NC}"
208
244
 
209
245
  # OpenAI (for Memory Server)
210
- read -p " OPENAI_API_KEY (for Memory Server, blank to skip): " OPENAI_API_KEY
246
+ local cur_oai
247
+ cur_oai=$(_env_cur OPENAI_API_KEY)
248
+ if [ -n "$cur_oai" ]; then
249
+ read -p " OPENAI_API_KEY [keep current]: " OPENAI_API_KEY
250
+ OPENAI_API_KEY="${OPENAI_API_KEY:-$cur_oai}"
251
+ else
252
+ read -p " OPENAI_API_KEY (for Memory Server, blank to skip): " OPENAI_API_KEY
253
+ fi
211
254
 
212
255
  # Write .env
213
256
  cat > "$ROOT/.env" << ENVEOF
@@ -67,21 +67,21 @@ material.
67
67
 
68
68
  ## Step 3 — Deploy loop (after the artifact is written)
69
69
 
70
- Create a client org with `./unoverse new org <name>` (folder structure + default token
71
- set). Components and templates have NO scaffold command author them from the references
72
- (mirror the closest existing artifact), and ALWAYS run `./unoverse lint` before deploying —
73
- it enforces the schema, token law, and state rules with doc-cited messages (0 errors
74
- required; justify any warning).
70
+ Components and templates have NO scaffold command author them from the references
71
+ (mirror the closest existing artifact). Validation lives in Studio: its publish step
72
+ lints every definition before anything leaves the machine (0 errors required, doc-cited
73
+ messages; justify any warning), and the conformance checklist is
74
+ [UNOVERSE_CONFORMANCE](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_CONFORMANCE.md).
75
75
 
76
76
  | Artifact | To see it live |
77
77
  |---|---|
78
- | Component / atom / template / style | `./unoverse lint`, then restart unoverse nodes SYNTHESIZE from definitions (no codegen); deploy with `unoverse deploy design` |
79
- | Existing component restyle/edit only | takes effect live (SDK reads `rx/` directly) |
80
- | Agent skill / prompt block | `docker compose restart unoverse` |
81
- | Node | `unoverse node lint`, then `unoverse node test <NodeType>`. No build: a node is YAML |
78
+ | Component / atom / template / style | Preview in Studio (`unoverse studio`), then publish from Studio the publish lint gates it |
79
+ | Existing component restyle/edit only | takes effect live (the SDK reads `rx/` directly) |
80
+ | Agent skill / prompt block | publish from Studio, same gate |
81
+ | Node | Studio's Nodes screen: Load sample Run. A node is YAML; running it is the only proof (no build) |
82
82
 
83
- Verify with `./unoverse check` (services, node catalog, bundles). Preview components in
84
- Studio: run `unoverse-studio` from the project folder, which opens on :4108.
83
+ Verify the platform with `unoverse check` (services, schema, environment). Studio opens
84
+ on :4108 (`unoverse studio` from the project folder).
85
85
 
86
86
  > In the platform monorepo (not the starter), the node docs live at
87
87
  > `packages/docs/nodes/`, the design journey at `packages/docs/design/`, and the dev loop
@@ -62,4 +62,4 @@ is invalid.
62
62
  ## Ship
63
63
 
64
64
  `docker compose restart unoverse` — the runtime rescans skills at boot. Verify the
65
- skill appears via the platform's skill listing (`./unoverse check` for overall health).
65
+ skill appears via the platform's skill listing (`unoverse check` for overall health).
@@ -22,7 +22,7 @@ apps/unoverse/rx/orgs/<org>/components/<name>/ # ORG tier (the client's own micr
22
22
  A **flat component** (simple card/chart) is just `<name>.json` + `root` — one face, no manifest,
23
23
  no folders. Structure is EARNED; start flat.
24
24
 
25
- ## The rules (lint enforces all of these — 0 errors required)
25
+ ## The rules (Studio's publish lint enforces all of these — 0 errors required)
26
26
 
27
27
  1. **Three homes for everything it shows** (AUTHORING §3):
28
28
  - static content → **hardcoded literals** in the layout (`value`, literal `items: []` on
@@ -147,6 +147,6 @@ component — its key nouns/verbs must appear in `whenToUse`'s first sentence.
147
147
 
148
148
  1. Study the closest exemplar; copy its folder shape.
149
149
  2. Write the envelope + manifest + layouts; put every shown thing in its ONE home.
150
- 3. `./unoverse lint` — 0 errors (it cites the doc for every rule).
150
+ 3. Publish from Studio its lint must report 0 errors (it cites the doc for every rule).
151
151
  4. Restart unoverse → Studio: mock (prop defaults + state picker + Inline/Focused
152
152
  toggle), then live. Debug: stream log → state inspector → definition; never guess.
@@ -119,14 +119,14 @@ package only for a new integration.
119
119
  ## Verify
120
120
 
121
121
  ```bash
122
- unoverse node lint
123
- unoverse node test <NodeType>
122
+ Studio → Nodes → your node (the catalog load reports schema errors)
123
+ Studio's Nodes screen (Load sample → Run)
124
124
  ```
125
125
 
126
126
  Both must pass before the node is done. Lint names the rule and the page behind it, so read
127
127
  the message rather than guessing.
128
128
 
129
- `node test` calls the real service and needs no platform running. It reads keys from `.env`
129
+ The Studio node runner calls the real service and needs no platform running. It reads keys from `.env`
130
130
  as `<CREDENTIAL>_<FIELD>` in upper snake case, so `openAICredential.apiKey` is
131
131
  `OPENAI_API_KEY`.
132
132
 
@@ -107,7 +107,7 @@ themes `unoverse://theme/<org>/<theme>`.
107
107
 
108
108
  ## Validate & ship
109
109
 
110
- 1. `./unoverse lint` — 0 errors (layout/name-sync rules, widths, reaction rules, tokens).
110
+ 1. Publish from Studio its lint must report 0 errors (layout/name-sync rules, widths, reaction rules, tokens).
111
111
  2. Restart unoverse — the node re-synthesizes.
112
112
  3. **See it**: Studio — layout pills on top (default first, stateOrder order), local
113
113
  states down the left, freely combinable; acting inside the preview transitions like
@@ -7,7 +7,7 @@ in the developer's browser as you build.
7
7
 
8
8
  ## Prerequisites (check before starting)
9
9
 
10
- 1. **The platform is running** (`./unoverse start`, or `npm run dev` in the platform
10
+ 1. **The platform is running** (`unoverse start`, or `npm run dev` in the platform
11
11
  repo). The builder listens on `localhost:4106` — local machine only, by design.
12
12
  2. **The MCP is connected**: `canvas` should show connected with 14 tools.
13
13
  If it shows failed, the platform wasn't up when the session started — reconnect
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unoverse",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
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",