@alfe.ai/integrations 0.0.26 → 0.0.28

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.d.ts CHANGED
@@ -193,6 +193,11 @@ declare class Installer {
193
193
  * Node module resolution from hook scripts (e.g.
194
194
  * ~/.alfe/integrations/console/hooks/post_install.js) walks up to
195
195
  * ~/.alfe/integrations/node_modules/ and finds these packages.
196
+ *
197
+ * Uses `"latest"` versions — npm install is always run when node_modules
198
+ * exists to pick up newer published versions (e.g. new API client methods).
199
+ * The in-memory `sharedPackagesReady` flag prevents redundant installs
200
+ * within a single daemon process lifetime.
196
201
  */
197
202
  private ensureSharedPackages;
198
203
  /**
@@ -240,6 +245,8 @@ interface ConfigSchemaField {
240
245
  select_options?: SelectOption[];
241
246
  /** OAuth provider identifier for oauth_connect fields */
242
247
  oauth_provider?: string;
248
+ /** If true, this field is not shown in the dashboard UI (install wizard or configure modal) */
249
+ hidden?: boolean;
243
250
  /** Only show this field if another field has a truthy value (string) or matches a specific value (object) */
244
251
  depends_on_field?: string | {
245
252
  key: string;
@@ -374,6 +381,8 @@ interface IntegrationStateEntry {
374
381
  installedAt: string;
375
382
  config: Record<string, unknown>;
376
383
  error?: string;
384
+ /** Number of consecutive auto-reinstall attempts. Reset on success or explicit reinstall. */
385
+ reinstallAttempts?: number;
377
386
  }
378
387
  interface IntegrationsStateFile {
379
388
  version: number;
@@ -389,7 +398,9 @@ interface RuntimeApplier {
389
398
  /** The runtime identifier (e.g. 'openclaw', 'nanoclaw') */
390
399
  readonly runtime: string;
391
400
  /** Install a plugin package into this runtime */
392
- applyPlugin(pkg: string, integrationInstallPath: string): Promise<void>;
401
+ applyPlugin(pkg: string, integrationInstallPath: string, opts?: {
402
+ force?: boolean;
403
+ }): Promise<void>;
393
404
  /** Remove a plugin package from this runtime */
394
405
  removePlugin(pkg: string): Promise<void>;
395
406
  /** Copy a skill directory into this runtime's skills location */
@@ -517,7 +528,9 @@ declare class IntegrationManager {
517
528
  * 4. Run health_check hook if present
518
529
  * 5. Mark as active in state
519
530
  */
520
- activate(integrationId: string): Promise<ManagerResponse>;
531
+ activate(integrationId: string, opts?: {
532
+ forcePlugins?: boolean;
533
+ }): Promise<ManagerResponse>;
521
534
  /**
522
535
  * Deactivate a running integration.
523
536
  *
@@ -560,6 +573,18 @@ declare class IntegrationManager {
560
573
  * Returns false if the state says installed but files are missing (corrupted install).
561
574
  */
562
575
  isInstallIntact(integrationId: string): boolean;
576
+ /**
577
+ * Get the number of consecutive auto-reinstall attempts for an integration.
578
+ */
579
+ getReinstallAttempts(integrationId: string): number;
580
+ /**
581
+ * Increment the auto-reinstall attempt counter.
582
+ */
583
+ incrementReinstallAttempts(integrationId: string): void;
584
+ /**
585
+ * Reset the auto-reinstall attempt counter.
586
+ */
587
+ resetReinstallAttempts(integrationId: string): void;
563
588
  /**
564
589
  * Get a specific integration by name.
565
590
  */
@@ -630,6 +655,14 @@ declare class StateManager {
630
655
  * Check if an integration is in a given status.
631
656
  */
632
657
  hasStatus(name: string, ...statuses: IntegrationStatus[]): boolean;
658
+ /**
659
+ * Increment the auto-reinstall attempt counter.
660
+ */
661
+ incrementReinstallAttempts(name: string): void;
662
+ /**
663
+ * Reset the auto-reinstall attempt counter (on success or explicit reinstall).
664
+ */
665
+ resetReinstallAttempts(name: string): void;
633
666
  }
634
667
  //#endregion
635
668
  //#region src/hooks.d.ts
@@ -728,7 +761,9 @@ declare class OpenClawApplier implements RuntimeApplier {
728
761
  private skillsDir;
729
762
  private trackingPath;
730
763
  constructor(options: OpenClawApplierOptions);
731
- applyPlugin(pkg: string): Promise<void>;
764
+ applyPlugin(pkg: string, _installPath?: string, opts?: {
765
+ force?: boolean;
766
+ }): Promise<void>;
732
767
  /**
733
768
  * Ensure the plugin is in plugins.allow in openclaw.json.
734
769
  * Uses `openclaw config set` to avoid clobbering OpenClaw's own file format.
@@ -856,6 +891,12 @@ interface IIntegrationManager {
856
891
  }>;
857
892
  /** Check if an integration's install directory and manifest are intact on disk */
858
893
  isInstallIntact(integrationId: string): Promise<boolean>;
894
+ /** Get the number of consecutive auto-reinstall attempts */
895
+ getReinstallAttempts(integrationId: string): number;
896
+ /** Increment the auto-reinstall attempt counter */
897
+ incrementReinstallAttempts(integrationId: string): void;
898
+ /** Reset the auto-reinstall attempt counter */
899
+ resetReinstallAttempts(integrationId: string): void;
859
900
  }
860
901
  declare class IntegrationManagerAdapter implements IIntegrationManager {
861
902
  private readonly manager;
@@ -876,6 +917,9 @@ declare class IntegrationManagerAdapter implements IIntegrationManager {
876
917
  configApplied: boolean;
877
918
  }>;
878
919
  isInstallIntact(integrationId: string): Promise<boolean>;
920
+ getReinstallAttempts(integrationId: string): number;
921
+ incrementReinstallAttempts(integrationId: string): void;
922
+ resetReinstallAttempts(integrationId: string): void;
879
923
  }
880
924
  //#endregion
881
925
  export { type HookEnvOptions, type HookResult, type IIntegrationManager, type InstalledInfo, Installer, InstallerError, type IntegrationConfigureParams, type IntegrationHealthParams, type IntegrationInfo, type IntegrationInstallParams, IntegrationManager, IntegrationManagerAdapter, type IntegrationManagerOptions, type IntegrationRemoveParams, LockManager, OpenClawApplier, type OpenClawApplierOptions, Registry, type RegistryEntry, type RegistryFetcher, type RegistryIndex, RegistryResolveError, type ResolvedIntegration, Resolver, type RuntimeApplier, type RuntimeDesiredState, type RuntimeLockFile, type RuntimePluginEntry, type RuntimeSkillEntry, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext };
package/dist/index.js CHANGED
@@ -276,24 +276,15 @@ var Installer = class {
276
276
  * Node module resolution from hook scripts (e.g.
277
277
  * ~/.alfe/integrations/console/hooks/post_install.js) walks up to
278
278
  * ~/.alfe/integrations/node_modules/ and finds these packages.
279
+ *
280
+ * Uses `"latest"` versions — npm install is always run when node_modules
281
+ * exists to pick up newer published versions (e.g. new API client methods).
282
+ * The in-memory `sharedPackagesReady` flag prevents redundant installs
283
+ * within a single daemon process lifetime.
279
284
  */
280
285
  async ensureSharedPackages() {
281
286
  if (this.sharedPackagesReady) return;
282
287
  const pkgJsonPath = join(this.basePath, "package.json");
283
- const nodeModulesExists = existsSync(join(this.basePath, "node_modules"));
284
- let needsInstall = false;
285
- if (existsSync(pkgJsonPath)) try {
286
- const deps = JSON.parse(readFileSync(pkgJsonPath, "utf-8")).dependencies ?? {};
287
- needsInstall = Object.keys(SHARED_PACKAGES).some((pkg) => !(pkg in deps));
288
- } catch {
289
- needsInstall = true;
290
- }
291
- else needsInstall = true;
292
- if (!needsInstall && !nodeModulesExists) needsInstall = true;
293
- if (!needsInstall) {
294
- this.sharedPackagesReady = true;
295
- return;
296
- }
297
288
  const pkgJson = {
298
289
  name: "alfe-integrations-root",
299
290
  private: true,
@@ -479,6 +470,24 @@ var StateManager = class {
479
470
  if (!entry) return false;
480
471
  return statuses.includes(entry.status);
481
472
  }
473
+ /**
474
+ * Increment the auto-reinstall attempt counter.
475
+ */
476
+ incrementReinstallAttempts(name) {
477
+ const state = this.read();
478
+ if (!(name in state.integrations)) return;
479
+ state.integrations[name].reinstallAttempts = (state.integrations[name].reinstallAttempts ?? 0) + 1;
480
+ this.write(state);
481
+ }
482
+ /**
483
+ * Reset the auto-reinstall attempt counter (on success or explicit reinstall).
484
+ */
485
+ resetReinstallAttempts(name) {
486
+ const state = this.read();
487
+ if (!(name in state.integrations)) return;
488
+ state.integrations[name].reinstallAttempts = 0;
489
+ this.write(state);
490
+ }
482
491
  };
483
492
  //#endregion
484
493
  //#region src/lock.ts
@@ -1016,7 +1025,7 @@ var IntegrationManager = class {
1016
1025
  * 4. Run health_check hook if present
1017
1026
  * 5. Mark as active in state
1018
1027
  */
1019
- async activate(integrationId) {
1028
+ async activate(integrationId, opts) {
1020
1029
  const entry = this.state.get(integrationId);
1021
1030
  if (!entry) return this.err("NOT_FOUND", `Integration "${integrationId}" is not installed`);
1022
1031
  if (entry.status === "active") return {
@@ -1050,10 +1059,19 @@ var IntegrationManager = class {
1050
1059
  continue;
1051
1060
  }
1052
1061
  const { plugins, skills, config: runtimeConfig } = resolveInstallsForRuntime(manifest, runtimeName);
1062
+ const pluginFailures = [];
1053
1063
  for (const plugin of plugins) {
1054
1064
  this.log.info(`Applying plugin ${plugin.package} to ${runtimeName}`);
1055
- await applier.applyPlugin(plugin.package, installPath);
1065
+ try {
1066
+ await applier.applyPlugin(plugin.package, installPath, { force: opts?.forcePlugins });
1067
+ } catch (err) {
1068
+ const msg = err instanceof Error ? err.message : String(err);
1069
+ this.log.error(`Failed to apply plugin ${plugin.package}: ${msg}`);
1070
+ pluginFailures.push(plugin.package);
1071
+ }
1056
1072
  }
1073
+ if (pluginFailures.length > 0 && pluginFailures.length === plugins.length) throw new Error(`All plugins failed to install: ${pluginFailures.join(", ")}`);
1074
+ if (pluginFailures.length > 0) this.log.warn(`Continuing activation with ${String(pluginFailures.length)} failed plugin(s): ${pluginFailures.join(", ")}`);
1057
1075
  for (const skill of skills) if (skill.clawhub) {
1058
1076
  this.log.info(`Installing ClawHub skill ${skill.clawhub} to ${runtimeName}`);
1059
1077
  await applier.applyClawHubSkill(skill.clawhub);
@@ -1083,7 +1101,8 @@ var IntegrationManager = class {
1083
1101
  configApplied = true;
1084
1102
  }
1085
1103
  }
1086
- if (plugins.length > 0 || skills.length > 0) this.lockManager.addEntries(runtimeName, integrationId, manifest.version, plugins, skills, installPath);
1104
+ const appliedPlugins = plugins.filter((p) => !pluginFailures.includes(p.package));
1105
+ if (appliedPlugins.length > 0 || skills.length > 0) this.lockManager.addEntries(runtimeName, integrationId, manifest.version, appliedPlugins, skills, installPath);
1087
1106
  }
1088
1107
  this.state.setStatus(integrationId, "active");
1089
1108
  if (manifest.hooks.post_activate) {
@@ -1122,6 +1141,7 @@ var IntegrationManager = class {
1122
1141
  } catch (err) {
1123
1142
  const message = err instanceof Error ? err.message : String(err);
1124
1143
  this.log.error(`Failed to activate "${integrationId}": ${message}`);
1144
+ this.state.setStatus(integrationId, "error", message);
1125
1145
  return this.err("ACTIVATE_FAILED", message);
1126
1146
  }
1127
1147
  }
@@ -1290,7 +1310,7 @@ var IntegrationManager = class {
1290
1310
  if (!uninstallResult.ok) this.log.warn(`Uninstall returned error during reinstall: ${String(uninstallResult.error?.message)} — proceeding with install`);
1291
1311
  const installResult = await this.install(params);
1292
1312
  if (!installResult.ok) return installResult;
1293
- return await this.activate(name);
1313
+ return await this.activate(name, { forcePlugins: true });
1294
1314
  } catch (err) {
1295
1315
  const message = err instanceof Error ? err.message : String(err);
1296
1316
  this.log.error(`Failed to reinstall "${name}": ${message}`);
@@ -1344,6 +1364,24 @@ var IntegrationManager = class {
1344
1364
  return existsSync(join(this.installer.getInstallPath(integrationId), "alfe-integration.yaml"));
1345
1365
  }
1346
1366
  /**
1367
+ * Get the number of consecutive auto-reinstall attempts for an integration.
1368
+ */
1369
+ getReinstallAttempts(integrationId) {
1370
+ return this.state.get(integrationId)?.reinstallAttempts ?? 0;
1371
+ }
1372
+ /**
1373
+ * Increment the auto-reinstall attempt counter.
1374
+ */
1375
+ incrementReinstallAttempts(integrationId) {
1376
+ this.state.incrementReinstallAttempts(integrationId);
1377
+ }
1378
+ /**
1379
+ * Reset the auto-reinstall attempt counter.
1380
+ */
1381
+ resetReinstallAttempts(integrationId) {
1382
+ this.state.resetReinstallAttempts(integrationId);
1383
+ }
1384
+ /**
1347
1385
  * Get a specific integration by name.
1348
1386
  */
1349
1387
  get(name) {
@@ -1494,8 +1532,19 @@ var OpenClawApplier = class {
1494
1532
  this.skillsDir = options.skillsDir ?? DEFAULT_SKILLS_DIR;
1495
1533
  this.trackingPath = options.configPath ?? join(this.workspace, "config.json");
1496
1534
  }
1497
- async applyPlugin(pkg) {
1535
+ async applyPlugin(pkg, _installPath, opts) {
1498
1536
  await this.ensurePluginsAllow(pkg);
1537
+ if (opts?.force && this.isPluginInstalled(pkg)) {
1538
+ log.info({ pkg }, "Force mode — uninstalling plugin before reinstall");
1539
+ try {
1540
+ await this.removePlugin(pkg);
1541
+ } catch (err) {
1542
+ log.warn({
1543
+ pkg,
1544
+ err: err instanceof Error ? err.message : String(err)
1545
+ }, "Failed to uninstall plugin during force reinstall — proceeding");
1546
+ }
1547
+ }
1499
1548
  if (!this.isPluginInstalled(pkg)) {
1500
1549
  try {
1501
1550
  const args = [
@@ -1555,8 +1604,7 @@ var OpenClawApplier = class {
1555
1604
  "config",
1556
1605
  "set",
1557
1606
  "plugins.allow",
1558
- JSON.stringify(updated),
1559
- "--strict-json"
1607
+ JSON.stringify(updated)
1560
1608
  ], { timeout: 1e4 });
1561
1609
  } catch (err) {
1562
1610
  log.warn({
@@ -1600,6 +1648,10 @@ var OpenClawApplier = class {
1600
1648
  log.info({ slug }, "ClawHub skill installed");
1601
1649
  } catch (err) {
1602
1650
  const msg = err instanceof Error ? err.message : String(err);
1651
+ if (msg.includes("already exists") || msg.includes("Skill already exists")) {
1652
+ log.info({ slug }, "ClawHub skill already installed (skipped)");
1653
+ return;
1654
+ }
1603
1655
  log.error({
1604
1656
  slug,
1605
1657
  err: msg
@@ -1646,8 +1698,7 @@ var OpenClawApplier = class {
1646
1698
  "config",
1647
1699
  "set",
1648
1700
  "--batch-json",
1649
- JSON.stringify(batch),
1650
- "--strict-json"
1701
+ JSON.stringify(batch)
1651
1702
  ], { timeout: 1e4 });
1652
1703
  } catch (err) {
1653
1704
  log.error({
@@ -1719,8 +1770,7 @@ var OpenClawApplier = class {
1719
1770
  "config",
1720
1771
  "set",
1721
1772
  "--batch-json",
1722
- JSON.stringify(batch),
1723
- "--strict-json"
1773
+ JSON.stringify(batch)
1724
1774
  ], { timeout: 1e4 });
1725
1775
  } catch (err) {
1726
1776
  log.error({
@@ -1825,6 +1875,15 @@ var IntegrationManagerAdapter = class {
1825
1875
  isInstallIntact(integrationId) {
1826
1876
  return Promise.resolve(this.manager.isInstallIntact(integrationId));
1827
1877
  }
1878
+ getReinstallAttempts(integrationId) {
1879
+ return this.manager.getReinstallAttempts(integrationId);
1880
+ }
1881
+ incrementReinstallAttempts(integrationId) {
1882
+ this.manager.incrementReinstallAttempts(integrationId);
1883
+ }
1884
+ resetReinstallAttempts(integrationId) {
1885
+ this.manager.resetReinstallAttempts(integrationId);
1886
+ }
1828
1887
  };
1829
1888
  //#endregion
1830
1889
  export { Installer, InstallerError, IntegrationManager, IntegrationManagerAdapter, LockManager, OpenClawApplier, Registry, RegistryResolveError, Resolver, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/integrations",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "description": "Integration lifecycle management for Alfe — registry, resolution, installation, and state",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@auriclabs/logger": "^0.1.1",
16
- "@alfe.ai/integration-manifest": "^0.0.9"
16
+ "@alfe.ai/integration-manifest": "^0.0.10"
17
17
  },
18
18
  "files": [
19
19
  "dist"