@quantiya/codevibe 2.0.67 → 2.0.68

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.
Files changed (2) hide show
  1. package/bin/codevibe +132 -0
  2. package/package.json +5 -5
package/bin/codevibe CHANGED
@@ -361,6 +361,135 @@ _verify_claude_plugin_files() {
361
361
  ' "$state" "$expected_version" 2>/dev/null
362
362
  }
363
363
 
364
+ # ─── Keyring module check (keytar) ───────────────────────────────────
365
+ #
366
+ # keytar is an OPTIONAL dependency of codevibe-core, so npm never fails an install
367
+ # when its native binary is missing. Claude Code's plugin installer can leave the
368
+ # Claude plugin with keytar's source but no build/Release/keytar.node (and no
369
+ # node_modules/.bin): observed 2026-09-23 for the live 2.0.54 cache and for every fresh
370
+ # `claude plugin install` in an empty npm cache, while another install of 2.0.55 did
371
+ # build it. On a machine where core needs the OS keyring, core then fails closed and
372
+ # the Claude companion cannot sign in.
373
+ #
374
+ # Checked on EVERY update, not only reinstalls: a cache can be at the right version
375
+ # and still be missing the binary. Only a keytar present inside the installed plugin
376
+ # that fails to load is repaired, and only where CodeVibe needs it; an absent optional
377
+ # dependency is left alone. The repair re-runs keytar's own install through npm with
378
+ # prebuild-install's bin linked, which downloads the prebuilt binary exactly as a
379
+ # normal install does and needs no compiler toolchain.
380
+
381
+ # Exit 0 = loads, 1 = resolves inside the plugin but fails to load (the first line of
382
+ # the load error is printed on stdout), 3 = not present inside the plugin.
383
+ _claude_plugin_keytar_state() {
384
+ local install_path="$1"
385
+ (cd "$install_path" 2>/dev/null && node -e '
386
+ const fs = require("fs");
387
+ const path = require("path");
388
+ const root = fs.realpathSync(process.cwd());
389
+ let from = root;
390
+ try {
391
+ from = path.dirname(require.resolve("@quantiya/codevibe-core/package.json", { paths: [root] }));
392
+ } catch {}
393
+ let resolved;
394
+ try { resolved = fs.realpathSync(require.resolve("keytar", { paths: [from] })); } catch { process.exit(3); }
395
+ // Only a keytar INSIDE this plugin counts; never probe or repair an unrelated copy
396
+ // that happens to resolve from an ancestor directory.
397
+ if (resolved !== root && !resolved.startsWith(root + path.sep)) process.exit(3);
398
+ try { require(resolved); } catch (e) {
399
+ process.stdout.write(String((e && e.message) || e).split("\n")[0]);
400
+ process.exit(1);
401
+ }
402
+ process.exit(0);
403
+ ' 2>/dev/null)
404
+ }
405
+
406
+ # The credential service core uses for this ENVIRONMENT (config.ts: production and
407
+ # development share one service; any other value falls back to production).
408
+ _codevibe_keychain_service() {
409
+ case "${ENVIRONMENT:-}" in
410
+ experiment) echo "ai.quantiya.app.codevibe.experiment" ;;
411
+ *) echo "ai.quantiya.app.codevibe" ;;
412
+ esac
413
+ }
414
+
415
+ # Does CodeVibe need keytar on this machine? Mirrors the order in which
416
+ # codevibe-core's keychain-backend.ts (initBackends) picks the backend for ONE service:
417
+ # 1. that service's migrated-to-keyring tombstone: keyring only, file storage retired;
418
+ # 2. CODEVIBE_ALLOW_FILE_KEYCHAIN=1 or that service's durable file credentials: file;
419
+ # 3. Linux: only a structurally headless session falls back to file storage, and not
420
+ # when the keyring-used marker says a keyring identity exists here. The updater
421
+ # cannot see the companion's session, so without the marker it depends on it;
422
+ # 4. anywhere else (macOS, Windows): keytar is required.
423
+ # Returns 0 = required, 1 = file storage, 2 = depends on the session.
424
+ _codevibe_keyring_required() {
425
+ local service
426
+ service=$(_codevibe_keychain_service)
427
+ [ -e "$HOME/.codevibe/.migrated-to-keyring-$service" ] && return 0
428
+ [ "${CODEVIBE_ALLOW_FILE_KEYCHAIN:-}" = "1" ] && return 1
429
+ [ -e "$HOME/.codevibe/$service.json" ] && return 1
430
+ if [ "$(uname -s)" = "Linux" ]; then
431
+ [ -e "$HOME/.codevibe/.keyring-used" ] && return 0
432
+ return 2
433
+ fi
434
+ return 0
435
+ }
436
+
437
+ _ensure_claude_plugin_keytar() {
438
+ local state="$1"
439
+ local install_path rc log load_error need problem
440
+ if ! install_path=$(_claude_plugin_state_field "$state" installPath) \
441
+ || ! install_path=$(cd "$install_path" 2>/dev/null && pwd -P); then
442
+ echo -e "${YELLOW}!${NC} Could not locate the installed Claude plugin to check its keyring module."
443
+ return 1
444
+ fi
445
+
446
+ load_error=$(_claude_plugin_keytar_state "$install_path")
447
+ rc=$?
448
+ [ "$rc" -eq 0 ] && return 0
449
+ [ "$rc" -eq 3 ] && return 0
450
+
451
+ case "$load_error" in
452
+ *"Cannot find module"*keytar.node*) problem="is missing its native binary" ;;
453
+ *) problem="does not load (${load_error:-unknown error})" ;;
454
+ esac
455
+
456
+ _codevibe_keyring_required
457
+ need=$?
458
+ if [ "$need" -eq 1 ]; then
459
+ echo -e "${YELLOW}!${NC} The Claude plugin's keyring module (keytar) ${problem}."
460
+ echo -e " ${DIM}This machine uses file-based credential storage, so CodeVibe does not need it.${NC}"
461
+ return 0
462
+ fi
463
+
464
+ echo -e "${YELLOW}!${NC} The Claude plugin's keyring module (keytar) ${problem}."
465
+ echo -e " ${DIM}Restoring it in ${install_path} ...${NC}"
466
+ if ! log=$(mktemp "${TMPDIR:-/tmp}/codevibe-keytar-repair.XXXXXX"); then
467
+ echo -e "${YELLOW}!${NC} Could not create a log file for the repair."
468
+ log=/dev/null
469
+ fi
470
+ (cd "$install_path" && npm rebuild prebuild-install keytar) >"$log" 2>&1
471
+
472
+ if load_error=$(_claude_plugin_keytar_state "$install_path"); then
473
+ [ "$log" = /dev/null ] || rm -f "$log"
474
+ echo -e "${GREEN}✓${NC} Keyring module restored"
475
+ return 0
476
+ fi
477
+ if [ "$need" -eq 0 ]; then
478
+ echo ""
479
+ echo -e "${YELLOW}!${NC} Could not restore the keyring module (${load_error:-unknown error})."
480
+ echo -e " CodeVibe needs it on this machine to read your credentials, so the Claude"
481
+ echo -e " companion cannot sign in until it loads."
482
+ echo -e " ${DIM}Repair log: ${log}${NC}"
483
+ echo -e " ${DIM}Retry: (cd \"${install_path}\" && npm rebuild prebuild-install keytar)${NC}"
484
+ return 1
485
+ fi
486
+ echo -e "${YELLOW}!${NC} Keyring module unavailable (${load_error:-unknown error}). A headless session uses"
487
+ echo -e " file-based credential storage; a desktop session cannot sign in until it loads."
488
+ echo -e " ${DIM}Repair log: ${log}${NC}"
489
+ echo -e " ${DIM}Retry: (cd \"${install_path}\" && npm rebuild prebuild-install keytar)${NC}"
490
+ return 0
491
+ }
492
+
364
493
  do_update() {
365
494
  local update_phase="${CODEVIBE_UPDATE_AFTER_META:-meta}"
366
495
  unset CODEVIBE_UPDATE_AFTER_META
@@ -605,6 +734,9 @@ do_update() {
605
734
  echo -e "${YELLOW}!${NC} Claude plugin files do not match the required package identity."
606
735
  return 1
607
736
  fi
737
+ if ! _ensure_claude_plugin_keytar "$PREVIEW_STATE"; then
738
+ return 1
739
+ fi
608
740
 
609
741
  # ─── Step 5: Tell the user about running sessions ──────────────────────────
610
742
  if [ "$NEEDS_REINSTALL" = "true" ] || [ "$REGISTRATION_CHANGED" = "true" ]; then
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe",
3
- "version": "2.0.67",
3
+ "version": "2.0.68",
4
4
  "description": "Companion and orchestration for Claude Code, OpenAI Codex CLI, and Antigravity, with secure mobile control.",
5
5
  "bin": {
6
6
  "codevibe": "./bin/codevibe"
@@ -14,10 +14,10 @@
14
14
  "test": "node --test test/cli-surface.test.js"
15
15
  },
16
16
  "dependencies": {
17
- "@quantiya/codevibe-antigravity-plugin": "2.0.53",
18
- "@quantiya/codevibe-claude-plugin": "2.0.55",
19
- "@quantiya/codevibe-codex-plugin": "2.0.56",
20
- "@quantiya/codevibe-core": "2.0.49"
17
+ "@quantiya/codevibe-antigravity-plugin": "2.0.54",
18
+ "@quantiya/codevibe-claude-plugin": "2.0.56",
19
+ "@quantiya/codevibe-codex-plugin": "2.0.57",
20
+ "@quantiya/codevibe-core": "2.0.50"
21
21
  },
22
22
  "keywords": [
23
23
  "codevibe",