@camstack/system 1.0.2 → 1.0.4
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/dist/index.js +75 -0
- package/dist/index.mjs +75 -0
- package/dist/kernel/addon-installer.d.ts +11 -0
- package/dist/kernel/addon-manifest.d.ts +21 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3473,6 +3473,66 @@ var AddonManifest = class {
|
|
|
3473
3473
|
this.cache = null;
|
|
3474
3474
|
}
|
|
3475
3475
|
/**
|
|
3476
|
+
* Reconcile the manifest with what is actually present on disk.
|
|
3477
|
+
*
|
|
3478
|
+
* For every `@scope/<pkg>` directory under `addonsDir` that has a
|
|
3479
|
+
* readable `package.json` but NO manifest entry yet, add one with
|
|
3480
|
+
* source `'seed'`. Existing entries are left untouched (their source,
|
|
3481
|
+
* version and timestamps are authoritative — a later install/update
|
|
3482
|
+
* overwrites them through `upsert`).
|
|
3483
|
+
*
|
|
3484
|
+
* Why this exists: addons baked into the Docker/Electron image are
|
|
3485
|
+
* copied straight into `addonsDir` by the container entrypoint (the
|
|
3486
|
+
* launcher's `ensureRequiredPackages` then sees `package.json` already
|
|
3487
|
+
* present and skips, never calling `upsert`). Without this reconcile
|
|
3488
|
+
* those seeded addons stay absent from the manifest, so `applyUpdate`
|
|
3489
|
+
* rejects them with "not currently tracked in manifest" — which is
|
|
3490
|
+
* exactly what broke "Update all" on a fresh image. Idempotent and
|
|
3491
|
+
* safe to run on every boot.
|
|
3492
|
+
*
|
|
3493
|
+
* Returns the number of entries added.
|
|
3494
|
+
*/
|
|
3495
|
+
reconcileFromDisk(addonsDir) {
|
|
3496
|
+
if (!node_fs.existsSync(addonsDir)) return 0;
|
|
3497
|
+
const m = this.read();
|
|
3498
|
+
let added = 0;
|
|
3499
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3500
|
+
for (const scope of node_fs.readdirSync(addonsDir, { withFileTypes: true })) {
|
|
3501
|
+
if (!scope.isDirectory() || !scope.name.startsWith("@")) continue;
|
|
3502
|
+
const scopeDir = node_path.join(addonsDir, scope.name);
|
|
3503
|
+
for (const pkg of node_fs.readdirSync(scopeDir, { withFileTypes: true })) {
|
|
3504
|
+
if (!pkg.isDirectory()) continue;
|
|
3505
|
+
const pkgJsonPath = node_path.join(scopeDir, pkg.name, "package.json");
|
|
3506
|
+
if (!node_fs.existsSync(pkgJsonPath)) continue;
|
|
3507
|
+
let pkgJson;
|
|
3508
|
+
try {
|
|
3509
|
+
const parsed = JSON.parse(node_fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
3510
|
+
if (typeof parsed !== "object" || parsed == null) continue;
|
|
3511
|
+
pkgJson = parsed;
|
|
3512
|
+
} catch {
|
|
3513
|
+
continue;
|
|
3514
|
+
}
|
|
3515
|
+
if (!pkgJson.name || !pkgJson.version) continue;
|
|
3516
|
+
const addons = pkgJson.camstack?.addons;
|
|
3517
|
+
if (!Array.isArray(addons) || addons.length === 0) continue;
|
|
3518
|
+
if (m.addons[pkgJson.name] != null) continue;
|
|
3519
|
+
m.addons[pkgJson.name] = {
|
|
3520
|
+
name: pkgJson.name,
|
|
3521
|
+
version: pkgJson.version,
|
|
3522
|
+
source: "seed",
|
|
3523
|
+
installedAt: now,
|
|
3524
|
+
updatedAt: now
|
|
3525
|
+
};
|
|
3526
|
+
added++;
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
if (added > 0) {
|
|
3530
|
+
m.updatedAt = now;
|
|
3531
|
+
this.write(m);
|
|
3532
|
+
}
|
|
3533
|
+
return added;
|
|
3534
|
+
}
|
|
3535
|
+
/**
|
|
3476
3536
|
* Migration helper: if the manifest is empty but the addons directory
|
|
3477
3537
|
* has packages with `.install-source` markers (the legacy per-addon
|
|
3478
3538
|
* tracking), seed the manifest from those markers. Runs once at boot.
|
|
@@ -3846,6 +3906,21 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3846
3906
|
}
|
|
3847
3907
|
}
|
|
3848
3908
|
/**
|
|
3909
|
+
* Reconcile the install manifest with the addons actually present on
|
|
3910
|
+
* disk. Seeded addons (baked into the image and copied verbatim by the
|
|
3911
|
+
* container entrypoint) never pass through an `install*` codepath, so
|
|
3912
|
+
* they are missing from `manifest.json` and `applyUpdate` would reject
|
|
3913
|
+
* them with "not currently tracked in manifest". Call this once after
|
|
3914
|
+
* `ensureRequiredPackages` so every on-disk addon is updatable.
|
|
3915
|
+
*
|
|
3916
|
+
* Returns the number of manifest entries added.
|
|
3917
|
+
*/
|
|
3918
|
+
reconcileManifest() {
|
|
3919
|
+
const added = this.manifest.reconcileFromDisk(this.addonsDir);
|
|
3920
|
+
if (added > 0) this.logger.info(`Manifest reconciled — ${added} seeded addon(s) registered`);
|
|
3921
|
+
return added;
|
|
3922
|
+
}
|
|
3923
|
+
/**
|
|
3849
3924
|
* Install a single addon — on-demand installer used by AddonPackageService
|
|
3850
3925
|
* (admin UI "Install addon" / "Reinstall" buttons).
|
|
3851
3926
|
*
|
package/dist/index.mjs
CHANGED
|
@@ -3467,6 +3467,66 @@ var AddonManifest = class {
|
|
|
3467
3467
|
this.cache = null;
|
|
3468
3468
|
}
|
|
3469
3469
|
/**
|
|
3470
|
+
* Reconcile the manifest with what is actually present on disk.
|
|
3471
|
+
*
|
|
3472
|
+
* For every `@scope/<pkg>` directory under `addonsDir` that has a
|
|
3473
|
+
* readable `package.json` but NO manifest entry yet, add one with
|
|
3474
|
+
* source `'seed'`. Existing entries are left untouched (their source,
|
|
3475
|
+
* version and timestamps are authoritative — a later install/update
|
|
3476
|
+
* overwrites them through `upsert`).
|
|
3477
|
+
*
|
|
3478
|
+
* Why this exists: addons baked into the Docker/Electron image are
|
|
3479
|
+
* copied straight into `addonsDir` by the container entrypoint (the
|
|
3480
|
+
* launcher's `ensureRequiredPackages` then sees `package.json` already
|
|
3481
|
+
* present and skips, never calling `upsert`). Without this reconcile
|
|
3482
|
+
* those seeded addons stay absent from the manifest, so `applyUpdate`
|
|
3483
|
+
* rejects them with "not currently tracked in manifest" — which is
|
|
3484
|
+
* exactly what broke "Update all" on a fresh image. Idempotent and
|
|
3485
|
+
* safe to run on every boot.
|
|
3486
|
+
*
|
|
3487
|
+
* Returns the number of entries added.
|
|
3488
|
+
*/
|
|
3489
|
+
reconcileFromDisk(addonsDir) {
|
|
3490
|
+
if (!fs$18.existsSync(addonsDir)) return 0;
|
|
3491
|
+
const m = this.read();
|
|
3492
|
+
let added = 0;
|
|
3493
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3494
|
+
for (const scope of fs$18.readdirSync(addonsDir, { withFileTypes: true })) {
|
|
3495
|
+
if (!scope.isDirectory() || !scope.name.startsWith("@")) continue;
|
|
3496
|
+
const scopeDir = path$40.join(addonsDir, scope.name);
|
|
3497
|
+
for (const pkg of fs$18.readdirSync(scopeDir, { withFileTypes: true })) {
|
|
3498
|
+
if (!pkg.isDirectory()) continue;
|
|
3499
|
+
const pkgJsonPath = path$40.join(scopeDir, pkg.name, "package.json");
|
|
3500
|
+
if (!fs$18.existsSync(pkgJsonPath)) continue;
|
|
3501
|
+
let pkgJson;
|
|
3502
|
+
try {
|
|
3503
|
+
const parsed = JSON.parse(fs$18.readFileSync(pkgJsonPath, "utf-8"));
|
|
3504
|
+
if (typeof parsed !== "object" || parsed == null) continue;
|
|
3505
|
+
pkgJson = parsed;
|
|
3506
|
+
} catch {
|
|
3507
|
+
continue;
|
|
3508
|
+
}
|
|
3509
|
+
if (!pkgJson.name || !pkgJson.version) continue;
|
|
3510
|
+
const addons = pkgJson.camstack?.addons;
|
|
3511
|
+
if (!Array.isArray(addons) || addons.length === 0) continue;
|
|
3512
|
+
if (m.addons[pkgJson.name] != null) continue;
|
|
3513
|
+
m.addons[pkgJson.name] = {
|
|
3514
|
+
name: pkgJson.name,
|
|
3515
|
+
version: pkgJson.version,
|
|
3516
|
+
source: "seed",
|
|
3517
|
+
installedAt: now,
|
|
3518
|
+
updatedAt: now
|
|
3519
|
+
};
|
|
3520
|
+
added++;
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
if (added > 0) {
|
|
3524
|
+
m.updatedAt = now;
|
|
3525
|
+
this.write(m);
|
|
3526
|
+
}
|
|
3527
|
+
return added;
|
|
3528
|
+
}
|
|
3529
|
+
/**
|
|
3470
3530
|
* Migration helper: if the manifest is empty but the addons directory
|
|
3471
3531
|
* has packages with `.install-source` markers (the legacy per-addon
|
|
3472
3532
|
* tracking), seed the manifest from those markers. Runs once at boot.
|
|
@@ -3840,6 +3900,21 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3840
3900
|
}
|
|
3841
3901
|
}
|
|
3842
3902
|
/**
|
|
3903
|
+
* Reconcile the install manifest with the addons actually present on
|
|
3904
|
+
* disk. Seeded addons (baked into the image and copied verbatim by the
|
|
3905
|
+
* container entrypoint) never pass through an `install*` codepath, so
|
|
3906
|
+
* they are missing from `manifest.json` and `applyUpdate` would reject
|
|
3907
|
+
* them with "not currently tracked in manifest". Call this once after
|
|
3908
|
+
* `ensureRequiredPackages` so every on-disk addon is updatable.
|
|
3909
|
+
*
|
|
3910
|
+
* Returns the number of manifest entries added.
|
|
3911
|
+
*/
|
|
3912
|
+
reconcileManifest() {
|
|
3913
|
+
const added = this.manifest.reconcileFromDisk(this.addonsDir);
|
|
3914
|
+
if (added > 0) this.logger.info(`Manifest reconciled — ${added} seeded addon(s) registered`);
|
|
3915
|
+
return added;
|
|
3916
|
+
}
|
|
3917
|
+
/**
|
|
3843
3918
|
* Install a single addon — on-demand installer used by AddonPackageService
|
|
3844
3919
|
* (admin UI "Install addon" / "Reinstall" buttons).
|
|
3845
3920
|
*
|
|
@@ -86,6 +86,17 @@ export declare class AddonInstaller {
|
|
|
86
86
|
* @param packages — optional custom package list (default: REQUIRED_PACKAGES)
|
|
87
87
|
*/
|
|
88
88
|
ensureRequiredPackages(packages?: readonly string[]): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Reconcile the install manifest with the addons actually present on
|
|
91
|
+
* disk. Seeded addons (baked into the image and copied verbatim by the
|
|
92
|
+
* container entrypoint) never pass through an `install*` codepath, so
|
|
93
|
+
* they are missing from `manifest.json` and `applyUpdate` would reject
|
|
94
|
+
* them with "not currently tracked in manifest". Call this once after
|
|
95
|
+
* `ensureRequiredPackages` so every on-disk addon is updatable.
|
|
96
|
+
*
|
|
97
|
+
* Returns the number of manifest entries added.
|
|
98
|
+
*/
|
|
99
|
+
reconcileManifest(): number;
|
|
89
100
|
/**
|
|
90
101
|
* Install a single addon — on-demand installer used by AddonPackageService
|
|
91
102
|
* (admin UI "Install addon" / "Reinstall" buttons).
|
|
@@ -66,6 +66,27 @@ export declare class AddonManifest {
|
|
|
66
66
|
* Useful when an external process may have written the manifest.
|
|
67
67
|
*/
|
|
68
68
|
invalidate(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Reconcile the manifest with what is actually present on disk.
|
|
71
|
+
*
|
|
72
|
+
* For every `@scope/<pkg>` directory under `addonsDir` that has a
|
|
73
|
+
* readable `package.json` but NO manifest entry yet, add one with
|
|
74
|
+
* source `'seed'`. Existing entries are left untouched (their source,
|
|
75
|
+
* version and timestamps are authoritative — a later install/update
|
|
76
|
+
* overwrites them through `upsert`).
|
|
77
|
+
*
|
|
78
|
+
* Why this exists: addons baked into the Docker/Electron image are
|
|
79
|
+
* copied straight into `addonsDir` by the container entrypoint (the
|
|
80
|
+
* launcher's `ensureRequiredPackages` then sees `package.json` already
|
|
81
|
+
* present and skips, never calling `upsert`). Without this reconcile
|
|
82
|
+
* those seeded addons stay absent from the manifest, so `applyUpdate`
|
|
83
|
+
* rejects them with "not currently tracked in manifest" — which is
|
|
84
|
+
* exactly what broke "Update all" on a fresh image. Idempotent and
|
|
85
|
+
* safe to run on every boot.
|
|
86
|
+
*
|
|
87
|
+
* Returns the number of entries added.
|
|
88
|
+
*/
|
|
89
|
+
reconcileFromDisk(addonsDir: string): number;
|
|
69
90
|
/**
|
|
70
91
|
* Migration helper: if the manifest is empty but the addons directory
|
|
71
92
|
* has packages with `.install-source` markers (the legacy per-addon
|