hmgr 0.0.24 → 0.0.26
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/cli/main.js +2 -1
- package/dist/gui/main.js +3 -1
- package/dist/kern/anwendungsfaelle.js +37 -8
- package/dist/kern/begrenzung.js +27 -0
- package/package.json +1 -1
package/dist/cli/main.js
CHANGED
|
@@ -990,7 +990,8 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
990
990
|
// Säubern-Angebot nur, wenn eigene Konfiguration liegt, mit derselben Bedingung wie die GUI
|
|
991
991
|
// (E38, GRILL.md Entwurfskritik 4): eigene Links oder eine liegen gebliebene Registrierung
|
|
992
992
|
// ohne Link (E38 R7), sonst machten CLI und GUI verschiedene Angebote.
|
|
993
|
-
|
|
993
|
+
// Ohne Plugin-Abfrage (E94): gelesen werden nur Harness, Einträge und Registrierung.
|
|
994
|
+
const ziele = (await anwendungsfaelle.istZustand({ plugins: false })).filter((z) => z.ziel.harness === name &&
|
|
994
995
|
(z.eintraege.some((eintrag) => eintrag.art !== 'fremd') || z.istRegistriert));
|
|
995
996
|
if (ziele.length === 0)
|
|
996
997
|
return 0;
|
package/dist/gui/main.js
CHANGED
|
@@ -187,7 +187,9 @@ function registriereIpc() {
|
|
|
187
187
|
// Ebenfalls nur für die Anzeige (E68): der Renderer leitet daraus den Pfadtrenner ab, mit dem er
|
|
188
188
|
// eigene Anzeigepfade zusammensetzt („~/.claude.json" bzw. „~\.claude.json").
|
|
189
189
|
ipcMain.handle('hmgr:plattform', async () => auffangen(async () => process.platform));
|
|
190
|
-
|
|
190
|
+
// Ohne Plugin-Abfrage (E93): der Renderer liest `plugins` aus dem Ziel-Zustand nirgends, und die
|
|
191
|
+
// Abfrage kostet einen claude-Prozess je Ziel.
|
|
192
|
+
ipcMain.handle('hmgr:istZustand', async () => auffangen(async () => (await anwendungsfaelle.istZustand({ plugins: false })).map(zielZustandSerialisieren)));
|
|
191
193
|
ipcMain.handle('hmgr:marketplaces', async () => auffangen(async () => (await anwendungsfaelle.marketplaces()).map((m) => ({
|
|
192
194
|
quelleText: quelleAlsText(m.quelle),
|
|
193
195
|
name: m.name,
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// Anwendungsfälle. Pfade werden hier nur als Zeichenketten normalisiert, gelesen wird über die Ports.
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { AbhaengigkeitFehlt, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, FremderEintragUnbekannt, HarnessInaktiv, HarnessNichtVorhanden, HarnessTypUnbekannt, HeimAdresseUngueltig, HeimBereitsVorhanden, HeimFaehigkeitFehlt, HeimNurUnterWindows, HeimUnbekannt, KeinPassenderRef, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpOrtUnterGitWurzel, McpUebernahmeNichtMoeglich, McpUnbekannt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, bundleSchluessel, heimAdresseAlsText, heimAdresseZerlegen, marketplaceUrl, pluginSchluessel, pluginSchluesselZerlegen, quelleAlsText, quelleMitNamen, quelleName, quelleNameAbleiten, quelleNameFrei, quelleSchluessel, zielSchluessel, } from "./modell.js";
|
|
4
|
+
import { mitBegrenzung } from "./begrenzung.js";
|
|
4
5
|
import { passenderRef } from "./version.js";
|
|
5
6
|
import { kannIstRegistriert, kannMcp, kannPlugins, kannProfile, kannSkillOrdner, kannZurueckbauen, kannZusaetzlicheHeime, } from "../ports/harness.js";
|
|
7
|
+
/** So viele `plugin list`-Prozesse laufen im Ist-Zustand höchstens gleichzeitig (E93). */
|
|
8
|
+
const PLUGIN_ABFRAGEN_GLEICHZEITIG = 6;
|
|
6
9
|
/**
|
|
7
10
|
* Frage vor dem Löschen, eine für alle Kandidaten (E28). Der Kern formuliert sie fertig, der
|
|
8
11
|
* Adapter stellt sie: Anzahl und Pfade, damit der Nutzer sieht, worüber er entscheidet.
|
|
@@ -1995,10 +1998,11 @@ export class Anwendungsfaelle {
|
|
|
1995
1998
|
* Pfade, in die ein Link zeigt. Gelesen wird wie beim Ist-Zustand (E19): die Ziele der
|
|
1996
1999
|
* vorhandenen Harnesses, die Arbeitsverzeichnisse aus dem Zustand. Ein Link, dessen Quelle nicht
|
|
1997
2000
|
* im Zustand steht, zeigt trotzdem in einen Checkout und hält ihn.
|
|
2001
|
+
* Gelesen werden nur die Einträge, deshalb ohne Plugin-Abfrage (ein Prozess je Ziel, E94).
|
|
1998
2002
|
*/
|
|
1999
2003
|
async belegteCheckoutPfade() {
|
|
2000
2004
|
const pfade = new Set();
|
|
2001
|
-
for (const zielZustand of await this.istZustand())
|
|
2005
|
+
for (const zielZustand of await this.istZustand({ plugins: false }))
|
|
2002
2006
|
for (const eintrag of zielZustand.eintraege) {
|
|
2003
2007
|
if (eintrag.art === 'link')
|
|
2004
2008
|
pfade.add(path.resolve(eintrag.link.checkout.pfad));
|
|
@@ -2314,8 +2318,13 @@ export class Anwendungsfaelle {
|
|
|
2314
2318
|
gruende.set(name, zeiger?.skills.has(name) === true ? { art: 'zeiger' } : { art: 'manuell' });
|
|
2315
2319
|
return gruende;
|
|
2316
2320
|
}
|
|
2317
|
-
/**
|
|
2318
|
-
|
|
2321
|
+
/**
|
|
2322
|
+
* Ist-Zustand: alle Ziele der vorhandenen Harnesses, aus dem Dateisystem gelesen.
|
|
2323
|
+
* `plugins: false` lässt die Plugin-Abfrage aus (E93): sie ist ein Prozess je Ziel und für die
|
|
2324
|
+
* GUI, die das Feld nicht zeigt, nur Wartezeit. Dann fehlt `plugins` an allen Ziel-Zuständen.
|
|
2325
|
+
*/
|
|
2326
|
+
async istZustand(optionen = {}) {
|
|
2327
|
+
const mitPlugins = optionen.plugins !== false;
|
|
2319
2328
|
const zustand = await this.p.zustand.lesen();
|
|
2320
2329
|
// Je Identität die Quelle, unter deren Schreibweise sie geführt wird (E36, E48), für die
|
|
2321
2330
|
// Übernahme-Prognose. Bleibt über den ganzen Lauf gleich, die Prognose verändert nichts.
|
|
@@ -2340,6 +2349,8 @@ export class Anwendungsfaelle {
|
|
|
2340
2349
|
return funde;
|
|
2341
2350
|
};
|
|
2342
2351
|
const ergebnis = [];
|
|
2352
|
+
// Zielliste einmal bilden: Vorabruf der Plugins und Hauptschleife gehen über dieselben Ziele.
|
|
2353
|
+
const plan = [];
|
|
2343
2354
|
for (const harness of await this.vorhandeneHarnesses()) {
|
|
2344
2355
|
const ziele = [...this.homeZiele(harness)];
|
|
2345
2356
|
if (harness.orte().includes('profil') && kannProfile(harness))
|
|
@@ -2348,14 +2359,32 @@ export class Anwendungsfaelle {
|
|
|
2348
2359
|
if (harness.orte().includes('arbeitsverzeichnis'))
|
|
2349
2360
|
for (const pfad of zustand.arbeitsverzeichnisse)
|
|
2350
2361
|
ziele.push(arbeitsverzeichnisZiel(harness.name, pfad));
|
|
2362
|
+
plan.push({ harness, ziele });
|
|
2363
|
+
}
|
|
2364
|
+
// Installierte Plugins je Ziel vorab, mit begrenzter Parallelität (E93): `claude plugin list`
|
|
2365
|
+
// ist ein Prozess je Ziel (cwd-gebunden, kein Sammelaufruf), nacheinander dauert das bei vielen
|
|
2366
|
+
// Arbeitsverzeichnissen Sekunden. Plugins nur am primären Homedir (E78): die claude-CLI dieses
|
|
2367
|
+
// Rechners kennt die Plugin-Konfiguration einer WSL-Distro nicht, ihre user-Liste gehörte zum
|
|
2368
|
+
// Windows-Home.
|
|
2369
|
+
const pluginListen = new Map();
|
|
2370
|
+
if (mitPlugins) {
|
|
2371
|
+
const abfragen = [];
|
|
2372
|
+
for (const { harness, ziele } of plan)
|
|
2373
|
+
if (kannPlugins(harness))
|
|
2374
|
+
for (const ziel of ziele)
|
|
2375
|
+
if (!(ziel.ort.art === 'home' && ziel.ort.heim !== undefined))
|
|
2376
|
+
abfragen.push({
|
|
2377
|
+
schluessel: zielSchluessel(ziel),
|
|
2378
|
+
abruf: () => harness.plugins.installierte(ziel).catch(() => []),
|
|
2379
|
+
});
|
|
2380
|
+
const listen = await mitBegrenzung(PLUGIN_ABFRAGEN_GLEICHZEITIG, abfragen.map((a) => a.abruf));
|
|
2381
|
+
abfragen.forEach((a, i) => pluginListen.set(a.schluessel, listen[i]));
|
|
2382
|
+
}
|
|
2383
|
+
for (const { harness, ziele } of plan) {
|
|
2351
2384
|
for (const ziel of ziele) {
|
|
2352
2385
|
const verzeichnis = harness.zielVerzeichnis(ziel.ort);
|
|
2353
2386
|
const eintraege = await this.p.ziele.eintraege(ziel, verzeichnis).catch(() => []);
|
|
2354
|
-
|
|
2355
|
-
// Plugin-Konfiguration einer WSL-Distro nicht, ihre user-Liste gehörte zum Windows-Home.
|
|
2356
|
-
const plugins = kannPlugins(harness) && !(ziel.ort.art === 'home' && ziel.ort.heim !== undefined)
|
|
2357
|
-
? await harness.plugins.installierte(ziel).catch(() => [])
|
|
2358
|
-
: undefined;
|
|
2387
|
+
const plugins = pluginListen.get(zielSchluessel(ziel));
|
|
2359
2388
|
const zeiger = await this.zeigerFuerZiel(ziel);
|
|
2360
2389
|
const istRegistriert = kannIstRegistriert(harness) ? await harness.istRegistriert(ziel.ort) : false;
|
|
2361
2390
|
// Fähigkeit am Typwächter, nicht am Namen (E16), und nur an Orten, die sie kennt.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Führt Aufgaben mit höchstens `grenze` gleichzeitig laufenden aus (E93). Die Ergebnisse stehen
|
|
3
|
+
* in der Reihenfolge der Aufgaben, nicht der Fertigstellung. Kein Fehlerschutz: wirft eine
|
|
4
|
+
* Aufgabe, bricht der Aufruf mit diesem Fehler ab und startet keine weiteren Aufgaben. Aufrufer,
|
|
5
|
+
* die einzelne Ausfälle hinnehmen wollen, setzen das `.catch` in der Aufgabe selbst.
|
|
6
|
+
*/
|
|
7
|
+
export async function mitBegrenzung(grenze, aufgaben) {
|
|
8
|
+
if (!Number.isInteger(grenze) || grenze < 1)
|
|
9
|
+
throw new RangeError(`Grenze muss eine ganze Zahl ab 1 sein, war ${grenze}`);
|
|
10
|
+
const ergebnisse = new Array(aufgaben.length);
|
|
11
|
+
let naechste = 0;
|
|
12
|
+
let abgebrochen = false;
|
|
13
|
+
const arbeiter = async () => {
|
|
14
|
+
while (!abgebrochen && naechste < aufgaben.length) {
|
|
15
|
+
const index = naechste++;
|
|
16
|
+
try {
|
|
17
|
+
ergebnisse[index] = await aufgaben[index]();
|
|
18
|
+
}
|
|
19
|
+
catch (fehler) {
|
|
20
|
+
abgebrochen = true;
|
|
21
|
+
throw fehler;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
await Promise.all(Array.from({ length: Math.min(grenze, aufgaben.length) }, arbeiter));
|
|
26
|
+
return ergebnisse;
|
|
27
|
+
}
|