@alfe.ai/integrations 0.3.0 → 0.3.1
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 +35 -1
- package/dist/index.js +62 -8
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -314,9 +314,17 @@ declare class Installer {
|
|
|
314
314
|
interface RuntimeApplier {
|
|
315
315
|
/** The runtime identifier (e.g. 'openclaw', 'nanoclaw') */
|
|
316
316
|
readonly runtime: string;
|
|
317
|
-
/**
|
|
317
|
+
/**
|
|
318
|
+
* Install a plugin package into this runtime.
|
|
319
|
+
*
|
|
320
|
+
* `opts.integrationId` identifies the owning integration so the applier can
|
|
321
|
+
* distinguish a legitimate same-integration pin bump from a cross-integration
|
|
322
|
+
* pin conflict (two integrations pinning the same plugin at different
|
|
323
|
+
* versions). See `OpenClawApplier`'s first-writer-wins guard.
|
|
324
|
+
*/
|
|
318
325
|
applyPlugin(pkg: string, integrationInstallPath: string, opts?: {
|
|
319
326
|
force?: boolean;
|
|
327
|
+
integrationId?: string;
|
|
320
328
|
}): Promise<void>;
|
|
321
329
|
/**
|
|
322
330
|
* Optional: pre-trust an integration's full plugin set in one write before
|
|
@@ -995,6 +1003,24 @@ declare class OpenClawApplier implements RuntimeApplier {
|
|
|
995
1003
|
* this instance performed (0 = never). See HEAL_MIN_INTERVAL_MS.
|
|
996
1004
|
*/
|
|
997
1005
|
private lastHealAt;
|
|
1006
|
+
/**
|
|
1007
|
+
* Cross-integration plugin-pin claims made during THIS process lifetime,
|
|
1008
|
+
* keyed by bare package name. `applyPlugin` is now version-aware for both
|
|
1009
|
+
* force and non-force callers — it actively reinstalls a plugin whose
|
|
1010
|
+
* installed version differs from the pinned spec (previously the non-force
|
|
1011
|
+
* path keyed only on bare-name presence and silently ignored a pin bump). If
|
|
1012
|
+
* two integrations pin the SAME plugin at DIFFERENT versions, that
|
|
1013
|
+
* version-aware reinstall would make them fight — each reconcile pass
|
|
1014
|
+
* uninstall+reinstalls the other's version forever. This map records the
|
|
1015
|
+
* first-applied pin per package so a later, divergent pin from a DIFFERENT
|
|
1016
|
+
* integration is refused (first-writer-wins) with a WARN instead of thrashing.
|
|
1017
|
+
* A later apply from the SAME integration (a legitimate pin bump across an
|
|
1018
|
+
* upgrade) is NOT a conflict and proceeds. Cleared per-package on
|
|
1019
|
+
* `removePlugin`, and wholesale on daemon restart. Convention is single-owner
|
|
1020
|
+
* per plugin (see the `alfe` manifest note) — this only turns a convention
|
|
1021
|
+
* violation into a stable warning.
|
|
1022
|
+
*/
|
|
1023
|
+
private readonly appliedPluginPins;
|
|
998
1024
|
constructor(options: OpenClawApplierOptions);
|
|
999
1025
|
/**
|
|
1000
1026
|
* Convenience: `openclaw config set <args>`, UNLOCKED + retried.
|
|
@@ -1080,8 +1106,16 @@ declare class OpenClawApplier implements RuntimeApplier {
|
|
|
1080
1106
|
private healMalformedStateDb;
|
|
1081
1107
|
applyPlugin(spec: string, _installPath?: string, opts?: {
|
|
1082
1108
|
force?: boolean;
|
|
1109
|
+
integrationId?: string;
|
|
1083
1110
|
}): Promise<void>;
|
|
1084
1111
|
private applyPluginLocked;
|
|
1112
|
+
/**
|
|
1113
|
+
* Record the pin a caller just applied for a package, keyed by bare name, so
|
|
1114
|
+
* a later divergent pin from a DIFFERENT integration in this process can be
|
|
1115
|
+
* refused (first-writer-wins). Bare (versionless) pins are untrackable and
|
|
1116
|
+
* ignored — they can't conflict on version. See `appliedPluginPins`.
|
|
1117
|
+
*/
|
|
1118
|
+
private recordPluginPin;
|
|
1085
1119
|
/**
|
|
1086
1120
|
* Ensure one or more plugins are in plugins.allow in openclaw.json, UNLOCKED.
|
|
1087
1121
|
* Uses `openclaw config set` to avoid clobbering OpenClaw's own file format.
|
package/dist/index.js
CHANGED
|
@@ -1271,7 +1271,10 @@ var IntegrationManager = class {
|
|
|
1271
1271
|
for (const plugin of plugins) {
|
|
1272
1272
|
this.log.info(`Applying plugin ${plugin.package} to ${runtimeName}`);
|
|
1273
1273
|
try {
|
|
1274
|
-
await applier.applyPlugin(plugin.package, installPath, {
|
|
1274
|
+
await applier.applyPlugin(plugin.package, installPath, {
|
|
1275
|
+
force: opts?.forcePlugins,
|
|
1276
|
+
integrationId
|
|
1277
|
+
});
|
|
1275
1278
|
} catch (err) {
|
|
1276
1279
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1277
1280
|
this.log.error(`Failed to apply plugin ${plugin.package}: ${msg}`);
|
|
@@ -2301,6 +2304,24 @@ var OpenClawApplier = class {
|
|
|
2301
2304
|
* this instance performed (0 = never). See HEAL_MIN_INTERVAL_MS.
|
|
2302
2305
|
*/
|
|
2303
2306
|
lastHealAt = 0;
|
|
2307
|
+
/**
|
|
2308
|
+
* Cross-integration plugin-pin claims made during THIS process lifetime,
|
|
2309
|
+
* keyed by bare package name. `applyPlugin` is now version-aware for both
|
|
2310
|
+
* force and non-force callers — it actively reinstalls a plugin whose
|
|
2311
|
+
* installed version differs from the pinned spec (previously the non-force
|
|
2312
|
+
* path keyed only on bare-name presence and silently ignored a pin bump). If
|
|
2313
|
+
* two integrations pin the SAME plugin at DIFFERENT versions, that
|
|
2314
|
+
* version-aware reinstall would make them fight — each reconcile pass
|
|
2315
|
+
* uninstall+reinstalls the other's version forever. This map records the
|
|
2316
|
+
* first-applied pin per package so a later, divergent pin from a DIFFERENT
|
|
2317
|
+
* integration is refused (first-writer-wins) with a WARN instead of thrashing.
|
|
2318
|
+
* A later apply from the SAME integration (a legitimate pin bump across an
|
|
2319
|
+
* upgrade) is NOT a conflict and proceeds. Cleared per-package on
|
|
2320
|
+
* `removePlugin`, and wholesale on daemon restart. Convention is single-owner
|
|
2321
|
+
* per plugin (see the `alfe` manifest note) — this only turns a convention
|
|
2322
|
+
* violation into a stable warning.
|
|
2323
|
+
*/
|
|
2324
|
+
appliedPluginPins = /* @__PURE__ */ new Map();
|
|
2304
2325
|
constructor(options) {
|
|
2305
2326
|
const home = options.home ?? options.workspace;
|
|
2306
2327
|
if (!home) throw new Error("OpenClawApplier requires `home` (or legacy `workspace`) option");
|
|
@@ -2484,25 +2505,43 @@ var OpenClawApplier = class {
|
|
|
2484
2505
|
}
|
|
2485
2506
|
async applyPluginLocked(spec, opts) {
|
|
2486
2507
|
const pkg = stripPluginVersion(spec);
|
|
2508
|
+
const pinnedVersion = pluginSpecVersion(spec);
|
|
2509
|
+
const claim = this.appliedPluginPins.get(pkg);
|
|
2510
|
+
if (pinnedVersion !== void 0 && claim !== void 0 && claim.version !== pinnedVersion && claim.integrationId !== opts?.integrationId) {
|
|
2511
|
+
log$3.warn({
|
|
2512
|
+
pkg,
|
|
2513
|
+
requestedVersion: pinnedVersion,
|
|
2514
|
+
keptVersion: claim.version,
|
|
2515
|
+
requestedBy: opts?.integrationId,
|
|
2516
|
+
ownedBy: claim.integrationId
|
|
2517
|
+
}, "Conflicting plugin pin across integrations — keeping the first-applied version (first-writer-wins)");
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2487
2520
|
await this.ensurePluginsAllowUnlocked(pkg);
|
|
2488
2521
|
this.cleanupUntrackedExtensionInstall(pkg);
|
|
2489
|
-
if (
|
|
2490
|
-
const pinnedVersion = pluginSpecVersion(spec);
|
|
2522
|
+
if (this.isPluginInstalled(pkg)) {
|
|
2491
2523
|
const installedVersion = this.installedPluginVersion(pkg);
|
|
2492
|
-
|
|
2493
|
-
|
|
2524
|
+
const versionsComparable = pinnedVersion !== void 0 && installedVersion !== void 0;
|
|
2525
|
+
if (versionsComparable && installedVersion === pinnedVersion) {
|
|
2526
|
+
if (opts?.force) log$3.info({
|
|
2494
2527
|
pkg,
|
|
2495
2528
|
spec,
|
|
2496
2529
|
version: installedVersion
|
|
2497
2530
|
}, "Force mode — installed version already matches pinned spec, skipping uninstall+reinstall");
|
|
2531
|
+
this.recordPluginPin(pkg, pinnedVersion, opts?.integrationId);
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
if (!(versionsComparable && installedVersion !== pinnedVersion) && !opts?.force) {
|
|
2535
|
+
this.recordPluginPin(pkg, pinnedVersion, opts?.integrationId);
|
|
2498
2536
|
return;
|
|
2499
2537
|
}
|
|
2500
2538
|
log$3.info({
|
|
2501
2539
|
pkg,
|
|
2502
2540
|
spec,
|
|
2503
2541
|
installedVersion,
|
|
2504
|
-
pinnedVersion
|
|
2505
|
-
|
|
2542
|
+
pinnedVersion,
|
|
2543
|
+
force: opts?.force ?? false
|
|
2544
|
+
}, "Reinstalling plugin to converge on the pinned version");
|
|
2506
2545
|
try {
|
|
2507
2546
|
await this.removePluginUnlocked(pkg);
|
|
2508
2547
|
} catch (err) {
|
|
@@ -2510,7 +2549,7 @@ var OpenClawApplier = class {
|
|
|
2510
2549
|
pkg,
|
|
2511
2550
|
spec,
|
|
2512
2551
|
err: err instanceof Error ? err.message : String(err)
|
|
2513
|
-
}, "Failed to uninstall plugin
|
|
2552
|
+
}, "Failed to uninstall plugin before reinstall — proceeding");
|
|
2514
2553
|
}
|
|
2515
2554
|
}
|
|
2516
2555
|
if (!this.isPluginInstalled(pkg)) {
|
|
@@ -2547,6 +2586,20 @@ var OpenClawApplier = class {
|
|
|
2547
2586
|
setTimeout(r, 500);
|
|
2548
2587
|
});
|
|
2549
2588
|
}
|
|
2589
|
+
this.recordPluginPin(pkg, pinnedVersion, opts?.integrationId);
|
|
2590
|
+
}
|
|
2591
|
+
/**
|
|
2592
|
+
* Record the pin a caller just applied for a package, keyed by bare name, so
|
|
2593
|
+
* a later divergent pin from a DIFFERENT integration in this process can be
|
|
2594
|
+
* refused (first-writer-wins). Bare (versionless) pins are untrackable and
|
|
2595
|
+
* ignored — they can't conflict on version. See `appliedPluginPins`.
|
|
2596
|
+
*/
|
|
2597
|
+
recordPluginPin(pkg, version, integrationId) {
|
|
2598
|
+
if (version === void 0) return;
|
|
2599
|
+
this.appliedPluginPins.set(pkg, {
|
|
2600
|
+
version,
|
|
2601
|
+
integrationId
|
|
2602
|
+
});
|
|
2550
2603
|
}
|
|
2551
2604
|
/**
|
|
2552
2605
|
* Ensure one or more plugins are in plugins.allow in openclaw.json, UNLOCKED.
|
|
@@ -2692,6 +2745,7 @@ var OpenClawApplier = class {
|
|
|
2692
2745
|
*/
|
|
2693
2746
|
async removePluginUnlocked(spec) {
|
|
2694
2747
|
const pkg = stripPluginVersion(spec);
|
|
2748
|
+
this.appliedPluginPins.delete(pkg);
|
|
2695
2749
|
await this.execOpenClawHealing([
|
|
2696
2750
|
"plugins",
|
|
2697
2751
|
"uninstall",
|
package/package.json
CHANGED