@alfe.ai/integrations 0.0.25 → 0.0.27

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
  /**
@@ -389,7 +394,9 @@ interface RuntimeApplier {
389
394
  /** The runtime identifier (e.g. 'openclaw', 'nanoclaw') */
390
395
  readonly runtime: string;
391
396
  /** Install a plugin package into this runtime */
392
- applyPlugin(pkg: string, integrationInstallPath: string): Promise<void>;
397
+ applyPlugin(pkg: string, integrationInstallPath: string, opts?: {
398
+ force?: boolean;
399
+ }): Promise<void>;
393
400
  /** Remove a plugin package from this runtime */
394
401
  removePlugin(pkg: string): Promise<void>;
395
402
  /** Copy a skill directory into this runtime's skills location */
@@ -517,7 +524,9 @@ declare class IntegrationManager {
517
524
  * 4. Run health_check hook if present
518
525
  * 5. Mark as active in state
519
526
  */
520
- activate(integrationId: string): Promise<ManagerResponse>;
527
+ activate(integrationId: string, opts?: {
528
+ forcePlugins?: boolean;
529
+ }): Promise<ManagerResponse>;
521
530
  /**
522
531
  * Deactivate a running integration.
523
532
  *
@@ -728,7 +737,9 @@ declare class OpenClawApplier implements RuntimeApplier {
728
737
  private skillsDir;
729
738
  private trackingPath;
730
739
  constructor(options: OpenClawApplierOptions);
731
- applyPlugin(pkg: string): Promise<void>;
740
+ applyPlugin(pkg: string, _installPath?: string, opts?: {
741
+ force?: boolean;
742
+ }): Promise<void>;
732
743
  /**
733
744
  * Ensure the plugin is in plugins.allow in openclaw.json.
734
745
  * Uses `openclaw config set` to avoid clobbering OpenClaw's own file format.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { execFile, spawn } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
  import { dirname, join } from "node:path";
4
- import { homedir, tmpdir } from "node:os";
4
+ import { homedir, platform, tmpdir } from "node:os";
5
5
  import { closeSync, cpSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, rmSync, writeFileSync } from "node:fs";
6
6
  import { buildConfigValidationSchema, parseManifestFile } from "@alfe.ai/integration-manifest";
7
7
  import { createLogger } from "@auriclabs/logger";
@@ -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,
@@ -663,6 +654,7 @@ function buildHookEnv(options, additionalEnv) {
663
654
  * 3. Default → "bash"
664
655
  */
665
656
  function resolveInterpreter(scriptPath) {
657
+ const isWindows = platform() === "win32";
666
658
  try {
667
659
  const fd = openSync(scriptPath, "r");
668
660
  const buf = Buffer.alloc(256);
@@ -678,8 +670,13 @@ function resolveInterpreter(scriptPath) {
678
670
  args: [...parts.slice(1), scriptPath]
679
671
  };
680
672
  }
673
+ const cmd = shebang.split(/\s+/)[0];
674
+ if (isWindows && cmd.startsWith("/")) return {
675
+ command: cmd.split("/").pop() ?? cmd,
676
+ args: [scriptPath]
677
+ };
681
678
  return {
682
- command: shebang.split(/\s+/)[0],
679
+ command: cmd,
683
680
  args: [scriptPath]
684
681
  };
685
682
  }
@@ -689,7 +686,7 @@ function resolveInterpreter(scriptPath) {
689
686
  args: [scriptPath]
690
687
  };
691
688
  if (scriptPath.endsWith(".py")) return {
692
- command: "python3",
689
+ command: isWindows ? "python" : "python3",
693
690
  args: [scriptPath]
694
691
  };
695
692
  return {
@@ -1010,7 +1007,7 @@ var IntegrationManager = class {
1010
1007
  * 4. Run health_check hook if present
1011
1008
  * 5. Mark as active in state
1012
1009
  */
1013
- async activate(integrationId) {
1010
+ async activate(integrationId, opts) {
1014
1011
  const entry = this.state.get(integrationId);
1015
1012
  if (!entry) return this.err("NOT_FOUND", `Integration "${integrationId}" is not installed`);
1016
1013
  if (entry.status === "active") return {
@@ -1046,7 +1043,7 @@ var IntegrationManager = class {
1046
1043
  const { plugins, skills, config: runtimeConfig } = resolveInstallsForRuntime(manifest, runtimeName);
1047
1044
  for (const plugin of plugins) {
1048
1045
  this.log.info(`Applying plugin ${plugin.package} to ${runtimeName}`);
1049
- await applier.applyPlugin(plugin.package, installPath);
1046
+ await applier.applyPlugin(plugin.package, installPath, { force: opts?.forcePlugins });
1050
1047
  }
1051
1048
  for (const skill of skills) if (skill.clawhub) {
1052
1049
  this.log.info(`Installing ClawHub skill ${skill.clawhub} to ${runtimeName}`);
@@ -1064,7 +1061,7 @@ var IntegrationManager = class {
1064
1061
  await applier.applyConfig(integrationId, interpolatedConfig);
1065
1062
  configApplied = true;
1066
1063
  }
1067
- const mcpServers = manifest.mcp_servers;
1064
+ const mcpServers = manifest.mcp_servers ?? [];
1068
1065
  if (mcpServers.length > 0) {
1069
1066
  const secretEntries = this.secrets.get(integrationId);
1070
1067
  const interpolatedServers = interpolateMcpServerEnvs(mcpServers, {
@@ -1116,6 +1113,7 @@ var IntegrationManager = class {
1116
1113
  } catch (err) {
1117
1114
  const message = err instanceof Error ? err.message : String(err);
1118
1115
  this.log.error(`Failed to activate "${integrationId}": ${message}`);
1116
+ this.state.setStatus(integrationId, "error", message);
1119
1117
  return this.err("ACTIVATE_FAILED", message);
1120
1118
  }
1121
1119
  }
@@ -1284,7 +1282,7 @@ var IntegrationManager = class {
1284
1282
  if (!uninstallResult.ok) this.log.warn(`Uninstall returned error during reinstall: ${String(uninstallResult.error?.message)} — proceeding with install`);
1285
1283
  const installResult = await this.install(params);
1286
1284
  if (!installResult.ok) return installResult;
1287
- return await this.activate(name);
1285
+ return await this.activate(name, { forcePlugins: true });
1288
1286
  } catch (err) {
1289
1287
  const message = err instanceof Error ? err.message : String(err);
1290
1288
  this.log.error(`Failed to reinstall "${name}": ${message}`);
@@ -1364,11 +1362,11 @@ var IntegrationManager = class {
1364
1362
  const manifestPath = join(installPath, "alfe-integration.yaml");
1365
1363
  if (!existsSync(manifestPath)) continue;
1366
1364
  try {
1367
- const manifest = parseManifestFile(manifestPath);
1368
- if (manifest.commands.length === 0) continue;
1365
+ const commands = parseManifestFile(manifestPath).commands ?? [];
1366
+ if (commands.length === 0) continue;
1369
1367
  result.push({
1370
1368
  integrationId: entry.id,
1371
- commands: manifest.commands.map((cmd) => ({
1369
+ commands: commands.map((cmd) => ({
1372
1370
  name: cmd.name,
1373
1371
  handler: cmd.handler,
1374
1372
  resolvedPath: join(installPath, cmd.handler),
@@ -1488,8 +1486,19 @@ var OpenClawApplier = class {
1488
1486
  this.skillsDir = options.skillsDir ?? DEFAULT_SKILLS_DIR;
1489
1487
  this.trackingPath = options.configPath ?? join(this.workspace, "config.json");
1490
1488
  }
1491
- async applyPlugin(pkg) {
1489
+ async applyPlugin(pkg, _installPath, opts) {
1492
1490
  await this.ensurePluginsAllow(pkg);
1491
+ if (opts?.force && this.isPluginInstalled(pkg)) {
1492
+ log.info({ pkg }, "Force mode — uninstalling plugin before reinstall");
1493
+ try {
1494
+ await this.removePlugin(pkg);
1495
+ } catch (err) {
1496
+ log.warn({
1497
+ pkg,
1498
+ err: err instanceof Error ? err.message : String(err)
1499
+ }, "Failed to uninstall plugin during force reinstall — proceeding");
1500
+ }
1501
+ }
1493
1502
  if (!this.isPluginInstalled(pkg)) {
1494
1503
  try {
1495
1504
  const args = [
@@ -1549,8 +1558,7 @@ var OpenClawApplier = class {
1549
1558
  "config",
1550
1559
  "set",
1551
1560
  "plugins.allow",
1552
- JSON.stringify(updated),
1553
- "--strict-json"
1561
+ JSON.stringify(updated)
1554
1562
  ], { timeout: 1e4 });
1555
1563
  } catch (err) {
1556
1564
  log.warn({
@@ -1594,6 +1602,10 @@ var OpenClawApplier = class {
1594
1602
  log.info({ slug }, "ClawHub skill installed");
1595
1603
  } catch (err) {
1596
1604
  const msg = err instanceof Error ? err.message : String(err);
1605
+ if (msg.includes("already exists") || msg.includes("Skill already exists")) {
1606
+ log.info({ slug }, "ClawHub skill already installed (skipped)");
1607
+ return;
1608
+ }
1597
1609
  log.error({
1598
1610
  slug,
1599
1611
  err: msg
@@ -1640,8 +1652,7 @@ var OpenClawApplier = class {
1640
1652
  "config",
1641
1653
  "set",
1642
1654
  "--batch-json",
1643
- JSON.stringify(batch),
1644
- "--strict-json"
1655
+ JSON.stringify(batch)
1645
1656
  ], { timeout: 1e4 });
1646
1657
  } catch (err) {
1647
1658
  log.error({
@@ -1713,8 +1724,7 @@ var OpenClawApplier = class {
1713
1724
  "config",
1714
1725
  "set",
1715
1726
  "--batch-json",
1716
- JSON.stringify(batch),
1717
- "--strict-json"
1727
+ JSON.stringify(batch)
1718
1728
  ], { timeout: 1e4 });
1719
1729
  } catch (err) {
1720
1730
  log.error({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/integrations",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "description": "Integration lifecycle management for Alfe — registry, resolution, installation, and state",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",