@alfe.ai/integrations 0.1.2 → 0.1.4
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 +52 -8
- package/dist/index.js +113 -28
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -264,7 +264,15 @@ interface RuntimeApplier {
|
|
|
264
264
|
* for the eventual `getCredentials(provider)` generalisation.
|
|
265
265
|
*/
|
|
266
266
|
interface CredentialsResolver {
|
|
267
|
-
|
|
267
|
+
/**
|
|
268
|
+
* Resolve credentials for env interpolation. `opts.connectionId`
|
|
269
|
+
* scopes resolution to a specific connection (Custom Connections),
|
|
270
|
+
* returning a flat field map; without it, resolution is
|
|
271
|
+
* provider-scoped (the agent's primary connection for the provider).
|
|
272
|
+
*/
|
|
273
|
+
getCredentials: (provider: string, opts?: {
|
|
274
|
+
connectionId?: string;
|
|
275
|
+
}) => Promise<Record<string, unknown> | undefined>;
|
|
268
276
|
}
|
|
269
277
|
/**
|
|
270
278
|
* Daemon-level platform context exposed to `{{alfe.<key>}}` env
|
|
@@ -298,7 +306,9 @@ declare class McpApplier {
|
|
|
298
306
|
* the daemon-hosted bundler will pick the change up via the store
|
|
299
307
|
* watcher and reconcile its children.
|
|
300
308
|
*/
|
|
301
|
-
applyForIntegration(integrationId: string, servers: McpServerDeclaration[], mergedConfig: Record<string, unknown
|
|
309
|
+
applyForIntegration(integrationId: string, servers: McpServerDeclaration[], mergedConfig: Record<string, unknown>, opts?: {
|
|
310
|
+
connectionId?: string;
|
|
311
|
+
}): Promise<string[]>;
|
|
302
312
|
/**
|
|
303
313
|
* Drop every entry owned by this integration. Store writes are
|
|
304
314
|
* durable before `removeServersByOwner` resolves; the daemon's
|
|
@@ -314,10 +324,37 @@ declare class McpApplier {
|
|
|
314
324
|
*
|
|
315
325
|
* These define the payloads for integration lifecycle operations.
|
|
316
326
|
*/
|
|
327
|
+
/**
|
|
328
|
+
* Embedded git pointer for a Custom Connection-driven install. When
|
|
329
|
+
* present on an install, the manager clones `manifestRepo` at
|
|
330
|
+
* `manifestCommit` and installs from the directory containing
|
|
331
|
+
* `manifestPath`, bypassing the central registry resolver (Custom
|
|
332
|
+
* Connection manifests are per-tenant and never registry-published).
|
|
333
|
+
* Defined structurally so the gateway's `DesiredCustomSource` is
|
|
334
|
+
* assignable without a cross-package import.
|
|
335
|
+
*/
|
|
336
|
+
interface CustomInstallSource {
|
|
337
|
+
/** Backing custom Connection — used to scope credential resolution. */
|
|
338
|
+
connectionId: string;
|
|
339
|
+
/** `https://github.com/{owner}/{repo}` clone URL. */
|
|
340
|
+
manifestRepo: string;
|
|
341
|
+
/** Path to the manifest inside the repo (its directory is the install root). */
|
|
342
|
+
manifestPath: string;
|
|
343
|
+
/** Symbolic ref the operator pinned (display/provenance only). */
|
|
344
|
+
manifestRef: string;
|
|
345
|
+
/** Blob SHA of the manifest file content (not checkout-able). */
|
|
346
|
+
manifestSha: string;
|
|
347
|
+
/** Checkout-able commit SHA to clone. */
|
|
348
|
+
manifestCommit: string;
|
|
349
|
+
/** Already-parsed-and-validated manifest blob (optional convenience). */
|
|
350
|
+
manifest?: unknown;
|
|
351
|
+
}
|
|
317
352
|
interface IntegrationInstallParams {
|
|
318
353
|
name: string;
|
|
319
354
|
version?: string;
|
|
320
355
|
config?: Record<string, unknown>;
|
|
356
|
+
/** Set for Custom Connection-driven installs (clone-from-git). */
|
|
357
|
+
customSource?: CustomInstallSource;
|
|
321
358
|
}
|
|
322
359
|
interface IntegrationRemoveParams {
|
|
323
360
|
name: string;
|
|
@@ -670,7 +707,7 @@ declare class OpenClawApplier implements RuntimeApplier {
|
|
|
670
707
|
private skillsDir;
|
|
671
708
|
private trackingPath;
|
|
672
709
|
constructor(options: OpenClawApplierOptions);
|
|
673
|
-
applyPlugin(
|
|
710
|
+
applyPlugin(spec: string, _installPath?: string, opts?: {
|
|
674
711
|
force?: boolean;
|
|
675
712
|
}): Promise<void>;
|
|
676
713
|
/**
|
|
@@ -697,7 +734,7 @@ declare class OpenClawApplier implements RuntimeApplier {
|
|
|
697
734
|
* write through the npm path with a proper tracking record.
|
|
698
735
|
*/
|
|
699
736
|
private cleanupUntrackedExtensionInstall;
|
|
700
|
-
removePlugin(
|
|
737
|
+
removePlugin(spec: string): Promise<void>;
|
|
701
738
|
applySkill(name: string, srcPath: string): Promise<void>;
|
|
702
739
|
applyClawHubSkill(slug: string): Promise<void>;
|
|
703
740
|
removeClawHubSkill(slug: string): Promise<void>;
|
|
@@ -723,6 +760,13 @@ declare class OpenClawApplier implements RuntimeApplier {
|
|
|
723
760
|
//#endregion
|
|
724
761
|
//#region src/lock.d.ts
|
|
725
762
|
interface RuntimePluginEntry {
|
|
763
|
+
/**
|
|
764
|
+
* The full npm install spec from the manifest (e.g. `@scope/name@1.2.3`).
|
|
765
|
+
* Consumers that key off the installed plugin's identity (file-system
|
|
766
|
+
* lookups, openclaw allowlist, cross-integration claim matching) must
|
|
767
|
+
* pass this through `stripPluginVersion` from `./plugin-spec.js` first.
|
|
768
|
+
* Only `openclaw plugins install` should receive the unstripped spec.
|
|
769
|
+
*/
|
|
726
770
|
package: string;
|
|
727
771
|
sourceIntegration: string;
|
|
728
772
|
integrationVersion: string;
|
|
@@ -785,7 +829,7 @@ interface IIntegrationManager {
|
|
|
785
829
|
installedAt?: string;
|
|
786
830
|
}[]>;
|
|
787
831
|
/** Install an integration at a specific version with config */
|
|
788
|
-
install(integrationId: string, version: string, config: unknown): Promise<void>;
|
|
832
|
+
install(integrationId: string, version: string, config: unknown, customSource?: CustomInstallSource): Promise<void>;
|
|
789
833
|
/** Activate an installed integration */
|
|
790
834
|
activate(integrationId: string): Promise<{
|
|
791
835
|
configApplied: boolean;
|
|
@@ -795,7 +839,7 @@ interface IIntegrationManager {
|
|
|
795
839
|
/** Uninstall an integration */
|
|
796
840
|
uninstall(integrationId: string): Promise<void>;
|
|
797
841
|
/** Reinstall an integration — full teardown then fresh install + activate */
|
|
798
|
-
reinstall(integrationId: string, version: string, config: unknown): Promise<{
|
|
842
|
+
reinstall(integrationId: string, version: string, config: unknown, customSource?: CustomInstallSource): Promise<{
|
|
799
843
|
configApplied: boolean;
|
|
800
844
|
}>;
|
|
801
845
|
/** Check if an integration's install directory and manifest are intact on disk */
|
|
@@ -816,13 +860,13 @@ declare class IntegrationManagerAdapter implements IIntegrationManager {
|
|
|
816
860
|
status: string;
|
|
817
861
|
installedAt?: string;
|
|
818
862
|
}[]>;
|
|
819
|
-
install(integrationId: string, version: string, config: unknown): Promise<void>;
|
|
863
|
+
install(integrationId: string, version: string, config: unknown, customSource?: CustomInstallSource): Promise<void>;
|
|
820
864
|
activate(integrationId: string): Promise<{
|
|
821
865
|
configApplied: boolean;
|
|
822
866
|
}>;
|
|
823
867
|
deactivate(integrationId: string): Promise<void>;
|
|
824
868
|
uninstall(integrationId: string): Promise<void>;
|
|
825
|
-
reinstall(integrationId: string, version: string, config: unknown): Promise<{
|
|
869
|
+
reinstall(integrationId: string, version: string, config: unknown, customSource?: CustomInstallSource): Promise<{
|
|
826
870
|
configApplied: boolean;
|
|
827
871
|
}>;
|
|
828
872
|
isInstallIntact(integrationId: string): Promise<boolean>;
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { execFile, spawn } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
4
|
import { homedir, platform, tmpdir } from "node:os";
|
|
5
|
-
import { closeSync, cpSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { closeSync, copyFileSync, 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";
|
|
8
8
|
//#region src/registry.ts
|
|
@@ -796,6 +796,26 @@ async function runHookWithContext(integrationPath, hookScript, options) {
|
|
|
796
796
|
return runHook(integrationPath, hookScript, buildHookEnv(options));
|
|
797
797
|
}
|
|
798
798
|
//#endregion
|
|
799
|
+
//#region src/plugin-spec.ts
|
|
800
|
+
/**
|
|
801
|
+
* Plugin spec parsing.
|
|
802
|
+
*
|
|
803
|
+
* Manifests express plugin installs as `package: "@scope/name@version"` strings
|
|
804
|
+
* (npm specifier form). The npm install command accepts the full spec, but file-
|
|
805
|
+
* system checks, plugin-allowlist entries, and cross-integration claim matching
|
|
806
|
+
* need the bare package name (npm stores `node_modules/<name>`, not
|
|
807
|
+
* `node_modules/<name>@<ver>`).
|
|
808
|
+
*
|
|
809
|
+
* `stripPluginVersion` returns the bare package name from any spec, handling
|
|
810
|
+
* scoped (`@scope/name@1.2.3`) and unscoped (`name@1.2.3`) forms. Specs without
|
|
811
|
+
* a version are returned unchanged.
|
|
812
|
+
*/
|
|
813
|
+
function stripPluginVersion(spec) {
|
|
814
|
+
const at = spec.lastIndexOf("@");
|
|
815
|
+
if (at <= 0) return spec;
|
|
816
|
+
return spec.slice(0, at);
|
|
817
|
+
}
|
|
818
|
+
//#endregion
|
|
799
819
|
//#region src/integration-manager.ts
|
|
800
820
|
/**
|
|
801
821
|
* Integration Manager — full lifecycle management for Alfe integrations.
|
|
@@ -848,6 +868,37 @@ function resolveInstallsForRuntime(manifest, runtime) {
|
|
|
848
868
|
config: runtimeSpecific?.config
|
|
849
869
|
};
|
|
850
870
|
}
|
|
871
|
+
/**
|
|
872
|
+
* Build a clone target for a Custom Connection-driven install,
|
|
873
|
+
* bypassing the central registry resolver. The manifest's directory
|
|
874
|
+
* inside the repo becomes the install root (subdir extraction); the
|
|
875
|
+
* checkout-able `manifestCommit` pins the clone.
|
|
876
|
+
*/
|
|
877
|
+
function buildCustomResolved(id, cs) {
|
|
878
|
+
if (id.includes("/") || id.includes("\\") || id.includes("..")) throw new Error(`Refusing custom integration id with path separators: ${id}`);
|
|
879
|
+
const dir = dirname(cs.manifestPath);
|
|
880
|
+
const subdir = dir === "." || dir === "" ? void 0 : dir;
|
|
881
|
+
const manifestVersion = cs.manifest?.version;
|
|
882
|
+
return {
|
|
883
|
+
id,
|
|
884
|
+
repository: cs.manifestRepo,
|
|
885
|
+
version: manifestVersion ?? "unknown",
|
|
886
|
+
commit: cs.manifestCommit,
|
|
887
|
+
subdir,
|
|
888
|
+
description: ""
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* The lifecycle assumes the manifest is named `alfe-integration.yaml`.
|
|
893
|
+
* Custom Connections may point at a differently-named file, so after
|
|
894
|
+
* cloning we alias the actual file to the canonical name if needed.
|
|
895
|
+
*/
|
|
896
|
+
function ensureCanonicalManifestName(installPath, manifestPath) {
|
|
897
|
+
const canonical = join(installPath, "alfe-integration.yaml");
|
|
898
|
+
if (existsSync(canonical)) return;
|
|
899
|
+
const actual = join(installPath, basename(manifestPath));
|
|
900
|
+
if (existsSync(actual)) copyFileSync(actual, canonical);
|
|
901
|
+
}
|
|
851
902
|
var IntegrationManager = class {
|
|
852
903
|
log = createLogger("IntegrationManager");
|
|
853
904
|
state;
|
|
@@ -883,7 +934,7 @@ var IntegrationManager = class {
|
|
|
883
934
|
* They are applied during activate() via runtime appliers.
|
|
884
935
|
*/
|
|
885
936
|
async install(params) {
|
|
886
|
-
const { name, version, config } = params;
|
|
937
|
+
const { name, version, config, customSource } = params;
|
|
887
938
|
if (!name) return this.err("INVALID_PARAMS", "Integration name is required");
|
|
888
939
|
const existing = this.state.get(name);
|
|
889
940
|
if (existing) {
|
|
@@ -906,13 +957,21 @@ var IntegrationManager = class {
|
|
|
906
957
|
status: "installing",
|
|
907
958
|
version: version ?? "unknown",
|
|
908
959
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
909
|
-
config: {}
|
|
960
|
+
config: {},
|
|
961
|
+
customConnectionId: customSource?.connectionId
|
|
910
962
|
});
|
|
911
963
|
try {
|
|
912
|
-
|
|
913
|
-
|
|
964
|
+
let resolved;
|
|
965
|
+
if (customSource) {
|
|
966
|
+
resolved = buildCustomResolved(name, customSource);
|
|
967
|
+
this.log.info(`Custom Connection install: ${name} from ${resolved.repository}@${resolved.commit}`);
|
|
968
|
+
} else {
|
|
969
|
+
resolved = await this.resolver.resolve(name, version);
|
|
970
|
+
this.log.info(`Resolved ${name}@${resolved.version} from ${resolved.repository}`);
|
|
971
|
+
}
|
|
914
972
|
const installPath = await this.installer.install(resolved);
|
|
915
973
|
this.log.info(`Cloned to ${installPath}`);
|
|
974
|
+
if (customSource) ensureCanonicalManifestName(installPath, customSource.manifestPath);
|
|
916
975
|
const manifestPath = join(installPath, "alfe-integration.yaml");
|
|
917
976
|
if (!existsSync(manifestPath)) throw new Error(`No alfe-integration.yaml found in ${installPath}`);
|
|
918
977
|
const manifest = parseManifestFile(manifestPath);
|
|
@@ -939,7 +998,8 @@ var IntegrationManager = class {
|
|
|
939
998
|
status: "installed",
|
|
940
999
|
version: manifest.version,
|
|
941
1000
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
942
|
-
config: config ?? {}
|
|
1001
|
+
config: config ?? {},
|
|
1002
|
+
customConnectionId: customSource?.connectionId
|
|
943
1003
|
});
|
|
944
1004
|
this.log.info(`Integration "${name}" installed successfully`);
|
|
945
1005
|
return {
|
|
@@ -1097,7 +1157,7 @@ var IntegrationManager = class {
|
|
|
1097
1157
|
...secretEntries ? Object.fromEntries(secretEntries) : {}
|
|
1098
1158
|
};
|
|
1099
1159
|
this.log.info(`Applying ${String(mcpServers.length)} MCP server(s) for ${integrationId} via bundler manager`);
|
|
1100
|
-
await this.mcpApplier.applyForIntegration(integrationId, mcpServers, mergedConfig);
|
|
1160
|
+
await this.mcpApplier.applyForIntegration(integrationId, mcpServers, mergedConfig, { connectionId: entry.customConnectionId });
|
|
1101
1161
|
}
|
|
1102
1162
|
this.state.setStatus(integrationId, "active");
|
|
1103
1163
|
if (manifest.hooks.post_activate) {
|
|
@@ -1166,7 +1226,7 @@ var IntegrationManager = class {
|
|
|
1166
1226
|
const claimedPluginsByRuntime = /* @__PURE__ */ new Map();
|
|
1167
1227
|
const claimedSkillsByRuntime = /* @__PURE__ */ new Map();
|
|
1168
1228
|
for (const [rtName, state] of Object.entries(remaining.runtimes)) {
|
|
1169
|
-
claimedPluginsByRuntime.set(rtName, new Set(state.plugins.map((p) => p.package)));
|
|
1229
|
+
claimedPluginsByRuntime.set(rtName, new Set(state.plugins.map((p) => stripPluginVersion(p.package))));
|
|
1170
1230
|
claimedSkillsByRuntime.set(rtName, new Set(state.skills.map((s) => s.name)));
|
|
1171
1231
|
}
|
|
1172
1232
|
for (const [runtimeName, entries] of Object.entries(removed)) {
|
|
@@ -1182,7 +1242,7 @@ var IntegrationManager = class {
|
|
|
1182
1242
|
const keepPlugins = claimedPluginsByRuntime.get(runtimeName) ?? /* @__PURE__ */ new Set();
|
|
1183
1243
|
const keepSkills = claimedSkillsByRuntime.get(runtimeName) ?? /* @__PURE__ */ new Set();
|
|
1184
1244
|
for (const plugin of entries.plugins) {
|
|
1185
|
-
if (keepPlugins.has(plugin.package)) {
|
|
1245
|
+
if (keepPlugins.has(stripPluginVersion(plugin.package))) {
|
|
1186
1246
|
this.log.info(`Keeping plugin ${plugin.package} on ${runtimeName} — still claimed by another active integration`);
|
|
1187
1247
|
continue;
|
|
1188
1248
|
}
|
|
@@ -1520,6 +1580,19 @@ var IntegrationManager = class {
|
|
|
1520
1580
|
const execFileAsync = promisify(execFile);
|
|
1521
1581
|
const log$1 = createLogger("OpenClawApplier");
|
|
1522
1582
|
const DEFAULT_SKILLS_DIR = join(homedir(), ".alfe", "skills");
|
|
1583
|
+
/**
|
|
1584
|
+
* Bundled OpenClaw plugins that ship inside the runtime (not installed as npm
|
|
1585
|
+
* `@alfe.ai/openclaw-*` packages) and must always be present in `plugins.allow`.
|
|
1586
|
+
*
|
|
1587
|
+
* OpenClaw 2026.6.8+ treats a non-empty `plugins.allow` as an EXCLUSIVE allowlist:
|
|
1588
|
+
* any plugin absent from it is gated off even when its manifest is
|
|
1589
|
+
* `enabledByDefault`. Because we build `plugins.allow` incrementally from the npm
|
|
1590
|
+
* plugins we `applyPlugin`, bundled plugins never land in it and are silently
|
|
1591
|
+
* disabled. Keep this list minimal — only bundled plugins Alfe actually relies on.
|
|
1592
|
+
* Providers (anthropic/openai/elevenlabs/…) are intentionally excluded: Alfe routes
|
|
1593
|
+
* LLM/voice traffic through its own AI proxy, so leaving them gated off is correct.
|
|
1594
|
+
*/
|
|
1595
|
+
const BUNDLED_BASELINE_ALLOW = ["browser"];
|
|
1523
1596
|
function flattenConfig(obj, prefix = "") {
|
|
1524
1597
|
const entries = [];
|
|
1525
1598
|
for (const [key, val] of Object.entries(obj)) {
|
|
@@ -1592,16 +1665,21 @@ var OpenClawApplier = class {
|
|
|
1592
1665
|
this.skillsDir = options.skillsDir ?? DEFAULT_SKILLS_DIR;
|
|
1593
1666
|
this.trackingPath = options.configPath ?? join(this.home, "config.json");
|
|
1594
1667
|
}
|
|
1595
|
-
async applyPlugin(
|
|
1668
|
+
async applyPlugin(spec, _installPath, opts) {
|
|
1669
|
+
const pkg = stripPluginVersion(spec);
|
|
1596
1670
|
await this.ensurePluginsAllow(pkg);
|
|
1597
1671
|
this.cleanupUntrackedExtensionInstall(pkg);
|
|
1598
1672
|
if (opts?.force && this.isPluginInstalled(pkg)) {
|
|
1599
|
-
log$1.info({
|
|
1673
|
+
log$1.info({
|
|
1674
|
+
pkg,
|
|
1675
|
+
spec
|
|
1676
|
+
}, "Force mode — uninstalling plugin before reinstall");
|
|
1600
1677
|
try {
|
|
1601
1678
|
await this.removePlugin(pkg);
|
|
1602
1679
|
} catch (err) {
|
|
1603
1680
|
log$1.warn({
|
|
1604
1681
|
pkg,
|
|
1682
|
+
spec,
|
|
1605
1683
|
err: err instanceof Error ? err.message : String(err)
|
|
1606
1684
|
}, "Failed to uninstall plugin during force reinstall — proceeding");
|
|
1607
1685
|
}
|
|
@@ -1610,7 +1688,7 @@ var OpenClawApplier = class {
|
|
|
1610
1688
|
const baseArgs = [
|
|
1611
1689
|
"plugins",
|
|
1612
1690
|
"install",
|
|
1613
|
-
|
|
1691
|
+
spec,
|
|
1614
1692
|
"--force"
|
|
1615
1693
|
];
|
|
1616
1694
|
const useUnsafeFlag = pkg.startsWith("@alfe.ai/");
|
|
@@ -1632,6 +1710,7 @@ var OpenClawApplier = class {
|
|
|
1632
1710
|
if (!this.isPluginInstalled(pkg)) throw err;
|
|
1633
1711
|
log$1.warn({
|
|
1634
1712
|
pkg,
|
|
1713
|
+
spec,
|
|
1635
1714
|
err: err instanceof Error ? err.message : String(err)
|
|
1636
1715
|
}, "openclaw plugins install exited with warnings but plugin is installed");
|
|
1637
1716
|
}
|
|
@@ -1655,8 +1734,9 @@ var OpenClawApplier = class {
|
|
|
1655
1734
|
const parsed = JSON.parse(stdout.trim());
|
|
1656
1735
|
if (Array.isArray(parsed)) currentAllow = parsed;
|
|
1657
1736
|
} catch {}
|
|
1658
|
-
|
|
1659
|
-
|
|
1737
|
+
const missing = [...new Set([pkg, ...BUNDLED_BASELINE_ALLOW])].filter((id) => !currentAllow.includes(id));
|
|
1738
|
+
if (missing.length === 0) return;
|
|
1739
|
+
const updated = [...currentAllow, ...missing];
|
|
1660
1740
|
try {
|
|
1661
1741
|
await execFileAsync("openclaw", [
|
|
1662
1742
|
"config",
|
|
@@ -1731,12 +1811,12 @@ var OpenClawApplier = class {
|
|
|
1731
1811
|
}
|
|
1732
1812
|
}
|
|
1733
1813
|
}
|
|
1734
|
-
async removePlugin(
|
|
1814
|
+
async removePlugin(spec) {
|
|
1735
1815
|
await execFileAsync("openclaw", [
|
|
1736
1816
|
"plugins",
|
|
1737
1817
|
"uninstall",
|
|
1738
1818
|
"--force",
|
|
1739
|
-
|
|
1819
|
+
stripPluginVersion(spec)
|
|
1740
1820
|
], { timeout: 3e4 });
|
|
1741
1821
|
}
|
|
1742
1822
|
applySkill(name, srcPath) {
|
|
@@ -1923,12 +2003,12 @@ var McpApplier = class {
|
|
|
1923
2003
|
* the daemon-hosted bundler will pick the change up via the store
|
|
1924
2004
|
* watcher and reconcile its children.
|
|
1925
2005
|
*/
|
|
1926
|
-
async applyForIntegration(integrationId, servers, mergedConfig) {
|
|
2006
|
+
async applyForIntegration(integrationId, servers, mergedConfig, opts) {
|
|
1927
2007
|
const owner = `integration:${integrationId}`;
|
|
1928
2008
|
const applied = [];
|
|
1929
2009
|
for (const server of servers) {
|
|
1930
2010
|
const id = `${integrationId}-${server.id}`;
|
|
1931
|
-
const envResolved = await this.resolveEnv(server, mergedConfig);
|
|
2011
|
+
const envResolved = await this.resolveEnv(server, mergedConfig, opts?.connectionId);
|
|
1932
2012
|
if (envResolved == null) {
|
|
1933
2013
|
log.info({
|
|
1934
2014
|
integrationId,
|
|
@@ -1954,17 +2034,19 @@ var McpApplier = class {
|
|
|
1954
2034
|
const owner = `integration:${integrationId}`;
|
|
1955
2035
|
return this.manager.removeServersByOwner(owner);
|
|
1956
2036
|
}
|
|
1957
|
-
async resolveEnv(server, mergedConfig) {
|
|
2037
|
+
async resolveEnv(server, mergedConfig, connectionId) {
|
|
1958
2038
|
if (!server.env || Object.keys(server.env).length === 0) return {};
|
|
1959
|
-
const
|
|
2039
|
+
const provider = server.requires_credentials;
|
|
2040
|
+
const needsCredentials = Boolean(provider) || Boolean(connectionId);
|
|
1960
2041
|
let credentialsCache;
|
|
1961
2042
|
if (needsCredentials) {
|
|
1962
2043
|
try {
|
|
1963
|
-
credentialsCache = await this.credentials.getCredentials(
|
|
2044
|
+
credentialsCache = await this.credentials.getCredentials(provider ?? "custom", connectionId ? { connectionId } : void 0);
|
|
1964
2045
|
} catch (err) {
|
|
1965
2046
|
log.warn({
|
|
1966
2047
|
err: errMsg(err),
|
|
1967
|
-
provider
|
|
2048
|
+
provider,
|
|
2049
|
+
connectionId
|
|
1968
2050
|
}, "Credentials fetch threw — skipping MCP registration");
|
|
1969
2051
|
return null;
|
|
1970
2052
|
}
|
|
@@ -1975,7 +2057,8 @@ var McpApplier = class {
|
|
|
1975
2057
|
const interpolated = interpolateString(value, mergedConfig, credentialsCache, this.platform);
|
|
1976
2058
|
if (needsCredentials && hasCredentialsPlaceholder(interpolated)) {
|
|
1977
2059
|
log.warn({
|
|
1978
|
-
provider
|
|
2060
|
+
provider,
|
|
2061
|
+
connectionId,
|
|
1979
2062
|
key
|
|
1980
2063
|
}, "Credentials interpolation left a placeholder — skipping MCP registration");
|
|
1981
2064
|
return null;
|
|
@@ -2028,11 +2111,12 @@ var IntegrationManagerAdapter = class {
|
|
|
2028
2111
|
installedAt: i.installedAt
|
|
2029
2112
|
})));
|
|
2030
2113
|
}
|
|
2031
|
-
async install(integrationId, version, config) {
|
|
2114
|
+
async install(integrationId, version, config, customSource) {
|
|
2032
2115
|
const result = await this.manager.install({
|
|
2033
2116
|
name: integrationId,
|
|
2034
2117
|
version,
|
|
2035
|
-
config
|
|
2118
|
+
config,
|
|
2119
|
+
customSource
|
|
2036
2120
|
});
|
|
2037
2121
|
if (!result.ok) throw new Error(result.error?.message ?? `Failed to install ${integrationId}`);
|
|
2038
2122
|
}
|
|
@@ -2049,11 +2133,12 @@ var IntegrationManagerAdapter = class {
|
|
|
2049
2133
|
const result = await this.manager.uninstall({ name: integrationId });
|
|
2050
2134
|
if (!result.ok) throw new Error(result.error?.message ?? `Failed to uninstall ${integrationId}`);
|
|
2051
2135
|
}
|
|
2052
|
-
async reinstall(integrationId, version, config) {
|
|
2136
|
+
async reinstall(integrationId, version, config, customSource) {
|
|
2053
2137
|
const result = await this.manager.reinstall({
|
|
2054
2138
|
name: integrationId,
|
|
2055
2139
|
version,
|
|
2056
|
-
config
|
|
2140
|
+
config,
|
|
2141
|
+
customSource
|
|
2057
2142
|
});
|
|
2058
2143
|
if (!result.ok) throw new Error(result.error?.message ?? `Failed to reinstall ${integrationId}`);
|
|
2059
2144
|
return { configApplied: result.payload?.configApplied ?? false };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/integrations",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Integration lifecycle management for Alfe — registry, resolution, installation, and state",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@auriclabs/logger": "^0.1.1",
|
|
16
|
-
"@alfe.ai/integration-manifest": "^0.2.
|
|
17
|
-
"@alfe.ai/mcp-bundler": "^0.2.
|
|
16
|
+
"@alfe.ai/integration-manifest": "^0.2.1",
|
|
17
|
+
"@alfe.ai/mcp-bundler": "^0.2.1"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist"
|