Commit 5ce8105
7
plików
+691
dodanych
-2
usuniętych
@@ -0,0 +1,3 @@
1
+__pycache__/
2
+*.pyc
3
+archive/
@@ -27,6 +27,7 @@ pag --help # same as above
27
27
- [Trust chain](#trust-chain)
28
28
- [Immutable system](#immutable-system)
29
29
- [Rollback](#rollback)
30
+ - [Rolling – base baked into the ISO](#rolling--base-baked-into-the-iso)
30
31
- [Commands](#commands)
31
32
- [Environment variables](#environment-variables)
32
33
- [Paths and files](#paths-and-files)
@@ -55,6 +56,10 @@ sudo pag update
55
56
# …or the whole system: packages + kernel / initramfs / GRUB
56
57
sudo pag upgrade
57
58
59
+# Base baked into the ISO vs. the repository (full rolling)
60
+pag base # what comes from the image, what pag manages
61
+sudo pag base adopt # adopt the base from the repo (--dry-run = preview)
62
+
58
63
# Verify the integrity of all files
59
64
sudo pag verify --deep
60
65
```
@@ -170,6 +175,33 @@ graph TD
170
175
171
176
---
172
177
178
+### Rolling – base baked into the ISO
179
+
180
+The PaganOS image is built **from sources** (glibc, gcc, GTK, XFCE, kernel…).
181
+`pag` did not install them, so they are not in its database
182
+(`/var/lib/pag/installed.json`) – and since it does not know them,
183
+`pag upgrade` will **not** update them. The image files are not untouchable
184
+though: `pag install <name>` overwrites them (a backup is kept for
185
+`pag rollback`).
186
+
187
+To make the system fully rolling, tell pag what the image base is:
188
+
189
+```bash
190
+pag base # report: how many image packages pag manages, how many can
191
+ # be adopted from the repo, which stay at the image version
192
+sudo pag base adopt # adopt the base from the repo (--dry-run = preview only)
193
+```
194
+
195
+After `pag base adopt` the image packages become ordinary repo packages, so
196
+the next `sudo pag upgrade` updates the **whole** system. The list of base
197
+packages comes from a manifest baked into the image:
198
+`/usr/share/pag/base.json` (name + version; the builder appends an entry for
199
+every package it builds). Builder names are mapped onto repo names (e.g.
200
+`gtk` → `gtk3`, `libX11` → `xorg-libx11`); names with no repo counterpart are
201
+reported as staying at the image version.
202
+
203
+---
204
+
173
205
## Commands
174
206
175
207
### BASICS
@@ -224,6 +256,14 @@ Flathub and suggests names – e.g. `pag firefox` will find the package.
224
256
| `pag sbom export [spdx\|cyclonedx]` | Export an SBOM manifest of all installed components |
225
257
| `pag self-update` | Update the `pag` client itself (GPG signature + SHA256 + syntax, atomically) |
226
258
259
+### ROLLING (BASE FROM THE IMAGE)
260
+
261
+| Command | Description |
262
+|---|---|
263
+| `pag base` | Report: image packages – managed by pag / adoptable from repo / no counterpart |
264
+| `pag base adopt` | Adopts the base packages from the repo (replaces image files, backup for `pag rollback`); `--dry-run` = preview |
265
+| `pag base adopt <pkg>...` | Adopts only the given base packages |
266
+
227
267
### FLATPAK
228
268
229
269
| Command | Description |
@@ -27,6 +27,7 @@ pag --help # to samo co wyżej
27
27
- [Łańcuch zaufania](#łańcuch-zaufania)
28
28
- [System immutable](#system-immutable)
29
29
- [Rollback](#rollback)
30
+ - [Rolling – baza z obrazu ISO](#rolling--baza-z-obrazu-iso)
30
31
- [Komendy](#komendy)
31
32
- [Zmienne środowiskowe](#zmienne-środowiskowe)
32
33
- [Ścieżki i pliki](#ścieżki-i-pliki)
@@ -55,6 +56,10 @@ sudo pag update
55
56
# …albo cały system: pakiety + kernel / initramfs / GRUB
56
57
sudo pag upgrade
57
58
59
+# Baza zbudowana w obrazie ISO a repozytorium (pełny rolling)
60
+pag base # co jest z obrazu, a czym pag już zarządza
61
+sudo pag base adopt # dograj bazę z repo (--dry-run = podgląd)
62
+
58
63
# Sprawdź integralność wszystkich plików
59
64
sudo pag verify --deep
60
65
```
@@ -171,6 +176,32 @@ graph TD
171
176
172
177
---
173
178
179
+### Rolling – baza z obrazu ISO
180
+
181
+Obraz PaganOS jest zbudowany **ze źródeł** (glibc, gcc, GTK, XFCE, jądro…).
182
+`pag` ich nie instalował, więc nie ma ich w swojej bazie
183
+(`/var/lib/pag/installed.json`) – a skoro ich nie zna, to `pag upgrade`
184
+ich **nie zaktualizuje**. Pliki z obrazu nie są jednak nietykalne:
185
+`pag install <nazwa>` je nadpisze (z kopią zapasową dla `pag rollback`).
186
+
187
+Żeby system był w pełni rolling, trzeba powiedzieć pagowi, co jest bazą obrazu:
188
+
189
+```bash
190
+pag base # raport: ile pakietów z obrazu pag zarządza, ile da się
191
+ # zaadoptować z repo i co zostaje w wersji z obrazu
192
+sudo pag base adopt # dograj bazę z repo (--dry-run = podgląd, nic nie instaluje)
193
+```
194
+
195
+Po `pag base adopt` pakiety z obrazu stają się zwykłymi pakietami z repo,
196
+więc kolejne `sudo pag upgrade` aktualizuje **cały** system. Spis pakietów
197
+bazy pochodzi z manifestu wypalanego w obrazie: `/usr/share/pag/base.json`
198
+(nazwa + wersja; builder dopisuje wpis przy każdym budowanym pakiecie).
199
+Nazwy z buildera są mapowane na nazwy z repo (np. `gtk` → `gtk3`,
200
+`libX11` → `xorg-libx11`); te bez odpowiednika w repo raport zostawia
201
+w wersji z obrazu i mówi o tym wprost.
202
+
203
+---
204
+
174
205
## Komendy
175
206
176
207
### PODSTAWY
@@ -225,6 +256,14 @@ i Flathub oraz podpowiada nazwy – np. `pag firefox` znajdzie pakiet.
225
256
| `pag sbom export [spdx\|cyclonedx]` | Eksportuje manifest SBOM wszystkich zainstalowanych komponentów |
226
257
| `pag self-update` | Aktualizuje samego klienta `pag` (podpis GPG + SHA256 + składnia, atomowo) |
227
258
259
+### ROLLING (BAZA Z OBRAZU)
260
+
261
+| Komenda | Opis |
262
+|---|---|
263
+| `pag base` | Raport: pakiety z obrazu ISO – zarządzane przez pag / do adoptowania z repo / bez odpowiednika |
264
+| `pag base adopt` | Dogrywa pakiety bazy z repo (zastępuje pliki z obrazu, kopie dla `pag rollback`); `--dry-run` = podgląd |
265
+| `pag base adopt <pkg>...` | Adoptuje tylko wskazane pakiety bazy |
266
+
228
267
### FLATPAK
229
268
230
269
| Komenda | Opis |
231
270
new file mode 100644
@@ -0,0 +1,84 @@
1
+#!/usr/bin/env python3
2
+"""extract-pag-lang.py – wyciąga wbudowane tłumaczenia z klienta `pag/pag`
3
+do plików `pag/pag-lang/{pl,en}.json` (klucz "usage" = tekst --help).
4
+
5
+Użycie:
6
+ python3 pag/extract-pag-lang.py # odśwież pag-lang/*.json
7
+ python3 pag/extract-pag-lang.py --check # tylko pokaż liczby kluczy
8
+
9
+Katalog projektu pag (odpowiada 1:1 repo git.paganlinux.eu/git/pag):
10
+ pag/ – klient (PAG_VERSION)
11
+ pag/pag-lang/ – tłumaczenia (generowane tym skryptem)
12
+ pag/README.md, README-EN.md
13
+ pag/archive/ – stare szkice klienta (build ich nie używa)
14
+
15
+Potem builder instaluje je do /usr/share/pag/lang (install_pag + respin-iso.sh),
16
+a `pag` czyta je w kolejności: PAG_LANG_DIR → /etc/pag/lang →
17
+/usr/share/pag/lang → ./pag-lang (dev).
18
+"""
19
+from __future__ import annotations
20
+
21
+import importlib.util
22
+import json
23
+import os
24
+import sys
25
+from importlib.machinery import SourceFileLoader
26
+
27
+HERE = os.path.dirname(os.path.abspath(__file__))
28
+PAG = os.path.join(HERE, "pag")
29
+OUT = os.path.join(HERE, "pag-lang")
30
+LANGS = ("pl", "en")
31
+
32
+
33
+def load_pag():
34
+ # Eksportujemy CZYSTE wbudowane słowniki (bez nadpisań ze starych plików)
35
+ os.environ["PAG_LANG_NO_FILES"] = "1"
36
+ # Plik nie ma rozszerzenia .py → trzeba podać loader jawnie
37
+ loader = SourceFileLoader("pagsrc", PAG)
38
+ spec = importlib.util.spec_from_loader("pagsrc", loader)
39
+ mod = importlib.util.module_from_spec(spec)
40
+ sys.modules["pagsrc"] = mod
41
+ # __name__ != "__main__" → main() z paga się NIE uruchomi
42
+ loader.exec_module(mod)
43
+ return mod
44
+
45
+
46
+def main() -> int:
47
+ check = "--check" in sys.argv
48
+ if not os.path.isfile(PAG):
49
+ print(f"Brak pliku: {PAG}", file=sys.stderr)
50
+ return 1
51
+ mod = load_pag()
52
+ tables = getattr(mod, "T", {})
53
+ # Kontrola spójności PL/EN (pomijamy klucze „*_pl” – to celowe warianty PL)
54
+ _pk = {k for k in tables.get("pl", {}) if not k.endswith("_pl")}
55
+ _ek = {k for k in tables.get("en", {}) if not k.endswith("_pl")}
56
+ _miss_en = sorted(_pk - _ek)
57
+ _miss_pl = sorted(_ek - _pk)
58
+ if _miss_en:
59
+ print(f" ! en: brakuje {len(_miss_en)} kluczy: {', '.join(_miss_en[:10])}"
60
+ + (" …" if len(_miss_en) > 10 else ""))
61
+ if _miss_pl:
62
+ print(f" ! pl: brakuje {len(_miss_pl)} kluczy: {', '.join(_miss_pl[:10])}"
63
+ + (" …" if len(_miss_pl) > 10 else ""))
64
+ os.makedirs(OUT, exist_ok=True)
65
+ for code in LANGS:
66
+ data = dict(tables.get(code, {}))
67
+ usage = getattr(mod, f"USAGE_{code.upper()}", "")
68
+ if usage:
69
+ data["usage"] = usage
70
+ n = len(data)
71
+ if check:
72
+ print(f" [{code}] {n} kluczy")
73
+ continue
74
+ path = os.path.join(OUT, f"{code}.json")
75
+ with open(path, "w", encoding="utf-8") as fh:
76
+ json.dump(data, fh, ensure_ascii=False, indent=2, sort_keys=True)
77
+ print(f"✓ {path} ({n} kluczy)")
78
+ if not check:
79
+ print("Gotowe. Edytuj JSON-y, a builder/ISO wezmą je automatycznie.")
80
+ return 0
81
+
82
+
83
+if __name__ == "__main__":
84
+ sys.exit(main())
@@ -55,7 +55,7 @@ import threading, itertools
55
55
import uuid # serialNumber SBOM (CycloneDX)
56
56
57
57
# Wersja klienta – do porównania z repo.json["pag_version"] (self-update)
58
-PAG_VERSION = "3.3.23"
58
+PAG_VERSION = "3.4.0"
59
59
from urllib.error import URLError, HTTPError
60
60
61
61
# =============================================================================
@@ -392,6 +392,26 @@ T = {
392
392
"conf_pacsave": "Modified config kept as {path}",
393
393
# Zależności wirtualne (provides)
394
394
"provides_conflict": "Multiple providers for '{name}' ({providers}) – using '{chosen}'",
395
+
396
+ # Base baked into the ISO image (pag base / base adopt)
397
+ "base_title": "Base from the ISO image",
398
+ "base_no_manifest": "This system carries no base manifest (/usr/share/pag/base.json) – nothing to adopt.",
399
+ "base_iso_version": "Image version",
400
+ "base_total": "Base packages (from image)",
401
+ "base_tracked": "Managed by pag",
402
+ "base_adoptable": "Adoptable from repo",
403
+ "base_unmatched": "No counterpart in repo",
404
+ "base_adopt_hint": "Adopt them (image files get replaced by repo packages): sudo pag base adopt",
405
+ "base_unmatched_hint": "These stay at the image version (no such package in repo):",
406
+ "base_more": "more",
407
+ "base_nothing_selected": "None of the given names is on the adopt list.",
408
+ "base_all_managed": "Every base package is already managed by pag.",
409
+ "base_adopt_plan": "Adopting {} base packages from the repo (~{:.0f} MB):",
410
+ "base_dry_run": "Dry run – nothing was installed.",
411
+ "base_unmanaged_hint": "{} packages from the ISO image are not managed by pag yet – 'sudo pag base adopt' replaces them with repo packages (only then does 'pag upgrade' update them).",
412
+ "base_overwrite": "{} comes from the ISO image – its files will be replaced (backup kept for 'pag rollback').",
413
+ "base_reboot_hint": "Base adopted from the repo – reboot before using the new libraries/toolchain.",
414
+ "stats_base": "Base (image) managed",
395
415
},
396
416
"pl": {
397
417
"root_required": "pag wymaga uprawnień root (sudo).",
@@ -502,6 +522,26 @@ T = {
502
522
"conf_pacnew": "Zmieniony plik konfiguracyjny zachowany; nowa wersja: {path}",
503
523
"conf_pacsave": "Zmieniony plik konfiguracyjny zachowany jako {path}",
504
524
"provides_conflict": "Wielu dostawców dla '{name}' ({providers}) – używam '{chosen}'",
525
+
526
+ # Baza wypalona w obrazie ISO (pag base / base adopt)
527
+ "base_title": "Baza z obrazu ISO",
528
+ "base_no_manifest": "Ten system nie ma manifestu bazy (/usr/share/pag/base.json) – nie ma czego adoptować.",
529
+ "base_iso_version": "Wersja obrazu",
530
+ "base_total": "Pakiety bazy (z obrazu)",
531
+ "base_tracked": "Zarządzane przez pag",
532
+ "base_adoptable": "Do adoptowania z repo",
533
+ "base_unmatched": "Bez odpowiednika w repo",
534
+ "base_adopt_hint": "Adoptuj (pliki z obrazu zostaną zastąpione pakietami z repo): sudo pag base adopt",
535
+ "base_unmatched_hint": "Te zostaną w wersji z obrazu (brak takiego pakietu w repo):",
536
+ "base_more": "więcej",
537
+ "base_nothing_selected": "Żadna z podanych nazw nie jest na liście do adoptowania.",
538
+ "base_all_managed": "Wszystkie pakiety bazy są już zarządzane przez pag.",
539
+ "base_adopt_plan": "Adoptowanie {} pakietów bazy z repo (~{:.0f} MB):",
540
+ "base_dry_run": "Próba na sucho – nic nie zainstalowano.",
541
+ "base_unmanaged_hint": "{} pakietów z obrazu ISO nie jest jeszcze zarządzanych przez pag – 'sudo pag base adopt' zamieni je na pakiety z repo (dopiero wtedy 'pag upgrade' je aktualizuje).",
542
+ "base_overwrite": "{} pochodzi z obrazu ISO – pliki zostaną zastąpione (kopia zapasowa dla 'pag rollback').",
543
+ "base_reboot_hint": "Baza zaadoptowana z repo – zrestartuj system, zanim zaczniesz używać nowych bibliotek/narzędzi.",
544
+ "stats_base": "Baza (obraz) zarządzana",
505
545
},
506
546
}
507
547
@@ -599,6 +639,7 @@ STAGING_DIR = "/.pag_staging" # na tej samej partycji co / (unikamy E
599
639
PKG_EXT = ".pag"
600
640
REPO_CACHE_TTL = 3600
601
641
MAX_PKG_SIZE = 2 * 1024 * 1024 * 1024 # 2 GB – maksymalny rozmiar paczki
642
+BASE_MANIFEST = "/usr/share/pag/base.json" # baza z obrazu ISO (pag base/adopt)
602
643
603
644
# ── UPRZYWILEJOWANE PLIKI ────────────────────────────────────────────────────
604
645
# Domyślnie pag zdejmuje bit SUID i wymusza właściciela root:root. Niektóre
@@ -2748,6 +2789,10 @@ def cmd_install(package_names, as_dep=False, upgrade=False):
2748
2789
marker = f" [{_('new')}]"
2749
2790
print(f" {name}-{p.version}{marker}")
2750
2791
2792
+ # Pakiet z bazy obrazu ISO (nie w installed.json) → ostrzeż, że pliki z ISO
2793
+ # zostaną zastąpione pakietem z repo (kopia do `pag rollback`).
2794
+ _base_warn_overwrite(to_install)
2795
+
2751
2796
if not as_dep and not upgrade:
2752
2797
if not _ask_confirm():
2753
2798
print(_("cancelled")); return 0
@@ -3348,6 +3393,209 @@ def _pending_updates() -> List[str]:
3348
3393
return [n for n, i in installed.items()
3349
3394
if n not in pinned and (rp := repo.get(n)) and _candidate_newer(rp, i)]
3350
3395
3396
+# =============================================================================
3397
+# BAZA Z OBRAZU ISO → REPO (`pag base`) – pełny rolling
3398
+# =============================================================================
3399
+# ISO jest zbudowane ZE ŹRÓDEŁ (glibc, gcc, gtk, XFCE, …) – pag tych pakietów
3400
+# nie instalował, więc nie ma ich w installed.json/files.db. Skutki:
3401
+# • `pag upgrade` ich NIE dotyka (nie ma o nich pojęcia),
3402
+# • mimo to `pag install <nazwa>` je NADPISZE – pag robi kopię plików spoza
3403
+# swojej bazy (patrz _new_transaction_backup_root), więc nie są nietykalne.
3404
+#
3405
+# /usr/share/pag/base.json (wypalany przez build-pagan.sh) mówi, co jest w bazie
3406
+# obrazu i w jakiej wersji. `pag base` pokazuje stan, a `pag base adopt` dogrywa
3407
+# te pakiety z repo – po tym zabiegu pag zarządza nimi normalnie, więc
3408
+# `pag upgrade` aktualizuje CAŁY system (rolling), a nie tylko to, co sam
3409
+# wcześniej zainstalował.
3410
+#
3411
+# Nazwy z buildera różnią się od nazw pakietów w repo (builder buduje „libX11”,
3412
+# repo ma pakiet „xorg-libx11”), stąd aliasy poniżej. Nazwy bez odpowiednika
3413
+# w repo zostają w wersji z obrazu – raport mówi o tym wprost, zamiast udawać,
3414
+# że są aktualne.
3415
+BASE_NAME_ALIASES = {
3416
+ "atk": "at-spi2-core",
3417
+ "dejavu-fonts": "dejavu-ttf",
3418
+ "fontconfig": "xorg-fontconfig",
3419
+ "gtk": "gtk3",
3420
+ "iceauth": "xorg-iceauth",
3421
+ "libX11": "xorg-libx11",
3422
+ "libXau": "xorg-libxau",
3423
+ "libXdmcp": "xorg-libxdmcp",
3424
+ "libXext": "xorg-libxext",
3425
+ "libXft": "xorg-libxft",
3426
+ "libdrm": "xorg-libdrm",
3427
+ "libnspr": "nspr",
3428
+ "libxkbfile": "xorg-libxkbfile",
3429
+ "libxklavier": "xorg-libxklavier",
3430
+ "linux-pam": "pam",
3431
+ "pixman": "xorg-pixman",
3432
+ "sdl2": "sdl",
3433
+ "setxkbmap": "xorg-setxkbmap",
3434
+ "xauth": "xorg-xauth",
3435
+ "xf86-input-libinput": "xorg-xf86-input-libinput",
3436
+ "xf86-video-fbdev": "xorg-xf86-video-fbdev",
3437
+ "xinit": "xorg-xinit",
3438
+ "xkbcomp": "xorg-xkbcomp",
3439
+ "xkeyboard-config": "xorg-xkeyboard-config",
3440
+}
3441
+
3442
+
3443
+def _base_manifest() -> dict:
3444
+ """Manifest bazy z obrazu ({} gdy obraz go nie dostarcza)."""
3445
+ _d = load_json(os.path.join(PAG_ROOT, BASE_MANIFEST.lstrip("/")))
3446
+ return _d if isinstance(_d, dict) else {}
3447
+
3448
+
3449
+def _base_entries() -> list:
3450
+ """[(nazwa_z_buildera, wersja_z_obrazu), ...] z manifestu."""
3451
+ _out = []
3452
+ for _e in (_base_manifest().get("packages") or []):
3453
+ if isinstance(_e, str):
3454
+ if _e:
3455
+ _out.append((_e, "?"))
3456
+ elif isinstance(_e, dict) and _e.get("name"):
3457
+ _out.append((str(_e["name"]), str(_e.get("version") or "?")))
3458
+ return _out
3459
+
3460
+
3461
+def _base_repo_name(name: str, repo: dict, installed: dict) -> str:
3462
+ """Nazwa pakietu w repo dla nazwy z buildera (alias → provides → brak)."""
3463
+ if name in repo:
3464
+ return name
3465
+ _alias = BASE_NAME_ALIASES.get(name)
3466
+ if _alias and _alias in repo:
3467
+ return _alias
3468
+ _real = _resolve_provides(name, repo, installed)
3469
+ return _real if _real in repo else ""
3470
+
3471
+
3472
+def _base_summary(repo: Optional[dict] = None) -> dict:
3473
+ """Klasyfikacja pakietów bazy z obrazu wobec repo.
3474
+
3475
+ repo=None → pobiera indeks repo (potrzebny do listy „do adoptowania”).
3476
+ repo={} → bez sieci: rozpoznaje tylko to, czym pag już zarządza.
3477
+ """
3478
+ _entries = _base_entries()
3479
+ if not _entries:
3480
+ return {"total": 0, "tracked": 0, "adoptable": [], "unmatched": []}
3481
+ if repo is None:
3482
+ repo = fetch_all_packages()
3483
+ installed = load_json(INSTALLED_DB)
3484
+ tracked, adoptable, unmatched = 0, [], []
3485
+ for _n, _v in _entries:
3486
+ _real = _base_repo_name(_n, repo, installed)
3487
+ if _n in installed or (_real and _real in installed):
3488
+ tracked += 1
3489
+ elif _real:
3490
+ adoptable.append((_n, _real, _v))
3491
+ else:
3492
+ unmatched.append(_n)
3493
+ return {"total": len(_entries), "tracked": tracked,
3494
+ "adoptable": adoptable, "unmatched": unmatched}
3495
+
3496
+
3497
+def _base_hint_unmanaged() -> None:
3498
+ """Jednozdaniowa informacja dla `pag sync/update/upgrade`."""
3499
+ try:
3500
+ _s = _base_summary()
3501
+ except Exception:
3502
+ return
3503
+ if _s["adoptable"]:
3504
+ print(f"\n ℹ {_('base_unmanaged_hint', len(_s['adoptable']))}")
3505
+
3506
+
3507
+def _base_warn_overwrite(names) -> None:
3508
+ """Ostrzeżenie w `pag install`, gdy pakiet pochodzi z bazy obrazu ISO."""
3509
+ _entries = _base_entries()
3510
+ if not _entries:
3511
+ return
3512
+ _known = set()
3513
+ for _n, _v in _entries:
3514
+ _known.add(_n)
3515
+ _al = BASE_NAME_ALIASES.get(_n)
3516
+ if _al:
3517
+ _known.add(_al)
3518
+ _hit = [n for n in names if n in _known]
3519
+ if _hit:
3520
+ _listed = ", ".join(_hit[:5]) + (" …" if len(_hit) > 5 else "")
3521
+ print(f" ℹ {_('base_overwrite', _listed)}")
3522
+
3523
+
3524
+def cmd_base_status():
3525
+ """`pag base` / `pag base status` – co w systemie pochodzi z obrazu,
3526
+ czym pag już zarządza i co da się zaadoptować z repo."""
3527
+ _man = _base_manifest()
3528
+ if not _man:
3529
+ print(f" ℹ {_('base_no_manifest')}")
3530
+ return 0
3531
+ _s = _base_summary()
3532
+ print(f"\n {_c('bold', _('base_title'))}")
3533
+ print(f" {'─' * 46}")
3534
+ _de = _man.get("de") or ""
3535
+ print(f" {_('base_iso_version'):<34} {_man.get('iso_version', '?')}" + (f" ({_de})" if _de else ""))
3536
+ print(f" {_('base_total'):<34} {_s['total']}")
3537
+ print(f" {_('base_tracked'):<34} {_s['tracked']}")
3538
+ print(f" {_('base_adoptable'):<34} {len(_s['adoptable'])}")
3539
+ print(f" {_('base_unmatched'):<34} {len(_s['unmatched'])}")
3540
+ if _s["adoptable"]:
3541
+ print(f"\n {_('base_adopt_hint')}")
3542
+ for _iso_n, _real, _v in _s["adoptable"][:10]:
3543
+ _m = "" if _iso_n == _real else f" ({_iso_n})"
3544
+ print(f" {_real}-{_v}{_m}")
3545
+ if len(_s["adoptable"]) > 10:
3546
+ print(f" … {len(_s['adoptable']) - 10} {_('base_more')}")
3547
+ if _s["unmatched"]:
3548
+ print(f"\n {_('base_unmatched_hint')}")
3549
+ print(" " + ", ".join(_s["unmatched"][:12])
3550
+ + (f" … +{len(_s['unmatched']) - 12}" if len(_s["unmatched"]) > 12 else ""))
3551
+ return 0
3552
+
3553
+
3554
+def cmd_base_adopt(args):
3555
+ """`pag base adopt [pkg...] [--dry-run]` – dograj bazę z obrazu z repo.
3556
+
3557
+ Pakiety bazy nie ma w installed.json, więc pag traktuje je jak NOWĄ
3558
+ instalację: pliki z obrazu trafiają do kopii transakcji (działa
3559
+ `pag rollback`), a po sukcesie pag zarządza nimi jak każdym innym pakietem.
3560
+ """
3561
+ if not _base_manifest():
3562
+ print(f" ℹ {_('base_no_manifest')}")
3563
+ return 1
3564
+ repo = fetch_all_packages()
3565
+ if not repo:
3566
+ print(f"❌ {_('no_index')}")
3567
+ return 1
3568
+ _want = [a for a in args if not a.startswith("-") and a != "adopt"]
3569
+ _s = _base_summary(repo)
3570
+ plan = _s["adoptable"]
3571
+ if _want:
3572
+ _sel = set(_want)
3573
+ plan = [p for p in plan if p[0] in _sel or p[1] in _sel]
3574
+ if not plan:
3575
+ print(f" ℹ {_('base_nothing_selected')}")
3576
+ return 0
3577
+ if not plan:
3578
+ print(f"✅ {_('base_all_managed')}")
3579
+ return 0
3580
+ _size = sum(repo[r].size_bytes for _i, r, _v in plan if r in repo)
3581
+ print(f"\n {_('base_adopt_plan', len(plan), _size / 1048576)}")
3582
+ for _iso_n, _real, _v in plan[:20]:
3583
+ _rv = repo[_real].version if _real in repo else "?"
3584
+ _m = "" if _iso_n == _real else f" ({_iso_n} → {_real})"
3585
+ print(f" {_real}: {_v} → {_rv}{_m}")
3586
+ if len(plan) > 20:
3587
+ print(f" … {len(plan) - 20} {_('base_more')}")
3588
+ if "-n" in args or "--dry-run" in args:
3589
+ print(f" {_('base_dry_run')}")
3590
+ return 0
3591
+ _rc = cmd_install([r for _i, r, _v in plan])
3592
+ if _rc == 0:
3593
+ # Wymienione biblioteki/narzędzia (glibc, gcc, GTK…) są już nowe na dysku,
3594
+ # ale działające procesy wciąż mają stare w pamięci – restart domyka.
3595
+ print(f"\n ℹ {_('base_reboot_hint')}")
3596
+ return _rc
3597
+
3598
+
3351
3599
def cmd_update(do_upgrade: bool = False):
3352
3600
"""`pag sync` / `pag update` – odświeżenie indeksów + raport aktualizacji.
3353
3601
@@ -3382,6 +3630,7 @@ def cmd_update(do_upgrade: bool = False):
3382
3630
pending = _pending_updates()
3383
3631
if not pending:
3384
3632
print(f"✅ {_('all_up_to_date')}")
3633
+ _base_hint_unmanaged()
3385
3634
return 0
3386
3635
print(f"{_('updates_available', len(pending))}")
3387
3636
installed = load_json(INSTALLED_DB)
@@ -3389,10 +3638,13 @@ def cmd_update(do_upgrade: bool = False):
3389
3638
for n in pending:
3390
3639
print(f" {n}: {installed.get(n, {}).get('version', '?')} → {repo[n].version}")
3391
3640
if not do_upgrade:
3641
+ _base_hint_unmanaged()
3392
3642
return 0 # sync: tylko informacja
3393
3643
if not _ask_confirm():
3394
3644
return 0
3395
- return cmd_install(pending, upgrade=True)
3645
+ _rc = cmd_install(pending, upgrade=True)
3646
+ _base_hint_unmanaged()
3647
+ return _rc
3396
3648
3397
3649
def _initramfs_stale() -> bool:
3398
3650
"""Czy initramfs jest starszy niż najnowsze jądro (wymaga przebudowy)."""
@@ -3977,6 +4229,12 @@ def cmd_stats():
3977
4229
print(f" {_('stats_cache'):<30} {cache_size/1048576:.1f} MB")
3978
4230
print(f" {_('stats_history'):<30} {len(history)}")
3979
4231
print(f" {_('stats_last_update'):<30} {last_update}")
4232
+ _bs = _base_summary(repo={}) # bez sieci – tylko stan zarządzania bazą
4233
+ if _bs["total"]:
4234
+ _bt = f"{_bs['tracked']}/{_bs['total']}"
4235
+ if _bs["tracked"] < _bs["total"]:
4236
+ _bt += " (sudo pag base adopt)"
4237
+ print(f" {_('stats_base'):<30} {_bt}")
3980
4238
by_size = sorted(installed.items(), key=lambda x: x[1].get("size_bytes", 0), reverse=True)[:5]
3981
4239
if by_size:
3982
4240
print(f"\n {_c('dim', 'Top 5:')}")
@@ -5410,6 +5668,10 @@ BASIC:
5410
5668
pag stats System statistics
5411
5669
pag download <pkg>... Download packages to cache (offline prep)
5412
5670
5671
+ROLLING (base baked into the image):
5672
+ pag base [status] Image packages vs. repo (what pag manages)
5673
+ pag base adopt [pkg...] Adopt base packages from the repo (--dry-run = preview)
5674
+
5413
5675
SECURITY:
5414
5676
pag key-add <url|file> Import GPG key
5415
5677
pag key-list List trusted keys
@@ -5465,6 +5727,10 @@ PODSTAWOWE:
5465
5727
pag stats Statystyki systemu
5466
5728
pag download <pkg>... Pobierz do cache (offline)
5467
5729
5730
+ROLLING (baza wypalona w obrazie):
5731
+ pag base [status] Pakiety z obrazu vs repo (czym pag zarządza)
5732
+ pag base adopt [pkg...] Adoptuj pakiety bazy z repo (--dry-run = podgląd)
5733
+
5468
5734
BEZPIECZEŃSTWO:
5469
5735
pag key-add <url|file> Importuj klucz GPG
5470
5736
pag key-list Lista zaufanych kluczy
@@ -5577,6 +5843,12 @@ def main():
5577
5843
if cmd in READ_ONLY:
5578
5844
sys.exit(READ_ONLY[cmd]() or 0)
5579
5845
5846
+ # --- `pag base` / `pag base status`: tylko odczyt (bez roota i bez locka) ---
5847
+ # `pag base adopt` NIE wpada tutaj – idzie ścieżką komend zapisu (roota + lock
5848
+ # pilnuje wspólna obsługa niżej), żeby transakcja była bezpieczna.
5849
+ if cmd in ("base", "base-status") and not (args and args[0] == "adopt"):
5850
+ sys.exit(cmd_base_status() or 0)
5851
+
5580
5852
# --- Smart search: `pag <nazwa-pakietu>` → repo + Flathub + sugestie ---
5581
5853
WRITE_CMDS = {
5582
5854
"install", "remove", "update", "sync", "upgrade", "clean", "download",
@@ -5585,6 +5857,7 @@ def main():
5585
5857
"self-update",
5586
5858
"flatpak", "flatpak-install", "flatpak-remove", "flatpak-update",
5587
5859
"deploy-rollback", "deploy-cleanup", "initramfs-update", "grub-update",
5860
+ "base", "base-adopt",
5588
5861
}
5589
5862
if cmd not in WRITE_CMDS:
5590
5863
# Literówka komendy? (np. `pag instal steam` zamiast `pag install`) –
@@ -5643,6 +5916,8 @@ def main():
5643
5916
"deploy-cleanup": lambda: cmd_deploy_cleanup(int(args[0]) if args else 3),
5644
5917
"initramfs-update": cmd_initramfs_update,
5645
5918
"grub-update": cmd_grub_update,
5919
+ "base": lambda: cmd_base_adopt(args),
5920
+ "base-adopt": lambda: cmd_base_adopt(args),
5646
5921
}
5647
5922
5648
5923
fn = WRITE_COMMANDS.get(cmd)
5649
5924
new file mode 100644
@@ -0,0 +1,120 @@
1
+{
2
+ "active_deployment": "ACTIVE",
3
+ "all_installed": "All packages are already installed.",
4
+ "all_up_to_date": "All packages are up to date.",
5
+ "autoremove_none": "No orphaned packages.",
6
+ "autoremove_ok": "Removed {} orphaned packages.",
7
+ "base_adopt_hint": "Adopt them (image files get replaced by repo packages): sudo pag base adopt",
8
+ "base_adopt_plan": "Adopting {} base packages from the repo (~{:.0f} MB):",
9
+ "base_adoptable": "Adoptable from repo",
10
+ "base_all_managed": "Every base package is already managed by pag.",
11
+ "base_dry_run": "Dry run – nothing was installed.",
12
+ "base_iso_version": "Image version",
13
+ "base_more": "more",
14
+ "base_no_manifest": "This system carries no base manifest (/usr/share/pag/base.json) – nothing to adopt.",
15
+ "base_nothing_selected": "None of the given names is on the adopt list.",
16
+ "base_overwrite": "{} comes from the ISO image – its files will be replaced (backup kept for 'pag rollback').",
17
+ "base_reboot_hint": "Base adopted from the repo – reboot before using the new libraries/toolchain.",
18
+ "base_title": "Base from the ISO image",
19
+ "base_total": "Base packages (from image)",
20
+ "base_tracked": "Managed by pag",
21
+ "base_unmanaged_hint": "{} packages from the ISO image are not managed by pag yet – 'sudo pag base adopt' replaces them with repo packages (only then does 'pag upgrade' update them).",
22
+ "base_unmatched": "No counterpart in repo",
23
+ "base_unmatched_hint": "These stay at the image version (no such package in repo):",
24
+ "cache_cleared": "{} files ({:.2f} MB) cleared from cache.",
25
+ "cache_ro": "Repo cache is read-only ({cache}) – using local index (may be outdated).\n Refresh as root: sudo pag sync",
26
+ "cancelled": "Cancelled.",
27
+ "conf_pacnew": "Modified config kept; new version saved as {path}",
28
+ "conf_pacsave": "Modified config kept as {path}",
29
+ "continue_q": "Continue? [Y/n] ",
30
+ "db_lock_hint": "If no other pag process is running, wait a moment and retry.",
31
+ "db_locked": "Another pag instance is running.",
32
+ "deploy_cleanup_none": "No deployments to clean (minimum {}).",
33
+ "deploy_cleanup_ok": "Removed {} old deployments.",
34
+ "deploy_rollback_fail": "No previous deployment.",
35
+ "deploy_rollback_ok": "Switched to deployment: {}",
36
+ "deployments_list": "Deployments ({}):",
37
+ "download_fail": "download failed",
38
+ "downloaded": "Downloaded {} to cache ({:.2f} MB).",
39
+ "downloading": "Downloading",
40
+ "flatpak_adding": "Adding Flathub remote...",
41
+ "flatpak_found": "Found {} results:",
42
+ "flatpak_info_branch": "Branch",
43
+ "flatpak_info_desc": "Description",
44
+ "flatpak_info_id": "ID",
45
+ "flatpak_info_origin": "Origin",
46
+ "flatpak_info_size": "Installed size",
47
+ "flatpak_info_version": "Version",
48
+ "flatpak_install_prompt": "Install {}? [Y/n] ",
49
+ "flatpak_installed": "Flatpak {} installed.",
50
+ "flatpak_installing": "Installing {}...",
51
+ "flatpak_menu_fix": "Add /var/lib/flatpak/exports/share to XDG_DATA_DIRS (and .../exports/bin to PATH) in your session, then re-login – PaganDE does this in session/pagande-session.",
52
+ "flatpak_menu_hint": "Installed and verified, but this session may not show it in the menu: Flatpak exports are missing from XDG_DATA_DIRS.",
53
+ "flatpak_missing": "Flatpak is not installed.",
54
+ "flatpak_not_found": "not found on Flathub",
55
+ "flatpak_not_installed": "Flatpak {} is not installed.",
56
+ "flatpak_removed": "Flatpak {} removed.",
57
+ "flatpak_scope": "Installation",
58
+ "flatpak_searching": "Searching Flathub for '{}'...",
59
+ "flatpak_updated": "Flatpaks updated.",
60
+ "flatpak_usage": "Usage: pag flatpak <search|install|remove|list|update|info> [args]",
61
+ "gpg_fail": "GPG verification failed",
62
+ "gpg_missing": "GNUPG MISSING – install gnupg and retry",
63
+ "indexes_refreshed": "Indexes refreshed.",
64
+ "install_failed": "installation failed",
65
+ "installed": "Installed {} packages.",
66
+ "key_add_failed": "Key import failed (gpg error) – key was NOT added.",
67
+ "key_imported": "Key imported successfully.",
68
+ "key_removed": "Key removed: {}",
69
+ "new": "NEW",
70
+ "no_deployments": "No deployments.",
71
+ "no_history": "No transaction history.",
72
+ "no_index": "Cannot fetch repository indexes. Run 'pag update'.",
73
+ "no_keys": "No trusted GPG keys.",
74
+ "no_pinned": "No pinned packages.",
75
+ "no_tty": "No TTY / stdin closed (EOF) – cancelling.",
76
+ "not_found": "not found in repos",
77
+ "not_found_hint": "Check the spelling or run 'pag search <query>'.",
78
+ "not_pinned": "was not pinned.",
79
+ "orphans_found": "Orphaned dependencies ({}): {}",
80
+ "pinned_list": "Pinned packages ({}):",
81
+ "pinned_to": "pinned to",
82
+ "pkg_not_found": "Package not found: {} (not in any repo)",
83
+ "provides_conflict": "Multiple providers for '{name}' ({providers}) – using '{chosen}'",
84
+ "provides_mapped": "{} → {} (provides)",
85
+ "removing": "Removing",
86
+ "repo_added": "Added repository: {}",
87
+ "repo_exists": "Repository already exists: {}",
88
+ "rollback_files": "Rolled back {} files.",
89
+ "rollback_restored": "Restored previous state from snapshot.",
90
+ "root_required": "pag requires root privileges (sudo).",
91
+ "sec_audit": "{pkg} installed by {user}",
92
+ "sec_badname": "Invalid package name: {name}",
93
+ "sec_conflict": "File conflict: {path} owned by {owner}",
94
+ "sec_downgrade": "Downgrade blocked: {pkg} {new} < {old}",
95
+ "sec_https": "HTTPS required for repos",
96
+ "sec_locked": "Another pag process is running",
97
+ "sec_suid": "SUID stripped from {path}",
98
+ "sec_toobig": "Package too large: {size_mb}MB > {max_mb}MB",
99
+ "sha256_mismatch": "SHA256 mismatch",
100
+ "ssl_broken": "Python has no working ssl module (HTTPS impossible) – fix the python/openssl packages (e.g. sudo pag install -f python).",
101
+ "stats_base": "Base (image) managed",
102
+ "stats_cache": "Cache size",
103
+ "stats_files": "Tracked files",
104
+ "stats_history": "Transactions",
105
+ "stats_last_update": "Last update",
106
+ "stats_packages": "Installed packages",
107
+ "stats_size": "Total size",
108
+ "stats_title": "PAG Statistics",
109
+ "to_install": "To install: {} packages ({:.2f} MB)",
110
+ "unpinned": "unpinned.",
111
+ "updated_done": "Index refresh complete. {} packages cached.",
112
+ "updates_available": "⚠ {} packages have updates – run: pag update",
113
+ "upgrading": "Upgrading: {} packages",
114
+ "usage": "pag v3 – Pagan Linux Package Manager\n\nBASIC:\n pag install <pkg>... Install packages\n pag remove <pkg>... Remove packages\n pag update Update PACKAGES (refreshes indexes first)\n pag sync Refresh indexes + show pending package updates\n pag upgrade Update SYSTEM (packages + kernel/initramfs/GRUB)\n pag list [--installed] List available / installed\n pag search <query> Search packages\n pag info <pkg> Package details\n pag files <pkg> List package files\n pag verify [--deep] Verify integrity (--deep = SHA256 per file)\n pag clean Clear download cache\n pag stats System statistics\n pag download <pkg>... Download packages to cache (offline prep)\n\nROLLING (base baked into the image):\n pag base [status] Image packages vs. repo (what pag manages)\n pag base adopt [pkg...] Adopt base packages from the repo (--dry-run = preview)\n\nSECURITY:\n pag key-add <url|file> Import GPG key\n pag key-list List trusted keys\n pag key-remove <id> Remove key\n pag key-trust <repo> Pin repo signing key fingerprint (no TOFU)\n pag key-untrust <repo> Forget repo fingerprint (back to TOFU)\n pag key-trusted List pinned repo fingerprints\n\nADVANCED:\n pag why <pkg> Show why a package is installed\n pag autoremove Auto-remove orphaned dependencies\n pag pin <pkg> [ver] Pin package version\n pag unpin <pkg> Unpin\n pag pinned List pinned\n pag history Transaction history\n pag rollback Rollback last transaction\n pag remove-orphans Remove orphaned deps\n pag repo-add <url> [name] Add repository (drop-in /etc/pag/repos/)\n pag repo-list List repositories\n pag sbom export [fmt] SBOM manifest (spdx|cyclonedx)\n\nFLATPAK:\n pag flatpak [<query>] Search & install (smart)\n pag flatpak search <q> Search Flathub\n pag flatpak install <id> Install flatpak\n pag flatpak remove <id> Remove flatpak\n pag flatpak list List installed flatpaks\n pag flatpak update Update all flatpaks\n pag flatpak info <id> Show flatpak details\n\nIMMUTABLE OS (PAG_IMMUTABLE=1):\n pag deploy-list List all deployments\n pag deploy-rollback Switch to previous deployment\n pag deploy-cleanup [N] Remove old deployments (keep last N, default 3)\n pag initramfs-update Rebuild initramfs for current kernel/deployment\n pag grub-update Regenerate GRUB entries for all deployments\n",
115
+ "verify_errors": "{} problems found:",
116
+ "verify_ok": "All {} files intact.",
117
+ "why_dependency": "dependency of",
118
+ "why_explicit": "explicitly installed",
119
+ "why_not_installed": "not installed"
120
+}
0
121
new file mode 100644
@@ -0,0 +1,128 @@
1
+{
2
+ "active_deployment": "AKTYWNY",
3
+ "all_installed": "Wszystkie pakiety są już zainstalowane.",
4
+ "all_up_to_date": "Wszystkie pakiety są aktualne.",
5
+ "autoremove_none": "Brak osieroconych pakietów.",
6
+ "autoremove_ok": "Usunięto {} osieroconych pakietów.",
7
+ "base_adopt_hint": "Adoptuj (pliki z obrazu zostaną zastąpione pakietami z repo): sudo pag base adopt",
8
+ "base_adopt_plan": "Adoptowanie {} pakietów bazy z repo (~{:.0f} MB):",
9
+ "base_adoptable": "Do adoptowania z repo",
10
+ "base_all_managed": "Wszystkie pakiety bazy są już zarządzane przez pag.",
11
+ "base_dry_run": "Próba na sucho – nic nie zainstalowano.",
12
+ "base_iso_version": "Wersja obrazu",
13
+ "base_more": "więcej",
14
+ "base_no_manifest": "Ten system nie ma manifestu bazy (/usr/share/pag/base.json) – nie ma czego adoptować.",
15
+ "base_nothing_selected": "Żadna z podanych nazw nie jest na liście do adoptowania.",
16
+ "base_overwrite": "{} pochodzi z obrazu ISO – pliki zostaną zastąpione (kopia zapasowa dla 'pag rollback').",
17
+ "base_reboot_hint": "Baza zaadoptowana z repo – zrestartuj system, zanim zaczniesz używać nowych bibliotek/narzędzi.",
18
+ "base_title": "Baza z obrazu ISO",
19
+ "base_total": "Pakiety bazy (z obrazu)",
20
+ "base_tracked": "Zarządzane przez pag",
21
+ "base_unmanaged_hint": "{} pakietów z obrazu ISO nie jest jeszcze zarządzanych przez pag – 'sudo pag base adopt' zamieni je na pakiety z repo (dopiero wtedy 'pag upgrade' je aktualizuje).",
22
+ "base_unmatched": "Bez odpowiednika w repo",
23
+ "base_unmatched_hint": "Te zostaną w wersji z obrazu (brak takiego pakietu w repo):",
24
+ "cache_cleared": "{} plików ({:.2f} MB) usuniętych z cache.",
25
+ "cache_ro": "Cache repozytoriów jest tylko-do-odczytu ({cache}) – używam lokalnego indeksu (może być nieaktualny).\n Odśwież jako root: sudo pag sync",
26
+ "cancelled": "Anulowano.",
27
+ "conf_pacnew": "Zmieniony plik konfiguracyjny zachowany; nowa wersja: {path}",
28
+ "conf_pacsave": "Zmieniony plik konfiguracyjny zachowany jako {path}",
29
+ "continue_q": "Kontynuować? [T/n] ",
30
+ "db_lock_hint": "Jeśli żaden inny proces pag nie działa, poczekaj chwilę i spróbuj ponownie.",
31
+ "db_locked": "Inna instancja pag jest uruchomiona.",
32
+ "deploy_cleanup_none": "Nie ma deploymentów do wyczyszczenia (minimum {}).",
33
+ "deploy_cleanup_ok": "Usunięto {} starych deploymentów.",
34
+ "deploy_rollback_fail": "Brak poprzedniego deploymentu.",
35
+ "deploy_rollback_ok": "Przełączono na deployment: {}",
36
+ "deployments_list": "Deploymenty ({}):",
37
+ "download_fail": "błąd pobierania",
38
+ "downloaded": "Pobrano {} do cache ({:.2f} MB).",
39
+ "downloading": "Pobieranie",
40
+ "flatpak_adding": "Dodaję zdalne repozytorium Flathub...",
41
+ "flatpak_found": "Znaleziono {} wyników:",
42
+ "flatpak_info_branch": "Gałąź",
43
+ "flatpak_info_desc": "Opis",
44
+ "flatpak_info_id": "ID",
45
+ "flatpak_info_origin": "Źródło",
46
+ "flatpak_info_size": "Rozmiar",
47
+ "flatpak_info_version": "Wersja",
48
+ "flatpak_install_prompt": "Zainstalować {}? [T/n] ",
49
+ "flatpak_installed": "Flatpak {} zainstalowany.",
50
+ "flatpak_installing": "Instalowanie {}...",
51
+ "flatpak_menu_fix": "Dodaj /var/lib/flatpak/exports/share do XDG_DATA_DIRS (a .../exports/bin do PATH) w sesji i zaloguj się ponownie – PaganDE robi to w session/pagande-session.",
52
+ "flatpak_menu_hint": "Zainstalowano i zweryfikowano, ale sesja może jej nie pokazać w menu: brak eksportów Flatpaka w XDG_DATA_DIRS.",
53
+ "flatpak_missing": "Flatpak nie jest zainstalowany.",
54
+ "flatpak_not_found": "nie znaleziono we Flathub",
55
+ "flatpak_not_installed": "Flatpak {} nie jest zainstalowany.",
56
+ "flatpak_removed": "Flatpak {} usunięty.",
57
+ "flatpak_scope": "Instalacja",
58
+ "flatpak_searching": "Szukam '{}' we Flathub...",
59
+ "flatpak_updated": "Flapaki zaktualizowane.",
60
+ "flatpak_usage": "Użycie: pag flatpak <search|install|remove|list|update|info> [args]",
61
+ "gpg_fail": "błąd weryfikacji GPG",
62
+ "gpg_missing": "BRAK GNUPG – zainstaluj gnupg i spróbuj ponownie",
63
+ "indexes_refreshed": "Indeksy odświeżone.",
64
+ "install_failed": "błąd instalacji",
65
+ "installed": "Zainstalowano {} pakietów.",
66
+ "key_add_failed": "Import klucza nie powiódł się (błąd gpg) – klucz NIE został dodany.",
67
+ "key_imported": "Klucz zaimportowany pomyślnie.",
68
+ "key_removed": "Klucz usunięty: {}",
69
+ "new": "NOWY",
70
+ "no_deployments": "Brak deploymentów.",
71
+ "no_history": "Brak historii transakcji.",
72
+ "no_index": "Nie można pobrać indeksów repozytoriów. Uruchom 'pag update'.",
73
+ "no_keys": "Brak zaufanych kluczy GPG.",
74
+ "no_pinned": "Brak przypiętych pakietów.",
75
+ "no_tty": "Brak terminala (EOF) – anuluję.",
76
+ "not_found": "brak w repozytoriach",
77
+ "not_found_hint": "Sprawdź pisownię lub uruchom 'pag search <fraza>'.",
78
+ "not_pinned": "nie był przypięty.",
79
+ "orphans_found": "Osierocone zależności ({}): {}",
80
+ "pinned_list": "Przypięte pakiety ({}):",
81
+ "pinned_to": "przypięty do",
82
+ "pkg_not_found": "Nie znaleziono pakietu: {} (brak w repozytoriach)",
83
+ "provides_conflict": "Wielu dostawców dla '{name}' ({providers}) – używam '{chosen}'",
84
+ "provides_mapped": "{} → {} (provides)",
85
+ "removing": "Usuwanie",
86
+ "repo_added": "Dodano repozytorium: {}",
87
+ "repo_exists": "Repozytorium już istnieje: {}",
88
+ "rollback_files": "Wycofano {} plików.",
89
+ "rollback_restored": "Przywrócono poprzedni stan z migawki.",
90
+ "root_required": "pag wymaga uprawnień root (sudo).",
91
+ "sec_audit": "{pkg} installed by {user}",
92
+ "sec_audit_pl": "{pkg} zainstalowany przez {user}",
93
+ "sec_badname": "Invalid package name: {name}",
94
+ "sec_badname_pl": "Nieprawidlowa nazwa pakietu: {name}",
95
+ "sec_conflict": "File conflict: {path} owned by {owner}",
96
+ "sec_conflict_pl": "Konflikt plikow: {path} nalezy do {owner}",
97
+ "sec_downgrade": "Downgrade blocked: {pkg} {new} < {old}",
98
+ "sec_downgrade_pl": "Blokada downgrade: {pkg} {new} < {old}",
99
+ "sec_https": "HTTPS required for repos",
100
+ "sec_https_pl": "Repozytorium wymaga HTTPS",
101
+ "sec_locked": "Another pag process is running",
102
+ "sec_locked_pl": "Inny proces pag juz dziala",
103
+ "sec_suid": "SUID stripped from {path}",
104
+ "sec_suid_pl": "SUID usuniety z {path}",
105
+ "sec_toobig": "Package too large: {size_mb}MB > {max_mb}MB",
106
+ "sec_toobig_pl": "Paczka za duza: {size_mb}MB > {max_mb}MB",
107
+ "sha256_mismatch": "niezgodność SHA256",
108
+ "ssl_broken": "Python bez działającego modułu ssl (HTTPS niemożliwe) – napraw pakiety python/openssl (np. sudo pag install -f python).",
109
+ "stats_base": "Baza (obraz) zarządzana",
110
+ "stats_cache": "Rozmiar cache",
111
+ "stats_files": "Śledzone pliki",
112
+ "stats_history": "Transakcje",
113
+ "stats_last_update": "Ostatnia aktualizacja",
114
+ "stats_packages": "Zainstalowane pakiety",
115
+ "stats_size": "Całkowity rozmiar",
116
+ "stats_title": "Statystyki PAG",
117
+ "to_install": "Do zainstalowania: {} pakietów ({:.2f} MB)",
118
+ "unpinned": "odpięty.",
119
+ "updated_done": "Odświeżanie zakończone. {} pakietów w cache.",
120
+ "updates_available": "⚠ jest {} pakietów do zaktualizowania – wpisz: pag update",
121
+ "upgrading": "Aktualizacje: {} pakietów",
122
+ "usage": "pag v3 – Pagan Linux Package Manager\n\nPODSTAWOWE:\n pag install <pkg>... Instalacja pakietów\n pag remove <pkg>... Usuwanie pakietów\n pag update Aktualizacja PAKIETÓW (odświeża indeksy)\n pag sync Odśwież indeksy + info o aktualizacjach\n pag upgrade Aktualizacja SYSTEMU (pakiety + kernel/initramfs/GRUB)\n pag list [--installed] Lista dostępnych / zainstalowanych\n pag search <query> Szukaj pakietów\n pag info <pkg> Szczegóły pakietu\n pag files <pkg> Lista plików pakietu\n pag verify [--deep] Weryfikacja integralności\n pag clean Wyczyść cache pobierania\n pag stats Statystyki systemu\n pag download <pkg>... Pobierz do cache (offline)\n\nROLLING (baza wypalona w obrazie):\n pag base [status] Pakiety z obrazu vs repo (czym pag zarządza)\n pag base adopt [pkg...] Adoptuj pakiety bazy z repo (--dry-run = podgląd)\n\nBEZPIECZEŃSTWO:\n pag key-add <url|file> Importuj klucz GPG\n pag key-list Lista zaufanych kluczy\n pag key-remove <id> Usuń klucz\n pag key-trust <repo> Przypnij fingerprint klucza repo (bez TOFU)\n pag key-untrust <repo> Zapomnij fingerprint repo (powrót do TOFU)\n pag key-trusted Lista przypiętych fingerprintów repo\n\nZAAWANSOWANE:\n pag why <pkg> Dlaczego pakiet jest zainstalowany\n pag autoremove Usuń osierocone zależności\n pag pin <pkg> [ver] Przypnij wersję pakietu\n pag unpin <pkg> Odepnij\n pag pinned Lista przypiętych\n pag history Historia transakcji\n pag rollback Cofnij ostatnią transakcję\n pag remove-orphans Usuń osierocone zależności\n pag repo-add <url> [nazwa] Dodaj repozytorium (drop-in w /etc/pag/repos/)\n pag repo-list Lista repozytoriów\n pag sbom export [fmt] Manifest SBOM (spdx|cyclonedx)\n\nFLATPAK:\n pag flatpak [<query>] Szukaj i instaluj\n pag flatpak search <q> Szukaj na Flathub\n pag flatpak install <id> Zainstaluj flatpak\n pag flatpak remove <id> Usuń flatpak\n pag flatpak list Lista zainstalowanych\n pag flatpak update Aktualizuj wszystkie\n pag flatpak info <id> Szczegóły flatpaka\n\nIMMUTABLE OS (PAG_IMMUTABLE=1):\n pag deploy-list Lista wdrożeń\n pag deploy-rollback Przełącz na poprzednie wdrożenie\n pag deploy-cleanup [N] Usuń stare wdrożenia (zachowaj N, domyślnie 3)\n pag initramfs-update Przebuduj initramfs\n pag grub-update Regeneruj wpisy GRUB",
123
+ "verify_errors": "Znaleziono {} problemów:",
124
+ "verify_ok": "Wszystkie {} plików sprawne.",
125
+ "why_dependency": "zależność od",
126
+ "why_explicit": "zainstalowany jawnie",
127
+ "why_not_installed": "niezainstalowany"
128
+}