hmgr 0.0.24 → 0.0.25

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/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
- ipcMain.handle('hmgr:istZustand', async () => auffangen(async () => (await anwendungsfaelle.istZustand()).map(zielZustandSerialisieren)));
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.
@@ -2314,8 +2317,13 @@ export class Anwendungsfaelle {
2314
2317
  gruende.set(name, zeiger?.skills.has(name) === true ? { art: 'zeiger' } : { art: 'manuell' });
2315
2318
  return gruende;
2316
2319
  }
2317
- /** Ist-Zustand: alle Ziele der vorhandenen Harnesses, aus dem Dateisystem gelesen. */
2318
- async istZustand() {
2320
+ /**
2321
+ * Ist-Zustand: alle Ziele der vorhandenen Harnesses, aus dem Dateisystem gelesen.
2322
+ * `plugins: false` lässt die Plugin-Abfrage aus (E93): sie ist ein Prozess je Ziel und für die
2323
+ * GUI, die das Feld nicht zeigt, nur Wartezeit. Dann fehlt `plugins` an allen Ziel-Zuständen.
2324
+ */
2325
+ async istZustand(optionen = {}) {
2326
+ const mitPlugins = optionen.plugins !== false;
2319
2327
  const zustand = await this.p.zustand.lesen();
2320
2328
  // Je Identität die Quelle, unter deren Schreibweise sie geführt wird (E36, E48), für die
2321
2329
  // Übernahme-Prognose. Bleibt über den ganzen Lauf gleich, die Prognose verändert nichts.
@@ -2340,6 +2348,8 @@ export class Anwendungsfaelle {
2340
2348
  return funde;
2341
2349
  };
2342
2350
  const ergebnis = [];
2351
+ // Zielliste einmal bilden: Vorabruf der Plugins und Hauptschleife gehen über dieselben Ziele.
2352
+ const plan = [];
2343
2353
  for (const harness of await this.vorhandeneHarnesses()) {
2344
2354
  const ziele = [...this.homeZiele(harness)];
2345
2355
  if (harness.orte().includes('profil') && kannProfile(harness))
@@ -2348,14 +2358,32 @@ export class Anwendungsfaelle {
2348
2358
  if (harness.orte().includes('arbeitsverzeichnis'))
2349
2359
  for (const pfad of zustand.arbeitsverzeichnisse)
2350
2360
  ziele.push(arbeitsverzeichnisZiel(harness.name, pfad));
2361
+ plan.push({ harness, ziele });
2362
+ }
2363
+ // Installierte Plugins je Ziel vorab, mit begrenzter Parallelität (E93): `claude plugin list`
2364
+ // ist ein Prozess je Ziel (cwd-gebunden, kein Sammelaufruf), nacheinander dauert das bei vielen
2365
+ // Arbeitsverzeichnissen Sekunden. Plugins nur am primären Homedir (E78): die claude-CLI dieses
2366
+ // Rechners kennt die Plugin-Konfiguration einer WSL-Distro nicht, ihre user-Liste gehörte zum
2367
+ // Windows-Home.
2368
+ const pluginListen = new Map();
2369
+ if (mitPlugins) {
2370
+ const abfragen = [];
2371
+ for (const { harness, ziele } of plan)
2372
+ if (kannPlugins(harness))
2373
+ for (const ziel of ziele)
2374
+ if (!(ziel.ort.art === 'home' && ziel.ort.heim !== undefined))
2375
+ abfragen.push({
2376
+ schluessel: zielSchluessel(ziel),
2377
+ abruf: () => harness.plugins.installierte(ziel).catch(() => []),
2378
+ });
2379
+ const listen = await mitBegrenzung(PLUGIN_ABFRAGEN_GLEICHZEITIG, abfragen.map((a) => a.abruf));
2380
+ abfragen.forEach((a, i) => pluginListen.set(a.schluessel, listen[i]));
2381
+ }
2382
+ for (const { harness, ziele } of plan) {
2351
2383
  for (const ziel of ziele) {
2352
2384
  const verzeichnis = harness.zielVerzeichnis(ziel.ort);
2353
2385
  const eintraege = await this.p.ziele.eintraege(ziel, verzeichnis).catch(() => []);
2354
- // Plugins nur am primären Homedir (E78): die claude-CLI dieses Rechners kennt die
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;
2386
+ const plugins = pluginListen.get(zielSchluessel(ziel));
2359
2387
  const zeiger = await this.zeigerFuerZiel(ziel);
2360
2388
  const istRegistriert = kannIstRegistriert(harness) ? await harness.istRegistriert(ziel.ort) : false;
2361
2389
  // 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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hmgr",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "description": "Harness Manager: Skills und Plugins für AI-Harnesses verwalten",
5
5
  "license": "MIT",
6
6
  "author": "Tim Kaufmann",