@alfe.ai/integrations 0.0.1 → 0.0.2
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 +33 -11
- package/dist/index.js +129 -155
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,39 @@ interface RegistryEntry {
|
|
|
20
20
|
description: string;
|
|
21
21
|
/** Agent runtimes this integration supports. Empty/absent = universal. */
|
|
22
22
|
supported_agents?: string[];
|
|
23
|
+
/** URL to integration icon */
|
|
24
|
+
icon?: string;
|
|
25
|
+
/** Author info */
|
|
26
|
+
author?: {
|
|
27
|
+
name: string;
|
|
28
|
+
url?: string;
|
|
29
|
+
} | string;
|
|
30
|
+
/** Pricing details */
|
|
31
|
+
pricing?: {
|
|
32
|
+
type: "free" | "paid";
|
|
33
|
+
price?: number;
|
|
34
|
+
currency?: string;
|
|
35
|
+
interval?: "month" | "year";
|
|
36
|
+
};
|
|
37
|
+
/** Feature list for marketplace display */
|
|
38
|
+
features?: string[];
|
|
39
|
+
/** Preview image URLs */
|
|
40
|
+
preview_images?: string[];
|
|
41
|
+
/** Configuration schema fields */
|
|
42
|
+
config_schema?: {
|
|
43
|
+
key: string;
|
|
44
|
+
label: string;
|
|
45
|
+
type: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
required?: boolean;
|
|
48
|
+
default?: string | number | boolean;
|
|
49
|
+
options?: string[];
|
|
50
|
+
select_options?: {
|
|
51
|
+
value: string;
|
|
52
|
+
label: string;
|
|
53
|
+
}[];
|
|
54
|
+
oauth_provider?: string;
|
|
55
|
+
}[];
|
|
23
56
|
}
|
|
24
57
|
interface RegistryIndex {
|
|
25
58
|
version: number;
|
|
@@ -560,17 +593,6 @@ interface OpenClawApplierOptions {
|
|
|
560
593
|
/** Path to the OpenClaw agent config file (defaults to {workspace}/config.json) */
|
|
561
594
|
configPath?: string;
|
|
562
595
|
}
|
|
563
|
-
/**
|
|
564
|
-
* Resolve LLM provider runtime config based on mode.
|
|
565
|
-
*
|
|
566
|
-
* - "alfe_credits": keep manifest config as-is (baseUrl points to local proxy,
|
|
567
|
-
* apiKey is a dummy — the proxy injects the real key)
|
|
568
|
-
* - "byok": remove baseUrl (SDK uses provider default), set apiKey from
|
|
569
|
-
* decrypted secret
|
|
570
|
-
*
|
|
571
|
-
* Non-LLM integrations pass through unchanged.
|
|
572
|
-
*/
|
|
573
|
-
|
|
574
596
|
declare class OpenClawApplier implements RuntimeApplier {
|
|
575
597
|
readonly runtime = "openclaw";
|
|
576
598
|
private workspace;
|
package/dist/index.js
CHANGED
|
@@ -652,159 +652,6 @@ async function runHookWithContext(integrationPath, hookScript, options) {
|
|
|
652
652
|
return runHook(integrationPath, hookScript, buildHookEnv(options));
|
|
653
653
|
}
|
|
654
654
|
//#endregion
|
|
655
|
-
//#region src/appliers/openclaw-applier.ts
|
|
656
|
-
/**
|
|
657
|
-
* OpenClawApplier — applies plugins, skills, and config to the OpenClaw runtime.
|
|
658
|
-
*
|
|
659
|
-
* Plugins are installed via `pnpm add` in the OpenClaw workspace directory.
|
|
660
|
-
* Skills are copied to ~/.alfe/skills/{name}.
|
|
661
|
-
* Config is deep-merged into the OpenClaw agent config, with per-integration
|
|
662
|
-
* tracking so changes can be cleanly removed on deactivation.
|
|
663
|
-
*/
|
|
664
|
-
const execFileAsync = promisify(execFile);
|
|
665
|
-
const DEFAULT_SKILLS_DIR = join(homedir(), ".alfe", "skills");
|
|
666
|
-
/**
|
|
667
|
-
* Deep-merge source into target, returning a new object.
|
|
668
|
-
* Arrays are replaced, not concatenated.
|
|
669
|
-
*/
|
|
670
|
-
function deepMerge(target, source) {
|
|
671
|
-
const result = { ...target };
|
|
672
|
-
for (const key of Object.keys(source)) {
|
|
673
|
-
const srcVal = source[key];
|
|
674
|
-
const tgtVal = result[key];
|
|
675
|
-
if (srcVal !== null && typeof srcVal === "object" && !Array.isArray(srcVal) && tgtVal !== null && typeof tgtVal === "object" && !Array.isArray(tgtVal)) result[key] = deepMerge(tgtVal, srcVal);
|
|
676
|
-
else result[key] = srcVal;
|
|
677
|
-
}
|
|
678
|
-
return result;
|
|
679
|
-
}
|
|
680
|
-
const LLM_INTEGRATION_IDS = new Set(["anthropic", "openai"]);
|
|
681
|
-
/**
|
|
682
|
-
* Resolve LLM provider runtime config based on mode.
|
|
683
|
-
*
|
|
684
|
-
* - "alfe_credits": keep manifest config as-is (baseUrl points to local proxy,
|
|
685
|
-
* apiKey is a dummy — the proxy injects the real key)
|
|
686
|
-
* - "byok": remove baseUrl (SDK uses provider default), set apiKey from
|
|
687
|
-
* decrypted secret
|
|
688
|
-
*
|
|
689
|
-
* Non-LLM integrations pass through unchanged.
|
|
690
|
-
*/
|
|
691
|
-
function resolveLlmRuntimeConfig(integrationId, runtimeConfig, integrationConfig, secrets) {
|
|
692
|
-
if (!LLM_INTEGRATION_IDS.has(integrationId)) return runtimeConfig;
|
|
693
|
-
if (integrationConfig?.mode === "byok") {
|
|
694
|
-
const apiKey = secrets?.get("api_key");
|
|
695
|
-
const config = structuredClone(runtimeConfig);
|
|
696
|
-
const providers = config.models?.providers;
|
|
697
|
-
if (providers?.[integrationId]) {
|
|
698
|
-
delete providers[integrationId].baseUrl;
|
|
699
|
-
if (apiKey) providers[integrationId].apiKey = apiKey;
|
|
700
|
-
}
|
|
701
|
-
return config;
|
|
702
|
-
}
|
|
703
|
-
return runtimeConfig;
|
|
704
|
-
}
|
|
705
|
-
var OpenClawApplier = class {
|
|
706
|
-
runtime = "openclaw";
|
|
707
|
-
workspace;
|
|
708
|
-
skillsDir;
|
|
709
|
-
configPath;
|
|
710
|
-
constructor(options) {
|
|
711
|
-
this.workspace = options.workspace;
|
|
712
|
-
this.skillsDir = options.skillsDir ?? DEFAULT_SKILLS_DIR;
|
|
713
|
-
this.configPath = options.configPath ?? join(this.workspace, "config.json");
|
|
714
|
-
}
|
|
715
|
-
async applyPlugin(pkg) {
|
|
716
|
-
await execFileAsync("pnpm", ["add", pkg], {
|
|
717
|
-
cwd: this.workspace,
|
|
718
|
-
timeout: 6e4
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
async removePlugin(pkg) {
|
|
722
|
-
await execFileAsync("pnpm", ["remove", pkg], {
|
|
723
|
-
cwd: this.workspace,
|
|
724
|
-
timeout: 3e4
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
applySkill(name, srcPath) {
|
|
728
|
-
if (!existsSync(srcPath)) throw new Error(`Skill source path not found: ${srcPath}`);
|
|
729
|
-
mkdirSync(this.skillsDir, { recursive: true });
|
|
730
|
-
cpSync(srcPath, join(this.skillsDir, name), { recursive: true });
|
|
731
|
-
return Promise.resolve();
|
|
732
|
-
}
|
|
733
|
-
removeSkill(name) {
|
|
734
|
-
const skillPath = join(this.skillsDir, name);
|
|
735
|
-
if (existsSync(skillPath)) rmSync(skillPath, {
|
|
736
|
-
recursive: true,
|
|
737
|
-
force: true
|
|
738
|
-
});
|
|
739
|
-
return Promise.resolve();
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* Deep-merge integration config into the OpenClaw agent config file.
|
|
743
|
-
*
|
|
744
|
-
* Each integration's config contribution is tracked in
|
|
745
|
-
* `_integrations.{integrationId}` within the config file so it can be
|
|
746
|
-
* cleanly removed later.
|
|
747
|
-
*/
|
|
748
|
-
applyConfig(integrationId, config) {
|
|
749
|
-
const current = this.readConfig();
|
|
750
|
-
const integrations = current._integrations ?? {};
|
|
751
|
-
integrations[integrationId] = config;
|
|
752
|
-
current._integrations = integrations;
|
|
753
|
-
const merged = deepMerge(current, config);
|
|
754
|
-
merged._integrations = current._integrations;
|
|
755
|
-
this.writeConfig(merged);
|
|
756
|
-
return Promise.resolve();
|
|
757
|
-
}
|
|
758
|
-
/**
|
|
759
|
-
* Remove config previously applied by an integration.
|
|
760
|
-
*
|
|
761
|
-
* Rebuilds the config by re-merging all remaining integrations' configs,
|
|
762
|
-
* ensuring clean removal without orphaned keys.
|
|
763
|
-
*/
|
|
764
|
-
removeConfig(integrationId) {
|
|
765
|
-
const current = this.readConfig();
|
|
766
|
-
const integrations = current._integrations ?? {};
|
|
767
|
-
if (!(integrationId in integrations)) return Promise.resolve();
|
|
768
|
-
const remainingIntegrations = Object.fromEntries(Object.entries(integrations).filter(([key]) => key !== integrationId));
|
|
769
|
-
let rebuilt = this.getBaseConfig(current);
|
|
770
|
-
for (const cfg of Object.values(remainingIntegrations)) rebuilt = deepMerge(rebuilt, cfg);
|
|
771
|
-
rebuilt._integrations = remainingIntegrations;
|
|
772
|
-
this.writeConfig(rebuilt);
|
|
773
|
-
return Promise.resolve();
|
|
774
|
-
}
|
|
775
|
-
isAvailable() {
|
|
776
|
-
return Promise.resolve(existsSync(this.workspace));
|
|
777
|
-
}
|
|
778
|
-
readConfig() {
|
|
779
|
-
if (!existsSync(this.configPath)) return {};
|
|
780
|
-
try {
|
|
781
|
-
return JSON.parse(readFileSync(this.configPath, "utf-8"));
|
|
782
|
-
} catch {
|
|
783
|
-
return {};
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
writeConfig(config) {
|
|
787
|
-
mkdirSync(join(this.configPath, ".."), { recursive: true });
|
|
788
|
-
writeFileSync(this.configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Extract the base config by stripping all keys that were contributed
|
|
792
|
-
* by integrations. This is done by removing each integration's keys
|
|
793
|
-
* from the current config.
|
|
794
|
-
*/
|
|
795
|
-
getBaseConfig(current) {
|
|
796
|
-
const integrations = current._integrations ?? {};
|
|
797
|
-
const allIntegrationKeys = /* @__PURE__ */ new Set();
|
|
798
|
-
for (const cfg of Object.values(integrations)) for (const key of Object.keys(cfg)) allIntegrationKeys.add(key);
|
|
799
|
-
const base = {};
|
|
800
|
-
for (const [key, val] of Object.entries(current)) {
|
|
801
|
-
if (key === "_integrations") continue;
|
|
802
|
-
if (!allIntegrationKeys.has(key)) base[key] = val;
|
|
803
|
-
}
|
|
804
|
-
return base;
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
//#endregion
|
|
808
655
|
//#region src/integration-manager.ts
|
|
809
656
|
/**
|
|
810
657
|
* Integration Manager — full lifecycle management for Alfe integrations.
|
|
@@ -1026,9 +873,8 @@ var IntegrationManager = class {
|
|
|
1026
873
|
await applier.applySkill(skillName, srcPath);
|
|
1027
874
|
}
|
|
1028
875
|
if (runtimeConfig && Object.keys(runtimeConfig).length > 0) {
|
|
1029
|
-
const resolvedConfig = resolveLlmRuntimeConfig(integrationId, runtimeConfig, entry.config, this.secrets.get(integrationId));
|
|
1030
876
|
this.log.info(`Applying config for ${integrationId} to ${runtimeName}`);
|
|
1031
|
-
await applier.applyConfig(integrationId,
|
|
877
|
+
await applier.applyConfig(integrationId, runtimeConfig);
|
|
1032
878
|
}
|
|
1033
879
|
if (plugins.length > 0 || skills.length > 0) this.lockManager.addEntries(runtimeName, integrationId, manifest.version, plugins, skills, installPath);
|
|
1034
880
|
}
|
|
@@ -1313,6 +1159,134 @@ var IntegrationManager = class {
|
|
|
1313
1159
|
}
|
|
1314
1160
|
};
|
|
1315
1161
|
//#endregion
|
|
1162
|
+
//#region src/appliers/openclaw-applier.ts
|
|
1163
|
+
/**
|
|
1164
|
+
* OpenClawApplier — applies plugins, skills, and config to the OpenClaw runtime.
|
|
1165
|
+
*
|
|
1166
|
+
* Plugins are installed via `pnpm add` in the OpenClaw workspace directory.
|
|
1167
|
+
* Skills are copied to ~/.alfe/skills/{name}.
|
|
1168
|
+
* Config is deep-merged into the OpenClaw agent config, with per-integration
|
|
1169
|
+
* tracking so changes can be cleanly removed on deactivation.
|
|
1170
|
+
*/
|
|
1171
|
+
const execFileAsync = promisify(execFile);
|
|
1172
|
+
const DEFAULT_SKILLS_DIR = join(homedir(), ".alfe", "skills");
|
|
1173
|
+
/**
|
|
1174
|
+
* Deep-merge source into target, returning a new object.
|
|
1175
|
+
* Arrays are replaced, not concatenated.
|
|
1176
|
+
*/
|
|
1177
|
+
function deepMerge(target, source) {
|
|
1178
|
+
const result = { ...target };
|
|
1179
|
+
for (const key of Object.keys(source)) {
|
|
1180
|
+
const srcVal = source[key];
|
|
1181
|
+
const tgtVal = result[key];
|
|
1182
|
+
if (srcVal !== null && typeof srcVal === "object" && !Array.isArray(srcVal) && tgtVal !== null && typeof tgtVal === "object" && !Array.isArray(tgtVal)) result[key] = deepMerge(tgtVal, srcVal);
|
|
1183
|
+
else result[key] = srcVal;
|
|
1184
|
+
}
|
|
1185
|
+
return result;
|
|
1186
|
+
}
|
|
1187
|
+
var OpenClawApplier = class {
|
|
1188
|
+
runtime = "openclaw";
|
|
1189
|
+
workspace;
|
|
1190
|
+
skillsDir;
|
|
1191
|
+
configPath;
|
|
1192
|
+
constructor(options) {
|
|
1193
|
+
this.workspace = options.workspace;
|
|
1194
|
+
this.skillsDir = options.skillsDir ?? DEFAULT_SKILLS_DIR;
|
|
1195
|
+
this.configPath = options.configPath ?? join(this.workspace, "config.json");
|
|
1196
|
+
}
|
|
1197
|
+
async applyPlugin(pkg) {
|
|
1198
|
+
await execFileAsync("pnpm", ["add", pkg], {
|
|
1199
|
+
cwd: this.workspace,
|
|
1200
|
+
timeout: 6e4
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
async removePlugin(pkg) {
|
|
1204
|
+
await execFileAsync("pnpm", ["remove", pkg], {
|
|
1205
|
+
cwd: this.workspace,
|
|
1206
|
+
timeout: 3e4
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
applySkill(name, srcPath) {
|
|
1210
|
+
if (!existsSync(srcPath)) throw new Error(`Skill source path not found: ${srcPath}`);
|
|
1211
|
+
mkdirSync(this.skillsDir, { recursive: true });
|
|
1212
|
+
cpSync(srcPath, join(this.skillsDir, name), { recursive: true });
|
|
1213
|
+
return Promise.resolve();
|
|
1214
|
+
}
|
|
1215
|
+
removeSkill(name) {
|
|
1216
|
+
const skillPath = join(this.skillsDir, name);
|
|
1217
|
+
if (existsSync(skillPath)) rmSync(skillPath, {
|
|
1218
|
+
recursive: true,
|
|
1219
|
+
force: true
|
|
1220
|
+
});
|
|
1221
|
+
return Promise.resolve();
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Deep-merge integration config into the OpenClaw agent config file.
|
|
1225
|
+
*
|
|
1226
|
+
* Each integration's config contribution is tracked in
|
|
1227
|
+
* `_integrations.{integrationId}` within the config file so it can be
|
|
1228
|
+
* cleanly removed later.
|
|
1229
|
+
*/
|
|
1230
|
+
applyConfig(integrationId, config) {
|
|
1231
|
+
const current = this.readConfig();
|
|
1232
|
+
const integrations = current._integrations ?? {};
|
|
1233
|
+
integrations[integrationId] = config;
|
|
1234
|
+
current._integrations = integrations;
|
|
1235
|
+
const merged = deepMerge(current, config);
|
|
1236
|
+
merged._integrations = current._integrations;
|
|
1237
|
+
this.writeConfig(merged);
|
|
1238
|
+
return Promise.resolve();
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Remove config previously applied by an integration.
|
|
1242
|
+
*
|
|
1243
|
+
* Rebuilds the config by re-merging all remaining integrations' configs,
|
|
1244
|
+
* ensuring clean removal without orphaned keys.
|
|
1245
|
+
*/
|
|
1246
|
+
removeConfig(integrationId) {
|
|
1247
|
+
const current = this.readConfig();
|
|
1248
|
+
const integrations = current._integrations ?? {};
|
|
1249
|
+
if (!(integrationId in integrations)) return Promise.resolve();
|
|
1250
|
+
const remainingIntegrations = Object.fromEntries(Object.entries(integrations).filter(([key]) => key !== integrationId));
|
|
1251
|
+
let rebuilt = this.getBaseConfig(current);
|
|
1252
|
+
for (const cfg of Object.values(remainingIntegrations)) rebuilt = deepMerge(rebuilt, cfg);
|
|
1253
|
+
rebuilt._integrations = remainingIntegrations;
|
|
1254
|
+
this.writeConfig(rebuilt);
|
|
1255
|
+
return Promise.resolve();
|
|
1256
|
+
}
|
|
1257
|
+
isAvailable() {
|
|
1258
|
+
return Promise.resolve(existsSync(this.workspace));
|
|
1259
|
+
}
|
|
1260
|
+
readConfig() {
|
|
1261
|
+
if (!existsSync(this.configPath)) return {};
|
|
1262
|
+
try {
|
|
1263
|
+
return JSON.parse(readFileSync(this.configPath, "utf-8"));
|
|
1264
|
+
} catch {
|
|
1265
|
+
return {};
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
writeConfig(config) {
|
|
1269
|
+
mkdirSync(join(this.configPath, ".."), { recursive: true });
|
|
1270
|
+
writeFileSync(this.configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Extract the base config by stripping all keys that were contributed
|
|
1274
|
+
* by integrations. This is done by removing each integration's keys
|
|
1275
|
+
* from the current config.
|
|
1276
|
+
*/
|
|
1277
|
+
getBaseConfig(current) {
|
|
1278
|
+
const integrations = current._integrations ?? {};
|
|
1279
|
+
const allIntegrationKeys = /* @__PURE__ */ new Set();
|
|
1280
|
+
for (const cfg of Object.values(integrations)) for (const key of Object.keys(cfg)) allIntegrationKeys.add(key);
|
|
1281
|
+
const base = {};
|
|
1282
|
+
for (const [key, val] of Object.entries(current)) {
|
|
1283
|
+
if (key === "_integrations") continue;
|
|
1284
|
+
if (!allIntegrationKeys.has(key)) base[key] = val;
|
|
1285
|
+
}
|
|
1286
|
+
return base;
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
//#endregion
|
|
1316
1290
|
//#region src/adapter.ts
|
|
1317
1291
|
var IntegrationManagerAdapter = class {
|
|
1318
1292
|
constructor(manager) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/integrations",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
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.
|
|
16
|
+
"@alfe.ai/integration-manifest": "^0.0.2"
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"dist"
|