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 +25 -9
- package/lib/create.mjs +16 -0
- package/operator/lib/init.sh +72 -29
- package/operator/skills/unoverse-create/SKILL.md +11 -11
- package/operator/skills/unoverse-create/references/agent-skill.md +1 -1
- package/operator/skills/unoverse-create/references/component.md +2 -2
- package/operator/skills/unoverse-create/references/node.md +3 -3
- package/operator/skills/unoverse-create/references/template.md +1 -1
- package/operator/skills/unoverse-create/references/workflow.md +1 -1
- package/package.json +1 -1
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
|
|
7
|
+
npm install -g unoverse
|
|
8
|
+
unoverse create # asks what you're building, then sets it up
|
|
7
9
|
```
|
|
8
10
|
|
|
9
|
-
- **
|
|
10
|
-
|
|
11
|
-
- **
|
|
12
|
-
|
|
13
|
-
- **
|
|
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
|
-
|
|
17
|
+
Standing in a universe folder, the same command operates it:
|
|
16
18
|
|
|
17
19
|
```bash
|
|
18
|
-
unoverse
|
|
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.
|
package/operator/lib/init.sh
CHANGED
|
@@ -77,22 +77,19 @@ cmd_init() {
|
|
|
77
77
|
echo ""
|
|
78
78
|
fi
|
|
79
79
|
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
144
|
-
REDIS_HOST="${
|
|
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
|
-
|
|
147
|
-
REDIS_PORT
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
REDIS_TLS
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
185
|
-
AUTH_AUDIENCE
|
|
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
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 |
|
|
79
|
-
| Existing component restyle/edit only | takes effect live (SDK reads `rx/` directly) |
|
|
80
|
-
| Agent skill / prompt block |
|
|
81
|
-
| Node |
|
|
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
|
|
84
|
-
|
|
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 (
|
|
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.
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
|
|
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.
|
|
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** (
|
|
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