unoverse 0.1.33 → 0.1.35

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/lib/create.mjs CHANGED
@@ -31,6 +31,17 @@ async function ask(rl, q) {
31
31
  * pre-encoded base64 `user:token` blob that docker config (and the starter's own
32
32
  * `docr` setting) uses. Wrapping the second shape in base64 AGAIN is how a valid
33
33
  * credential gets refused, so detect it and pass it through untouched. */
34
+ /** The raw dop_v1 token, whichever shape was pasted. docker login needs the raw form,
35
+ * so the base64 user:token blob is decoded before anything downstream sees it. */
36
+ function rawToken(token) {
37
+ if (/^[A-Za-z0-9+/]+=*$/.test(token) && !token.startsWith("dop_v1_")) {
38
+ const decoded = Buffer.from(token, "base64").toString("utf8");
39
+ const m = /^([\x20-\x7e]+):[\x20-\x7e]+$/.exec(decoded);
40
+ if (m) return m[1];
41
+ }
42
+ return token;
43
+ }
44
+
34
45
  function registryAuth(token) {
35
46
  if (/^[A-Za-z0-9+/]+=*$/.test(token)) {
36
47
  const decoded = Buffer.from(token, "base64").toString("utf8");
@@ -280,7 +291,7 @@ export async function create(nameArg) {
280
291
  const r = spawnSync("bash", [operatorScript, "init"], {
281
292
  stdio: "inherit",
282
293
  cwd: name,
283
- env: { ...process.env, UNOVERSE_DOCR_TOKEN: token },
294
+ env: { ...process.env, UNOVERSE_DOCR_TOKEN: rawToken(token) },
284
295
  });
285
296
  if (r.status !== 0) {
286
297
  fail(`setup did not finish. Run 'unoverse start'${name === "." ? "" : ` in ./${name}`} to pick it back up`);
@@ -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,22 @@ 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
+ local login_err
164
+ if login_err=$(echo "$DOCR_TOKEN" | docker login "$DOCR_REGISTRY" -u "$DOCR_TOKEN" --password-stdin 2>&1 >/dev/null); then
165
+ start_background_pull
166
+ echo ""
167
+ echo -e " ${CYAN}⬇${NC} ${DIM}Platform images are downloading in the background while you configure${NC}"
168
+ echo ""
169
+ else
170
+ # The REASON, not just the fact: a swallowed docker error left "login failed"
171
+ # undiagnosable when create had just validated the same credential.
172
+ warn "Registry login failed. Images will not pull until it is fixed"
173
+ echo "$login_err" | grep -vi "warning" | head -3 | sed 's/^/ /'
174
+ fi
175
+ fi
176
+
118
177
  # A DEFAULT, because "from your admin" is meaningless when you are the admin. The
119
178
  # platform ships no database (docker-compose has no postgres), so this points at one
120
179
  # you run. Enter takes the conventional local one.
@@ -144,6 +203,7 @@ cmd_init() {
144
203
  fi
145
204
  fi
146
205
 
206
+ pull_status_line
147
207
  # Redis, current values as defaults
148
208
  local rd
149
209
  rd=$(_env_cur REDIS_HOST); rd="${rd:-host.docker.internal}"
@@ -174,7 +234,7 @@ cmd_init() {
174
234
  # a deployed universe ("there is no auth-off deployment"), and the platform enforces it
175
235
  # rather than trusting this wizard: authConfig.ts refuses to start with auth off when
176
236
  # NODE_ENV=production. So answering "no" here cannot produce an unprotected deployment.
177
- echo ""
237
+ pull_status_line
178
238
  local cur_auth idp_prompt
179
239
  cur_auth=$(_env_cur AUTH_ENABLED)
180
240
  idp_prompt="[y/N]"
@@ -243,6 +303,7 @@ cmd_init() {
243
303
  ok "Redis namespace: ${BOLD}${REDIS_NAMESPACE}${NC}"
244
304
 
245
305
  # OpenAI (for Memory Server)
306
+ pull_status_line
246
307
  local cur_oai
247
308
  cur_oai=$(_env_cur OPENAI_API_KEY)
248
309
  # NOT just the memory server: compose hands this to the unoverse service itself —
@@ -297,21 +358,15 @@ ENVEOF
297
358
 
298
359
  ok ".env created"
299
360
 
300
- # Registry login and the image pull both need a daemon. Without one the token is
301
- # already written to .env, so `unoverse start` does this on its first run.
361
+ # Attach to the background pull started when the token was accepted: layers already
362
+ # down show as complete, the rest stream docker's progress bars. Without a daemon the
363
+ # token is in .env, and `unoverse start` pulls on its first run.
302
364
  if [ "$DOCKER_OK" = "1" ]; then
303
- echo ""
304
- echo -e " Logging in to DigitalOcean Container Registry..."
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
365
  pull_missing_images
366
+ [ -n "$PULL_STATE" ] && rm -f "$PULL_STATE"
312
367
  else
313
368
  echo ""
314
- info "Skipped the registry login and image pull. ${BOLD}unoverse start${NC} does both once Docker is up"
369
+ info "Skipped the image download. ${BOLD}unoverse start${NC} does it once Docker is up"
315
370
  fi
316
371
 
317
372
  # Install to PATH
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unoverse",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
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",