@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.
Files changed (3) hide show
  1. package/dist/index.d.ts +33 -11
  2. package/dist/index.js +129 -155
  3. 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, resolvedConfig);
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.1",
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.1"
16
+ "@alfe.ai/integration-manifest": "^0.0.2"
17
17
  },
18
18
  "files": [
19
19
  "dist"