mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
Merge branch 'main' into meow/control-chat-responsive
This commit is contained in:
@@ -63,6 +63,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Doctor/plugins: remove stale managed npm plugin shadow entries from the managed package lock as well as `package.json` and `node_modules`, so future npm operations do not keep referencing repaired bundled-plugin shadows. Thanks @vincentkoc.
|
||||
- Plugins/runtime state: keep the key being registered when namespace eviction runs in the same millisecond as existing entries, so `register` and `registerIfAbsent` do not report success while evicting their own fresh value. Thanks @vincentkoc.
|
||||
- Control UI/Talk: make failed Talk startup errors dismissable and clear the stale Talk error state when dismissed, so missing realtime voice provider configuration does not leave a permanent chat banner. Fixes #77071. Thanks @ijoshdavis.
|
||||
- Control UI/Talk: stop and clear failed realtime Talk sessions when dismissing runtime error banners, so the next Talk click starts a fresh session instead of only stopping the stale one. Thanks @vincentkoc.
|
||||
|
||||
@@ -104,6 +104,7 @@ function createManagedNpmPlugin(params: {
|
||||
id: string;
|
||||
packageName: string;
|
||||
version: string;
|
||||
packageLock?: boolean;
|
||||
}) {
|
||||
const npmRoot = path.join(params.stateDir, "npm");
|
||||
const packageDir = path.join(npmRoot, "node_modules", params.packageName);
|
||||
@@ -117,6 +118,37 @@ function createManagedNpmPlugin(params: {
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
if (params.packageLock) {
|
||||
fs.writeFileSync(
|
||||
path.join(npmRoot, "package-lock.json"),
|
||||
JSON.stringify({
|
||||
lockfileVersion: 3,
|
||||
packages: {
|
||||
"": {
|
||||
dependencies: {
|
||||
[params.packageName]: params.version,
|
||||
"other-plugin": "1.0.0",
|
||||
},
|
||||
},
|
||||
[`node_modules/${params.packageName}`]: {
|
||||
version: params.version,
|
||||
},
|
||||
"node_modules/other-plugin": {
|
||||
version: "1.0.0",
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
[params.packageName]: {
|
||||
version: params.version,
|
||||
},
|
||||
"other-plugin": {
|
||||
version: "1.0.0",
|
||||
},
|
||||
},
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
@@ -301,4 +333,51 @@ describe("maybeRepairPluginRegistryState", () => {
|
||||
"Removed stale managed npm plugin package",
|
||||
);
|
||||
});
|
||||
|
||||
it("removes stale managed npm packages from the package lock during repair", async () => {
|
||||
const stateDir = makeTempDir();
|
||||
const bundledDir = path.join(stateDir, "bundled", "google-meet");
|
||||
fs.mkdirSync(bundledDir, { recursive: true });
|
||||
createManagedNpmPlugin({
|
||||
stateDir,
|
||||
id: "google-meet",
|
||||
packageName: "@openclaw/google-meet",
|
||||
version: "2026.5.2",
|
||||
packageLock: true,
|
||||
});
|
||||
await writePersistedInstalledPluginIndex(createCurrentIndex(), { stateDir });
|
||||
|
||||
await maybeRepairPluginRegistryState({
|
||||
stateDir,
|
||||
candidates: [
|
||||
createBundledCandidate({
|
||||
rootDir: bundledDir,
|
||||
id: "google-meet",
|
||||
packageName: "@openclaw/google-meet",
|
||||
version: "2026.5.3",
|
||||
}),
|
||||
],
|
||||
env: hermeticEnv(),
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["google-meet"],
|
||||
entries: {
|
||||
"google-meet": {
|
||||
enabled: true,
|
||||
config: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
prompter: { shouldRepair: true },
|
||||
});
|
||||
|
||||
const packageLock = JSON.parse(
|
||||
fs.readFileSync(path.join(stateDir, "npm", "package-lock.json"), "utf8"),
|
||||
);
|
||||
expect(packageLock.packages[""].dependencies).toEqual({ "other-plugin": "1.0.0" });
|
||||
expect(packageLock.packages).not.toHaveProperty("node_modules/@openclaw/google-meet");
|
||||
expect(packageLock.dependencies).not.toHaveProperty("@openclaw/google-meet");
|
||||
expect(packageLock.dependencies).toHaveProperty("other-plugin");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,6 +58,14 @@ function readStringMap(value: unknown): Record<string, string> {
|
||||
return result;
|
||||
}
|
||||
|
||||
function deleteObjectKey(record: Record<string, unknown>, key: string): boolean {
|
||||
if (!Object.prototype.hasOwnProperty.call(record, key)) {
|
||||
return false;
|
||||
}
|
||||
delete record[key];
|
||||
return true;
|
||||
}
|
||||
|
||||
function readPackageVersion(packageDir: string): string | undefined {
|
||||
const packageJson = readJsonObject(path.join(packageDir, "package.json"));
|
||||
const version = packageJson?.version;
|
||||
@@ -137,6 +145,7 @@ function removeManagedNpmDependency(params: {
|
||||
dependencies,
|
||||
};
|
||||
saveJsonFile(npmPackageJsonPath, nextPackageJson);
|
||||
removeManagedNpmPackageLockDependency(params);
|
||||
fs.rmSync(params.packageDir, { recursive: true, force: true });
|
||||
const scopeDir = path.dirname(params.packageDir);
|
||||
if (path.basename(path.dirname(scopeDir)) === "node_modules") {
|
||||
@@ -148,6 +157,44 @@ function removeManagedNpmDependency(params: {
|
||||
}
|
||||
}
|
||||
|
||||
function removeManagedNpmPackageLockDependency(params: {
|
||||
npmRoot: string;
|
||||
packageName: string;
|
||||
}): void {
|
||||
const packageLockPath = path.join(params.npmRoot, "package-lock.json");
|
||||
const packageLock = readJsonObject(packageLockPath);
|
||||
if (!packageLock) {
|
||||
return;
|
||||
}
|
||||
|
||||
let changed = false;
|
||||
const packages = packageLock.packages;
|
||||
if (isRecord(packages)) {
|
||||
const rootPackage = packages[""];
|
||||
if (isRecord(rootPackage)) {
|
||||
const rootDependencies = readStringMap(rootPackage.dependencies);
|
||||
if (deleteObjectKey(rootDependencies, params.packageName)) {
|
||||
changed = true;
|
||||
if (Object.keys(rootDependencies).length === 0) {
|
||||
delete rootPackage.dependencies;
|
||||
} else {
|
||||
rootPackage.dependencies = rootDependencies;
|
||||
}
|
||||
}
|
||||
}
|
||||
changed = deleteObjectKey(packages, `node_modules/${params.packageName}`) || changed;
|
||||
}
|
||||
|
||||
const dependencies = packageLock.dependencies;
|
||||
if (isRecord(dependencies)) {
|
||||
changed = deleteObjectKey(dependencies, params.packageName) || changed;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
saveJsonFile(packageLockPath, packageLock);
|
||||
}
|
||||
}
|
||||
|
||||
function maybeRepairStaleManagedNpmBundledPlugins(
|
||||
params: PluginRegistryDoctorRepairParams,
|
||||
): boolean {
|
||||
|
||||
Reference in New Issue
Block a user