unoverse 0.1.32 → 0.1.34
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 +22 -12
- package/operator/lib/init.sh +64 -13
- package/operator/lib/start.sh +20 -0
- package/operator/operator.sh +16 -0
- package/package.json +1 -1
package/bin/unoverse.mjs
CHANGED
|
@@ -103,26 +103,36 @@ switch (cmd) {
|
|
|
103
103
|
break;
|
|
104
104
|
|
|
105
105
|
case "update": {
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
106
|
+
// ONE update: the CLI from npm, then everything the CLI is responsible for keeping
|
|
107
|
+
// current — the repo-local authoring skill and, in a universe, the platform images.
|
|
108
|
+
// Those two steps run via `_postupdate` in the NEWLY installed binary, not this
|
|
109
|
+
// process: this file is being replaced mid-command, and copying the skill from the
|
|
110
|
+
// old install would pin it one release behind forever.
|
|
111
|
+
const r = spawnSync("npm", ["install", "-g", "unoverse@latest"], { stdio: "inherit" });
|
|
112
|
+
if (r.status === 0) {
|
|
113
|
+
const v = spawnSync("unoverse", ["-v"], { encoding: "utf8" }).stdout?.trim();
|
|
114
|
+
console.log(`\n ✓ unoverse is now ${v || "up to date"}`);
|
|
115
|
+
spawnSync("unoverse", ["_postupdate"], { stdio: "inherit" });
|
|
116
|
+
} else {
|
|
117
|
+
console.log(`\n ✗ global install failed (permissions?). Try:\n\n sudo npm install -g unoverse@latest\n npx unoverse@latest # or skip the global install entirely\n`);
|
|
118
|
+
}
|
|
119
|
+
process.exit(r.status ?? 0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case "_postupdate": {
|
|
123
|
+
// Runs AS the new version, in the developer's cwd. Outside a universe there is
|
|
124
|
+
// nothing to refresh, and silence is the right answer.
|
|
109
125
|
if (UNIVERSE) {
|
|
110
126
|
const skillSrc = join(dirname(fileURLToPath(import.meta.url)), "../operator/skills/unoverse-create");
|
|
111
127
|
if (existsSync(skillSrc)) {
|
|
112
128
|
const { cpSync, mkdirSync } = await import("node:fs");
|
|
113
129
|
mkdirSync(join(UNIVERSE.root, ".claude/skills"), { recursive: true });
|
|
114
130
|
cpSync(skillSrc, join(UNIVERSE.root, ".claude/skills/unoverse-create"), { recursive: true });
|
|
115
|
-
console.log(" ✓ authoring skill refreshed
|
|
131
|
+
console.log(" ✓ authoring skill refreshed");
|
|
116
132
|
}
|
|
133
|
+
operator(UNIVERSE, ["refresh-images"]);
|
|
117
134
|
}
|
|
118
|
-
|
|
119
|
-
if (r.status === 0) {
|
|
120
|
-
const v = spawnSync("unoverse", ["-v"], { encoding: "utf8" }).stdout?.trim();
|
|
121
|
-
console.log(`\n ✓ unoverse is now ${v || "up to date"}\n`);
|
|
122
|
-
} else {
|
|
123
|
-
console.log(`\n ✗ global install failed (permissions?). Try:\n\n sudo npm install -g unoverse@latest\n npx unoverse@latest # or skip the global install entirely\n`);
|
|
124
|
-
}
|
|
125
|
-
process.exit(r.status ?? 0);
|
|
135
|
+
process.exit(0);
|
|
126
136
|
}
|
|
127
137
|
|
|
128
138
|
// `urls` stays as an alias: it shipped in 0.1.4/0.1.5 and is in people's fingers.
|
package/operator/lib/init.sh
CHANGED
|
@@ -5,6 +5,49 @@
|
|
|
5
5
|
# (`npm i -g unoverse`); symlinking a per-project bash script into /usr/local/bin was the
|
|
6
6
|
# second CLI we set out to delete.
|
|
7
7
|
|
|
8
|
+
# ── The image download, as an experience ─────────────────────────────────────
|
|
9
|
+
#
|
|
10
|
+
# The pull starts the moment the token is accepted, so the minute spent answering
|
|
11
|
+
# questions is also the minute the images arrive. NOTHING writes to the terminal
|
|
12
|
+
# asynchronously — a progress bar racing a readline prompt corrupts what the developer
|
|
13
|
+
# is typing. The background half writes one line of STATE to a temp file; the wizard
|
|
14
|
+
# prints a status line synchronously between question groups, where output composes
|
|
15
|
+
# with input; the end of setup attaches with docker's own bars for whatever remains.
|
|
16
|
+
PULL_STATE=""
|
|
17
|
+
start_background_pull() {
|
|
18
|
+
PULL_STATE=$(mktemp)
|
|
19
|
+
(
|
|
20
|
+
imgs=$(docker compose -f "$ROOT/docker-compose.yml" config --images 2>/dev/null | sort -u)
|
|
21
|
+
total=$(printf '%s\n' "$imgs" | grep -c .)
|
|
22
|
+
n=0
|
|
23
|
+
for img in $imgs; do
|
|
24
|
+
short="${img##*/}"; short="${short%%:*}"
|
|
25
|
+
if docker image inspect "$img" >/dev/null 2>&1; then
|
|
26
|
+
n=$((n+1)); echo "$n $total ready $short" > "$PULL_STATE"; continue
|
|
27
|
+
fi
|
|
28
|
+
echo "$n $total pulling $short" > "$PULL_STATE"
|
|
29
|
+
docker pull "$img" >/dev/null 2>&1
|
|
30
|
+
n=$((n+1)); echo "$n $total pulled $short" > "$PULL_STATE"
|
|
31
|
+
done
|
|
32
|
+
echo "$total $total done -" > "$PULL_STATE"
|
|
33
|
+
) &
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# One dim line, printed only when the wizard is printing anyway. Never a redraw.
|
|
37
|
+
pull_status_line() {
|
|
38
|
+
[ -n "$PULL_STATE" ] && [ -s "$PULL_STATE" ] || return 0
|
|
39
|
+
local n total verb what
|
|
40
|
+
read -r n total verb what < "$PULL_STATE" 2>/dev/null || return 0
|
|
41
|
+
if [ "$verb" = "done" ]; then
|
|
42
|
+
echo -e " ${GREEN}⬇${NC} ${DIM}platform images: all $total downloaded${NC}"
|
|
43
|
+
elif [ "$verb" = "pulling" ]; then
|
|
44
|
+
echo -e " ${CYAN}⬇${NC} ${DIM}platform images: $n of $total · pulling $what…${NC}"
|
|
45
|
+
else
|
|
46
|
+
echo -e " ${CYAN}⬇${NC} ${DIM}platform images: $n of $total${NC}"
|
|
47
|
+
fi
|
|
48
|
+
echo ""
|
|
49
|
+
}
|
|
50
|
+
|
|
8
51
|
# First-run detection
|
|
9
52
|
check_first_run() {
|
|
10
53
|
if [ ! -f "$ROOT/.env" ]; then
|
|
@@ -115,6 +158,18 @@ cmd_init() {
|
|
|
115
158
|
done
|
|
116
159
|
fi
|
|
117
160
|
|
|
161
|
+
# THE DOWNLOAD STARTS NOW (see the block at the top of this file).
|
|
162
|
+
if [ "$DOCKER_OK" = "1" ]; then
|
|
163
|
+
if echo "$DOCR_TOKEN" | docker login "$DOCR_REGISTRY" -u "$DOCR_TOKEN" --password-stdin >/dev/null 2>&1; then
|
|
164
|
+
start_background_pull
|
|
165
|
+
echo ""
|
|
166
|
+
echo -e " ${CYAN}⬇${NC} ${DIM}Platform images are downloading in the background while you configure${NC}"
|
|
167
|
+
echo ""
|
|
168
|
+
else
|
|
169
|
+
warn "Registry login failed with this token. Images will not pull until it is fixed"
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
|
|
118
173
|
# A DEFAULT, because "from your admin" is meaningless when you are the admin. The
|
|
119
174
|
# platform ships no database (docker-compose has no postgres), so this points at one
|
|
120
175
|
# you run. Enter takes the conventional local one.
|
|
@@ -144,6 +199,7 @@ cmd_init() {
|
|
|
144
199
|
fi
|
|
145
200
|
fi
|
|
146
201
|
|
|
202
|
+
pull_status_line
|
|
147
203
|
# Redis, current values as defaults
|
|
148
204
|
local rd
|
|
149
205
|
rd=$(_env_cur REDIS_HOST); rd="${rd:-host.docker.internal}"
|
|
@@ -174,7 +230,7 @@ cmd_init() {
|
|
|
174
230
|
# a deployed universe ("there is no auth-off deployment"), and the platform enforces it
|
|
175
231
|
# rather than trusting this wizard: authConfig.ts refuses to start with auth off when
|
|
176
232
|
# NODE_ENV=production. So answering "no" here cannot produce an unprotected deployment.
|
|
177
|
-
|
|
233
|
+
pull_status_line
|
|
178
234
|
local cur_auth idp_prompt
|
|
179
235
|
cur_auth=$(_env_cur AUTH_ENABLED)
|
|
180
236
|
idp_prompt="[y/N]"
|
|
@@ -243,6 +299,7 @@ cmd_init() {
|
|
|
243
299
|
ok "Redis namespace: ${BOLD}${REDIS_NAMESPACE}${NC}"
|
|
244
300
|
|
|
245
301
|
# OpenAI (for Memory Server)
|
|
302
|
+
pull_status_line
|
|
246
303
|
local cur_oai
|
|
247
304
|
cur_oai=$(_env_cur OPENAI_API_KEY)
|
|
248
305
|
# NOT just the memory server: compose hands this to the unoverse service itself —
|
|
@@ -297,21 +354,15 @@ ENVEOF
|
|
|
297
354
|
|
|
298
355
|
ok ".env created"
|
|
299
356
|
|
|
300
|
-
#
|
|
301
|
-
#
|
|
357
|
+
# Attach to the background pull started when the token was accepted: layers already
|
|
358
|
+
# down show as complete, the rest stream docker's progress bars. Without a daemon the
|
|
359
|
+
# token is in .env, and `unoverse start` pulls on its first run.
|
|
302
360
|
if [ "$DOCKER_OK" = "1" ]; then
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if echo "$DOCR_TOKEN" | docker login "$DOCR_REGISTRY" -u "$DOCR_TOKEN" --password-stdin &>/dev/null; then
|
|
306
|
-
ok "Logged in to DOCR"
|
|
307
|
-
else
|
|
308
|
-
fail "DOCR login failed. Check your token"
|
|
309
|
-
exit 1
|
|
310
|
-
fi
|
|
311
|
-
cmd_pull
|
|
361
|
+
pull_missing_images
|
|
362
|
+
[ -n "$PULL_STATE" ] && rm -f "$PULL_STATE"
|
|
312
363
|
else
|
|
313
364
|
echo ""
|
|
314
|
-
info "Skipped the
|
|
365
|
+
info "Skipped the image download. ${BOLD}unoverse start${NC} does it once Docker is up"
|
|
315
366
|
fi
|
|
316
367
|
|
|
317
368
|
# Install to PATH
|
package/operator/lib/start.sh
CHANGED
|
@@ -33,6 +33,24 @@ check_docker_file_sharing() {
|
|
|
33
33
|
return 0
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
# Pull whatever images are missing, VISIBLY. docker's own per-layer progress bars are the
|
|
37
|
+
# loading indication — a developer staring at a silent screen while gigabytes download
|
|
38
|
+
# assumes the tool is hung. Exists because init used to call cmd_pull (deleted with
|
|
39
|
+
# update.sh), so nothing pulled and nothing printed, and start buried compose's pull
|
|
40
|
+
# inside a log file.
|
|
41
|
+
pull_missing_images() {
|
|
42
|
+
local img missing=""
|
|
43
|
+
for img in $(docker compose -f "$ROOT/docker-compose.yml" config --images 2>/dev/null | sort -u); do
|
|
44
|
+
docker image inspect "$img" >/dev/null 2>&1 || missing="$missing $img"
|
|
45
|
+
done
|
|
46
|
+
[ -n "$missing" ] || return 0
|
|
47
|
+
echo ""
|
|
48
|
+
info "Pulling platform images (first run — a few minutes, progress below)..."
|
|
49
|
+
echo ""
|
|
50
|
+
docker compose -f "$ROOT/docker-compose.yml" --env-file "$ROOT/.env" pull || { fail "Image pull failed. Check the registry token (re-run unoverse create to change it)"; exit 1; }
|
|
51
|
+
echo ""
|
|
52
|
+
}
|
|
53
|
+
|
|
36
54
|
cmd_start() {
|
|
37
55
|
# Login to registry if DOCR_TOKEN is set
|
|
38
56
|
local docr_token
|
|
@@ -44,6 +62,8 @@ cmd_start() {
|
|
|
44
62
|
# `start --pull` refreshes the images first. This is where refreshing a LOCAL universe
|
|
45
63
|
# lives now: `update` is the CLI's word and means only the CLI, wherever you type it.
|
|
46
64
|
# A server is refreshed by `unoverse deploy` from your machine, not on the box.
|
|
65
|
+
pull_missing_images
|
|
66
|
+
|
|
47
67
|
if [ "${1:-}" = "--pull" ]; then
|
|
48
68
|
info "Pulling the latest images..."
|
|
49
69
|
docker compose -f "$ROOT/docker-compose.yml" pull || { fail "Image pull failed"; exit 1; }
|
package/operator/operator.sh
CHANGED
|
@@ -64,6 +64,22 @@ case "${1:-}" in
|
|
|
64
64
|
check) cmd_check && cmd_db_verify && cmd_doctor ;;
|
|
65
65
|
dev) cmd_dev ;;
|
|
66
66
|
ground) shift; cmd_ground "$@" ;;
|
|
67
|
+
refresh-images)
|
|
68
|
+
# internal: `unoverse update` runs this after updating the CLI. Pull newer images
|
|
69
|
+
# if the registry has them, and recreate only what is already running — an update
|
|
70
|
+
# never boots a stopped universe.
|
|
71
|
+
if [ ! -f "$ROOT/.env" ]; then
|
|
72
|
+
: # unconfigured: nothing to refresh
|
|
73
|
+
elif ! docker info >/dev/null 2>&1; then
|
|
74
|
+
info "Docker is not running. Images refresh on the next unoverse start"
|
|
75
|
+
else
|
|
76
|
+
info "Checking for newer platform images..."
|
|
77
|
+
docker compose -f "$ROOT/docker-compose.yml" --env-file "$ROOT/.env" pull || warn "image check failed (network/registry?)"
|
|
78
|
+
if [ -n "$(docker compose -f "$ROOT/docker-compose.yml" ps -q 2>/dev/null)" ]; then
|
|
79
|
+
docker compose -f "$ROOT/docker-compose.yml" --env-file "$ROOT/.env" up -d --remove-orphans >/dev/null 2>&1 \
|
|
80
|
+
&& ok "Running services moved to the new images"
|
|
81
|
+
fi
|
|
82
|
+
fi ;;
|
|
67
83
|
deploy)
|
|
68
84
|
# `deploy marketplace` is PLATFORM-OWNER only: it publishes the marketplace package
|
|
69
85
|
# to npm, which a starter developer never does (they INSTALL from the marketplace).
|
package/package.json
CHANGED