@alfe.ai/integrations 0.2.8 → 0.2.10
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 +17 -0
- package/dist/index.js +49 -15
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -610,6 +610,15 @@ declare class IntegrationManager {
|
|
|
610
610
|
clear(): void;
|
|
611
611
|
private checkHealth;
|
|
612
612
|
private err;
|
|
613
|
+
/**
|
|
614
|
+
* True when the manifest's `supported_agents` (if declared) intersects the
|
|
615
|
+
* registered runtime appliers. Hooks and MCP registration run once per
|
|
616
|
+
* integration (not per runtime), so they must use this aggregate check —
|
|
617
|
+
* the per-runtime loop's check only protects plugin/skill/config apply.
|
|
618
|
+
* The daemon registers exactly the agent's runtime, so checking the
|
|
619
|
+
* registered appliers' keys is sufficient.
|
|
620
|
+
*/
|
|
621
|
+
private manifestSupportsRegisteredRuntime;
|
|
613
622
|
}
|
|
614
623
|
//#endregion
|
|
615
624
|
//#region src/state.d.ts
|
|
@@ -699,6 +708,13 @@ interface HookEnvOptions {
|
|
|
699
708
|
config?: Record<string, unknown>;
|
|
700
709
|
/** Secret key-value pairs (in-memory only, injected as env vars) */
|
|
701
710
|
secrets?: Map<string, unknown>;
|
|
711
|
+
/**
|
|
712
|
+
* Registered runtime name(s) on this agent (e.g. ["openclaw"]). Injected as
|
|
713
|
+
* ALFE_AGENT_RUNTIME so hooks can branch on runtime instead of sniffing for
|
|
714
|
+
* runtime binaries. The daemon registers exactly one runtime today; the
|
|
715
|
+
* value is comma-joined if that ever changes.
|
|
716
|
+
*/
|
|
717
|
+
runtimes?: string[];
|
|
702
718
|
}
|
|
703
719
|
/**
|
|
704
720
|
* Build the environment variables for a hook script execution.
|
|
@@ -706,6 +722,7 @@ interface HookEnvOptions {
|
|
|
706
722
|
* Injects:
|
|
707
723
|
* - ALFE_INTEGRATION_DIR=~/.alfe/integrations/{name}/
|
|
708
724
|
* - ALFE_STATE_DIR=~/.alfe/state/{name}/ (creates dir if needed)
|
|
725
|
+
* - ALFE_AGENT_RUNTIME=openclaw (comma-joined registered runtime names)
|
|
709
726
|
* - ALFE_<NAME_UPPER>_<KEY_UPPER>=value for each config entry
|
|
710
727
|
* - ALFE_<NAME_UPPER>_<KEY_UPPER>=value for each secret entry
|
|
711
728
|
*/
|
package/dist/index.js
CHANGED
|
@@ -671,11 +671,12 @@ const STATE_BASE_DIR = join(homedir(), ".alfe", "state");
|
|
|
671
671
|
* Injects:
|
|
672
672
|
* - ALFE_INTEGRATION_DIR=~/.alfe/integrations/{name}/
|
|
673
673
|
* - ALFE_STATE_DIR=~/.alfe/state/{name}/ (creates dir if needed)
|
|
674
|
+
* - ALFE_AGENT_RUNTIME=openclaw (comma-joined registered runtime names)
|
|
674
675
|
* - ALFE_<NAME_UPPER>_<KEY_UPPER>=value for each config entry
|
|
675
676
|
* - ALFE_<NAME_UPPER>_<KEY_UPPER>=value for each secret entry
|
|
676
677
|
*/
|
|
677
678
|
function buildHookEnv(options, additionalEnv) {
|
|
678
|
-
const { integrationName, config, secrets } = options;
|
|
679
|
+
const { integrationName, config, secrets, runtimes } = options;
|
|
679
680
|
const nameUpper = integrationName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
680
681
|
const integrationDir = join(INTEGRATIONS_BASE_DIR, integrationName);
|
|
681
682
|
const stateDir = join(STATE_BASE_DIR, integrationName);
|
|
@@ -688,6 +689,7 @@ function buildHookEnv(options, additionalEnv) {
|
|
|
688
689
|
ALFE_INTEGRATION_DIR: integrationDir,
|
|
689
690
|
ALFE_STATE_DIR: stateDir
|
|
690
691
|
};
|
|
692
|
+
if (runtimes && runtimes.length > 0) env.ALFE_AGENT_RUNTIME = runtimes.join(",");
|
|
691
693
|
if (config) {
|
|
692
694
|
for (const [key, value] of Object.entries(config)) if (value !== void 0 && value !== null) {
|
|
693
695
|
const envKey = `ALFE_${nameUpper}_${key.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
@@ -1037,17 +1039,20 @@ var IntegrationManager = class {
|
|
|
1037
1039
|
const depState = this.state.get(dep);
|
|
1038
1040
|
if (!depState || depState.status === "error") throw new Error(`Dependency "${dep}" is not installed. Install it first: alfe integration install ${dep}`);
|
|
1039
1041
|
}
|
|
1040
|
-
|
|
1042
|
+
const installHooksSupported = this.manifestSupportsRegisteredRuntime(manifest);
|
|
1043
|
+
if (!installHooksSupported && (manifest.hooks.pre_install || manifest.hooks.post_install)) this.log.warn(`Integration "${name}" install hooks skipped — no registered runtime (${[...this.runtimeAppliers.keys()].join(", ")}) is in supported_agents (${(manifest.supported_agents ?? []).join(", ")})`);
|
|
1044
|
+
if (installHooksSupported && manifest.hooks.pre_install) {
|
|
1041
1045
|
this.log.info(`Running pre_install hook: ${manifest.hooks.pre_install}`);
|
|
1042
1046
|
const hookResult = await runHook(installPath, manifest.hooks.pre_install);
|
|
1043
1047
|
if (hookResult.exitCode !== 0) throw new Error(`pre_install hook failed (exit ${String(hookResult.exitCode)}): ${hookResult.stderr || hookResult.stdout}`);
|
|
1044
1048
|
}
|
|
1045
|
-
if (manifest.hooks.post_install) {
|
|
1049
|
+
if (installHooksSupported && manifest.hooks.post_install) {
|
|
1046
1050
|
this.log.info(`Running post_install hook: ${manifest.hooks.post_install}`);
|
|
1047
1051
|
const hookResult = await runHookWithContext(installPath, manifest.hooks.post_install, {
|
|
1048
1052
|
integrationName: name,
|
|
1049
1053
|
config: config ?? {},
|
|
1050
|
-
secrets: this.secrets.get(name)
|
|
1054
|
+
secrets: this.secrets.get(name),
|
|
1055
|
+
runtimes: [...this.runtimeAppliers.keys()]
|
|
1051
1056
|
});
|
|
1052
1057
|
if (hookResult.exitCode !== 0) throw new Error(`post_install hook failed (exit ${String(hookResult.exitCode)}): ${hookResult.stderr || hookResult.stdout}`);
|
|
1053
1058
|
}
|
|
@@ -1213,8 +1218,8 @@ var IntegrationManager = class {
|
|
|
1213
1218
|
if (appliedPlugins.length > 0 || skills.length > 0 || runtimeConfigApplied) this.lockManager.addEntries(runtimeName, integrationId, manifest.version, appliedPlugins, skills, installPath, { configApplied: runtimeConfigApplied });
|
|
1214
1219
|
}
|
|
1215
1220
|
const mcpServers = manifest.mcp_servers ?? [];
|
|
1216
|
-
const
|
|
1217
|
-
if (mcpServers.length > 0 && !
|
|
1221
|
+
const runtimeSupported = this.manifestSupportsRegisteredRuntime(manifest);
|
|
1222
|
+
if (mcpServers.length > 0 && !runtimeSupported) this.log.warn(`Integration "${integrationId}" declares ${String(mcpServers.length)} mcp_server(s) but no registered runtime (${[...this.runtimeAppliers.keys()].join(", ")}) is in supported_agents (${(supportedAgents ?? []).join(", ")}) — skipping MCP registration`);
|
|
1218
1223
|
else if (mcpServers.length > 0) if (!this.mcpApplier) this.log.warn(`Integration "${integrationId}" declares ${String(mcpServers.length)} mcp_server(s) but no mcpApplier is wired — skipping MCP registration`);
|
|
1219
1224
|
else {
|
|
1220
1225
|
const secretEntries = this.secrets.get(integrationId);
|
|
@@ -1226,24 +1231,28 @@ var IntegrationManager = class {
|
|
|
1226
1231
|
await this.mcpApplier.applyForIntegration(integrationId, mcpServers, mergedConfig, { connectionId: entry.customConnectionId });
|
|
1227
1232
|
}
|
|
1228
1233
|
this.state.setStatus(integrationId, "active");
|
|
1229
|
-
if (manifest.hooks.post_activate) {
|
|
1234
|
+
if (manifest.hooks.post_activate && !runtimeSupported) this.log.warn(`Integration "${integrationId}" post_activate hook skipped — no registered runtime (${[...this.runtimeAppliers.keys()].join(", ")}) is in supported_agents (${(supportedAgents ?? []).join(", ")})`);
|
|
1235
|
+
else if (manifest.hooks.post_activate) {
|
|
1230
1236
|
this.log.info(`Running post_activate hook: ${manifest.hooks.post_activate}`);
|
|
1231
1237
|
const hookResult = await runHookWithContext(installPath, manifest.hooks.post_activate, {
|
|
1232
1238
|
integrationName: integrationId,
|
|
1233
1239
|
config: entry.config,
|
|
1234
|
-
secrets: this.secrets.get(integrationId)
|
|
1240
|
+
secrets: this.secrets.get(integrationId),
|
|
1241
|
+
runtimes: [...this.runtimeAppliers.keys()]
|
|
1235
1242
|
});
|
|
1236
1243
|
if (hookResult.exitCode !== 0) {
|
|
1237
1244
|
this.state.setStatus(integrationId, "error", `post_activate hook failed: ${hookResult.stderr || hookResult.stdout}`);
|
|
1238
1245
|
return this.err("POST_ACTIVATE_FAILED", `post_activate hook failed (exit ${String(hookResult.exitCode)}): ${hookResult.stderr || hookResult.stdout}`);
|
|
1239
1246
|
}
|
|
1240
1247
|
}
|
|
1241
|
-
if (manifest.hooks.health_check) {
|
|
1248
|
+
if (manifest.hooks.health_check && !runtimeSupported) this.log.warn(`Integration "${integrationId}" health_check hook skipped — no registered runtime (${[...this.runtimeAppliers.keys()].join(", ")}) is in supported_agents (${(supportedAgents ?? []).join(", ")})`);
|
|
1249
|
+
else if (manifest.hooks.health_check) {
|
|
1242
1250
|
this.log.info(`Running health check: ${manifest.hooks.health_check}`);
|
|
1243
1251
|
const hookResult = await runHookWithContext(installPath, manifest.hooks.health_check, {
|
|
1244
1252
|
integrationName: integrationId,
|
|
1245
1253
|
config: entry.config,
|
|
1246
|
-
secrets: this.secrets.get(integrationId)
|
|
1254
|
+
secrets: this.secrets.get(integrationId),
|
|
1255
|
+
runtimes: [...this.runtimeAppliers.keys()]
|
|
1247
1256
|
});
|
|
1248
1257
|
if (hookResult.exitCode !== 0) {
|
|
1249
1258
|
this.state.setStatus(integrationId, "error", `Health check failed: ${hookResult.stderr || hookResult.stdout}`);
|
|
@@ -1386,21 +1395,25 @@ var IntegrationManager = class {
|
|
|
1386
1395
|
} catch {
|
|
1387
1396
|
this.log.warn(`Could not parse manifest for "${name}" — proceeding with basic cleanup`);
|
|
1388
1397
|
}
|
|
1389
|
-
|
|
1398
|
+
const uninstallHooksSupported = manifest ? this.manifestSupportsRegisteredRuntime(manifest) : true;
|
|
1399
|
+
if (manifest && !uninstallHooksSupported && (manifest.hooks.pre_uninstall || manifest.hooks.post_uninstall)) this.log.warn(`Integration "${name}" uninstall hooks skipped — no registered runtime (${[...this.runtimeAppliers.keys()].join(", ")}) is in supported_agents (${(manifest.supported_agents ?? []).join(", ")})`);
|
|
1400
|
+
if (uninstallHooksSupported && manifest?.hooks.pre_uninstall) {
|
|
1390
1401
|
this.log.info(`Running pre_uninstall hook: ${manifest.hooks.pre_uninstall}`);
|
|
1391
1402
|
const hookResult = await runHookWithContext(installPath, manifest.hooks.pre_uninstall, {
|
|
1392
1403
|
integrationName: name,
|
|
1393
1404
|
config: entry.config,
|
|
1394
|
-
secrets: this.secrets.get(name)
|
|
1405
|
+
secrets: this.secrets.get(name),
|
|
1406
|
+
runtimes: [...this.runtimeAppliers.keys()]
|
|
1395
1407
|
});
|
|
1396
1408
|
if (hookResult.exitCode !== 0) this.log.warn(`pre_uninstall hook failed (continuing): ${hookResult.stderr}`);
|
|
1397
1409
|
}
|
|
1398
|
-
if (manifest?.hooks.post_uninstall) {
|
|
1410
|
+
if (uninstallHooksSupported && manifest?.hooks.post_uninstall) {
|
|
1399
1411
|
this.log.info(`Running post_uninstall hook: ${manifest.hooks.post_uninstall}`);
|
|
1400
1412
|
const hookResult = await runHookWithContext(installPath, manifest.hooks.post_uninstall, {
|
|
1401
1413
|
integrationName: name,
|
|
1402
1414
|
config: entry.config,
|
|
1403
|
-
secrets: this.secrets.get(name)
|
|
1415
|
+
secrets: this.secrets.get(name),
|
|
1416
|
+
runtimes: [...this.runtimeAppliers.keys()]
|
|
1404
1417
|
});
|
|
1405
1418
|
if (hookResult.exitCode !== 0) this.log.warn(`post_uninstall hook failed (non-fatal): ${hookResult.stderr}`);
|
|
1406
1419
|
}
|
|
@@ -1597,10 +1610,19 @@ var IntegrationManager = class {
|
|
|
1597
1610
|
version: manifest.version,
|
|
1598
1611
|
message: "No health check hook defined"
|
|
1599
1612
|
};
|
|
1613
|
+
if (!this.manifestSupportsRegisteredRuntime(manifest)) return {
|
|
1614
|
+
id,
|
|
1615
|
+
name: manifest.name,
|
|
1616
|
+
healthy: entry.status === "active" || entry.status === "configured" || entry.status === "installed",
|
|
1617
|
+
status: entry.status,
|
|
1618
|
+
version: manifest.version,
|
|
1619
|
+
message: "Health check hook skipped — integration does not support this agent runtime"
|
|
1620
|
+
};
|
|
1600
1621
|
const hookResult = await runHookWithContext(installPath, manifest.hooks.health_check, {
|
|
1601
1622
|
integrationName: id,
|
|
1602
1623
|
config: entry.config,
|
|
1603
|
-
secrets: this.secrets.get(id)
|
|
1624
|
+
secrets: this.secrets.get(id),
|
|
1625
|
+
runtimes: [...this.runtimeAppliers.keys()]
|
|
1604
1626
|
});
|
|
1605
1627
|
return {
|
|
1606
1628
|
id,
|
|
@@ -1631,6 +1653,18 @@ var IntegrationManager = class {
|
|
|
1631
1653
|
}
|
|
1632
1654
|
};
|
|
1633
1655
|
}
|
|
1656
|
+
/**
|
|
1657
|
+
* True when the manifest's `supported_agents` (if declared) intersects the
|
|
1658
|
+
* registered runtime appliers. Hooks and MCP registration run once per
|
|
1659
|
+
* integration (not per runtime), so they must use this aggregate check —
|
|
1660
|
+
* the per-runtime loop's check only protects plugin/skill/config apply.
|
|
1661
|
+
* The daemon registers exactly the agent's runtime, so checking the
|
|
1662
|
+
* registered appliers' keys is sufficient.
|
|
1663
|
+
*/
|
|
1664
|
+
manifestSupportsRegisteredRuntime(manifest) {
|
|
1665
|
+
const supported = manifest.supported_agents;
|
|
1666
|
+
return !supported || supported.length === 0 || [...this.runtimeAppliers.keys()].some((r) => supported.includes(r));
|
|
1667
|
+
}
|
|
1634
1668
|
};
|
|
1635
1669
|
//#endregion
|
|
1636
1670
|
//#region src/openclaw-cli-lock.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/integrations",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "Integration lifecycle management for Alfe — registry, resolution, installation, and state",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"@auriclabs/logger": "^0.1.1",
|
|
16
16
|
"yaml": ">=2.8.3",
|
|
17
17
|
"@alfe.ai/integration-manifest": "^0.3.1",
|
|
18
|
-
"@alfe.ai/mcp-bundler": "^0.
|
|
18
|
+
"@alfe.ai/mcp-bundler": "^0.3.0"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"dist"
|