One product per machine: the CLI and the desktop app share one version, the store accounts and the app folder list

- one version: package.json's version is the desktop app's too (desktopVersion removed); tools/desktop-version.ts
  keeps the plugin manifests and the app's copies equal, desktop-release publish refuses another version, release
  order app first then npm publish; the unpublished desktop 0.1.0 candidate is noted in the docs and CHANGELOG
- storebound desktop installs (per user, no admin: macOS DMG -> /Applications or ~/Applications, Windows NSIS /S,
  Linux AppImage + .desktop with the storebound:// handler), updates an older app, notes a newer one (downgrade only
  with --force), asks once ([Y/n], --yes, exit 3 without a terminal), opens on the folder; --dry / --json / --download;
  the install steps are pure plans (cli/lib/desktop-install.ts); the version's own manifest dl/<version>/latest.json
- credentials: one store, the CLI's credential profiles in ~/.storebound/credentials; the desktop writes and reads
  them (profiles.rs, files 0600), moves old keychain accounts into profiles on first start, shows profiles the CLI
  made; precedence env > named profile > project file > default profile; config `account`, "account" in the
  project's credentials.json; storebound accounts list | import (dedup by key id / service account e-mail, key files
  copied 0600, project file keeps keystore and review contact, backup, --dry, never prints values) | use
- app folders: one list ~/.storebound/projects.json shared by the desktop (registry.rs), storebound desktop,
  apps folders | add | remove, ws init and ws without a workspace file
- desktop: notice when a folder pins another storebound version; storebound-desktop --version
- tests: version drift, desktop decisions and per-OS plans, credential precedence and import, registry (TS + Rust),
  keychain migration and profile round trip (Rust), boot check with old keychain state and the new shared state
This commit is contained in:
CRTkafa 2026-10-13 06:17:31 +08:00
parent bba46b5c7f
commit d2ce185441
60 changed files with 2443 additions and 246 deletions

View file

@ -413,7 +413,7 @@ export const tr: Record<keyof typeof en, string> = {
'checks.sev.accepted': 'Kabul',
'checks.sev.low': 'Düşük',
'folders.lead': 'Her uygulama için storebound.config.ts dosyası olan bir klasör (iOS ve Android tek ayarda). Her uygulamanın kullandığı mağaza hesaplarını seçin; anahtarlar anahtar zincirinde kalır.',
'folders.lead': 'Her uygulama için storebound.config.ts dosyası olan bir klasör (iOS ve Android tek ayarda). Bu liste bilgisayarın listesidir (~/.storebound/projects.json): terminalde `storebound desktop` ya da `storebound apps add` da buraya ekler. Her uygulamanın kullandığı mağaza hesaplarını seçin.',
'folders.list': 'Uygulama klasörleri',
'folders.listSub': '{count} klasör',
'folders.listSub_one': '1 klasör',
@ -447,16 +447,19 @@ export const tr: Record<keyof typeof en, string> = {
'run.dryFailed': 'Deneme çalıştırması başarısız: hiçbir şey yazılmadı',
'run.done': 'Bitti',
'accounts.lead': 'Mağaza API anahtarları sistemin anahtar zincirinde durur (macOS\'ta Anahtar Zinciri, Windows\'ta Kimlik Bilgisi Yöneticisi, Linux\'ta Secret Service). Bir daha gösterilmez, uygulama klasörlerinize yazılmaz ve storebound\'a yalnızca bir çağrı için bellekte verilir.',
'accounts.lead': 'Mağaza hesapları bilgisayarda bir kez, ~/.storebound içinde durur ve storebound CLI ile paylaşılır (`storebound accounts`): her hesap için bir kimlik profili, anahtar dosyalarını yalnızca siz okuyabilirsiniz. Bir daha gösterilmez, uygulama klasörlerinize yazılmaz, storebound\'a bir çağrı için bellekte ulaşır.',
'accounts.list': 'Mağaza hesapları',
'accounts.listSub': 'Yalnızca etiketler: anahtarlar anahtar zincirinde',
'accounts.listSub': 'Yalnızca etiketler ve profil adları: anahtarlar ~/.storebound içinde',
'accounts.emptyTitle': 'Henüz mağaza hesabı yok',
'accounts.emptyText': 'Aşağıdan bir Google Play hizmet hesabı veya App Store Connect API anahtarı ekleyin. Mağaza okumaları bunlara ihtiyaç duyar; uygulama klasörleri onlarsız da çalışır.',
'accounts.unused': 'Kullanılmıyor',
'accounts.remove': 'Kaldır',
'accounts.removeAria': '{label} hesabını kaldır ve anahtarını anahtar zincirinden sil',
'accounts.removeAria': '{label} hesabını kaldır; anahtar dosyası ~/.storebound içindeki .removed klasörüne taşınır',
'accounts.saved': '"{label}" kaydedildi',
'accounts.savedDetail': 'Anahtar sistemin anahtar zincirinde.',
'accounts.savedDetail': 'Anahtar ~/.storebound içindeki bir kimlik profilinde (CLI da kullanır).',
'acct.profile': 'profil {name}',
'acct.noProfile': 'anahtar henüz anahtar zincirinden taşınmadı',
'app.pinNotice': 'Bu proje storebound {pin} kullanıyor (package.json), uygulama {app} çalıştırıyor. Uygulama kendi kopyasıyla çalışır; ikisini aynı tutmak için projeyi `bun add -d storebound@{app}` (ya da npm i -D) ile güncelleyin.',
'accounts.playSub': 'Hizmet hesabı JSON anahtarı',
'accounts.appleSub': 'Kullanıcılar ve Erişim, Entegrasyonlar altındaki ekip API anahtarı (.p8)',
'accounts.label': 'Ad',
@ -467,7 +470,7 @@ export const tr: Record<keyof typeof en, string> = {
'accounts.issuerId': 'Veren kimliği',
'accounts.choosePlay': 'JSON anahtarını seç',
'accounts.chooseApple': '.p8 anahtarını seç',
'accounts.safety': 'Anahtarlar sistemin dosya penceresinde seçilir ve doğrudan anahtar zincirine gider. Uygulama penceresi onları hiç görmez.',
'accounts.safety': 'Anahtarlar sistemin dosya penceresinde seçilir ve doğrudan ~/.storebound klasörüne gider (yalnızca siz okuyabilirsiniz). Pencere onları hiç görmez.',
'deps.lead': 'Ekran görüntüleri, uygulama önizlemeleri, simgeler ve simülatör ya da emülatör kayıtları için yerel araçlar. Mağaza işleri (metinler, ürünler, sürümler, yorumlar) hiçbirine ihtiyaç duymaz.',
'deps.checkAgain': 'Yeniden kontrol et',
@ -549,7 +552,7 @@ export const tr: Record<keyof typeof en, string> = {
'wz.p2t': 'Tasarımı gereği güvenli',
'wz.p2s': 'Okumalar salt okunurdur. Her değişiklik önce bir deneme çalıştırması ve sistem onayı gösterir.',
'wz.p3t': 'Anahtarlarınız sizde kalır',
'wz.p3s': 'Mağaza anahtarları sistemin anahtar zincirinde durur. Hesap yok, telemetri yok.',
'wz.p3s': 'Mağaza anahtarları ~/.storebound içinde, storebound CLI ile paylaşılır. Hesap yok, telemetri yok.',
'wz.appsTitle': 'Uygulamalarınızı ekleyin',
'wz.appsLead': 'Her uygulama, onu iki mağazada anlatan bir storebound.config.ts dosyası olan bir klasördür.',
'wz.addTitle': 'Var olan klasörü ekle',
@ -609,11 +612,11 @@ export const tr: Record<keyof typeof en, string> = {
'color.teal': 'Deniz mavisi',
'color.sky': 'Gök mavisi',
'color.slate': 'Arduvaz',
'accounts.leadStudios': 'Stüdyolar mağaza hesaplarınızı gruplar: Google Play hizmet hesapları ve App Store Connect ekip anahtarları. Anahtarlar sistem anahtar zincirinde kalır; uygulamalar "Uygulamaları içe aktar" ile gelir.',
'accounts.leadStudios': 'Stüdyolar mağaza hesaplarınızı gruplar: Google Play hizmet hesapları ve App Store Connect ekip anahtarları. Anahtarlar ~/.storebound içinde, CLI ile paylaşılır; uygulamalar Uygulamaları içe aktar ile gelir.',
'accounts.emptyTextStudios': 'Bir stüdyo, sonra onun Google Play hizmet hesabını ve App Store Connect anahtarlarını ekleyin. "Uygulamaları içe aktar" bu anahtarların gördüğü tüm uygulamaları listeler.',
'accounts.playSubMany': 'Hizmet hesabı JSON anahtarı. Bir hizmet hesabı birden çok Play geliştirici hesabına davet edilebilir: hepsinin uygulamalarını görür.',
'accounts.appleSubMany': 'Ekip başına bir API anahtarı (şirket ya da bireysel): anahtar kimliği, issuer kimliği ve .p8 dosyası.',
'accounts.safetyStudios': 'Anahtarlar seçtiğiniz dosyadan bir kez okunur, sistem anahtar zincirine kaydedilir ve bir daha gösterilmez. storebound bir hesabın anahtarını tek bir çağrı için bellekte alır. Stüdyo adları, etiketler ve uygulama listeleri bu bilgisayarda kalır.',
'accounts.safetyStudios': 'Anahtarlar seçtiğiniz dosyadan bir kez okunur, ~/.storebound içinde saklanır (yalnızca siz okuyabilirsiniz, storebound CLI ile paylaşılır) ve bir daha gösterilmez. storebound bir hesabın anahtarını bir çağrı için bellekte alır. Stüdyo adları, etiketler ve uygulama listeleri bu bilgisayarda kalır.',
'acct.ok': 'Çalışıyor',
'acct.noApps': 'Uygulama yok',
'acct.permission': 'İzin eksik',
@ -770,7 +773,7 @@ export const tr: Record<keyof typeof en, string> = {
'set.writesText': 'Önce deneme, sonra sistem penceresinde sizin Uygula onayınız. Gönderme, yayınlama ve yayımlama konsollarda kalır.',
'set.privacy': 'Gizlilik',
'set.p1': 'Telemetri, analiz ya da çökme raporu yok: uygulama yalnızca siz istediğinizde, anahtarlarınızla mağazalarla konuşur.',
'set.p2': 'Mağaza anahtarları sistem anahtar zincirinde durur; uygulama veri klasöründe adlar, ayarlar ve kayıtlı okumalar vardır, anahtar asla.',
'set.p2': 'Mağaza anahtarları ~/.storebound, uygulama klasörü listesi ~/.storebound/projects.json içinde, CLI ile paylaşılır; uygulama veri klasöründe etiketler, ayarlar ve kayıtlı okumalar durur, anahtar asla.',
'set.p3': 'Siz bir sistem penceresinde onaylamadıkça uygulama klasörlerinize hiçbir şey yazılmaz.',
'set.p4': 'Test kullanıcısı e-postaları maskeli kalır; yorum taslakları her uygulama klasörünün .storebound/ klasöründe (gitignore) kalır.',
'set.reveal': 'Veri klasörünü göster',
@ -787,7 +790,7 @@ export const tr: Record<keyof typeof en, string> = {
'set.updateInstall': '{version} sürümünü yükle…',
'set.updateInstalling': 'Güncelleme indiriliyor… {done}',
'set.reset': 'Sıfırla',
'set.resetText': '{accounts} hesabı (anahtarları anahtar zincirinden silinir), {studios} stüdyoyu, {folders} uygulama klasörünü (klasörler diskte kalır), eşleştirmeyi, yok sayılanları, ayarları ve kayıtlı okumaları unutur. Önce bir sistem penceresi sorar.',
'set.resetText': '{studios} stüdyoyu, {accounts} hesabın etiketlerini, eşleştirmeyi, yok sayılanları, ayarları ve kayıtlı okumaları unutur. ~/.storebound içindeki hesaplar ve {folders} uygulama klasörü kalır: CLI da kullanır. Önce bir sistem penceresi sorar.',
'set.resetButton': 'Uygulamayı sıfırla',
'set.resetDone': 'storebound masaüstü sıfırlandı',
'set.footer': 'Bu bilgisayarın bildiği {n} uygulama',
@ -1277,7 +1280,7 @@ export const tr: Record<keyof typeof en, string> = {
'crash.retry': 'Yeniden dene',
'crash.reload': 'Yeniden yükle',
'crash.reset': 'Kayıtlı durumu sıfırla (yedek tutulur)',
'crash.resetHint': 'Boş bir listeyle başlar. Kayıtlı durum bir yedek dosyada saklanır, mağaza anahtarları anahtar zincirinde kalır, uygulama klasörlerine dokunulmaz.',
'crash.resetHint': 'Bilgisayarın hesapları ve uygulama klasörleriyle yeniden başlar. Kayıtlı durum yedek dosya olarak saklanır, mağaza anahtarları ~/.storebound içinde kalır, uygulama klasörlerine dokunulmaz.',
'crash.resetDone': 'Kayıtlı durum sıfırlandı. Yedek: {path}. Yeniden yükleniyor…',
'crash.logNote': 'Uygulama veri klasöründeki logs/webview.log dosyasına da yazıldı (ana klasör ~ olarak, anahtarlar olmadan).',
'folder.missing': 'Klasör bulunamadı.',