mirror of
https://github.com/ValueCell-ai/ClawX.git
synced 2026-08-14 08:53:09 +00:00
upgrade openclaw to 7.1-2 (#1236)
This commit is contained in:
@@ -36,7 +36,10 @@ import { prependPathEntry } from '../utils/env-path';
|
||||
import { copyPluginFromNodeModules, fixupPluginManifest, cpSyncSafe, buildCandidateSources, repairTrustedOfficialPluginInstallRecords, removeTrustedOfficialPluginInstallRecord, resolvePluginNpmPackagePath } from '../utils/plugin-install';
|
||||
import { safeRmSync } from '../utils/safe-fs';
|
||||
import { CLAWX_OPENAI_IMAGE_PROVIDER_KEY } from '../utils/openclaw-image-relay-constants';
|
||||
import { ensureOpenClaw2026_7_1UpgradeSnapshot } from '../utils/openclaw-upgrade-snapshot';
|
||||
import {
|
||||
ensureOpenClaw2026_7_1UpgradeSnapshot,
|
||||
quarantineLegacyUpdateCheckState,
|
||||
} from '../utils/openclaw-upgrade-snapshot';
|
||||
import { stripSystemdSupervisorEnv } from './config-sync-env';
|
||||
import { cleanupAgentsSymlinkedSkills, cleanupStalePluginRuntimeDeps } from './skills-symlink-cleanup';
|
||||
import {
|
||||
@@ -651,6 +654,19 @@ export async function prepareGatewayLaunchContext(port: number): Promise<Gateway
|
||||
}
|
||||
});
|
||||
|
||||
await measureAsync(timingsMs, 'legacyUpdateCheckCleanupMs', async () => {
|
||||
try {
|
||||
const cleanup = await quarantineLegacyUpdateCheckState();
|
||||
if (cleanup.status === 'quarantined') {
|
||||
logger.info(
|
||||
`[upgrade] Quarantined conflicting legacy update-check state: ${cleanup.sourcePath} → ${cleanup.backupPath}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('[upgrade] Failed to quarantine legacy update-check state:', error);
|
||||
}
|
||||
});
|
||||
|
||||
const appSettings = await measureAsync(timingsMs, 'settingsMs', getAllSettings);
|
||||
const prelaunchSummary = await measureAsync(timingsMs, 'prelaunchSyncMs', async () => (
|
||||
await syncGatewayConfigBeforeLaunch(appSettings, openclawDir)
|
||||
|
||||
@@ -735,6 +735,10 @@ export class GatewayManager extends EventEmitter {
|
||||
logger.info('Gateway ready fallback RPC router probe succeeded');
|
||||
this.resetGatewayReadyFallback();
|
||||
this.setStatus({ gatewayReady: true });
|
||||
// A fast Gateway can emit gateway.ready before the WebSocket client is
|
||||
// attached. A successful router probe is equivalent readiness, so it
|
||||
// must also complete the one-time migration snapshot lifecycle.
|
||||
void this.cleanupOpenClawUpgradeSnapshot();
|
||||
}
|
||||
} catch (error) {
|
||||
this.capabilityMonitor.recordCoreProbe({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { chmod, copyFile, lstat, mkdir, readdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
|
||||
import { basename, dirname, join, relative, resolve } from 'node:path';
|
||||
import { DatabaseSync } from 'node:sqlite';
|
||||
import { resolveOpenClawConfigPath, resolveOpenClawStateDir } from './paths';
|
||||
|
||||
const UPGRADE_ID = 'openclaw-2026.7.1';
|
||||
@@ -23,6 +24,12 @@ export type OpenClawUpgradeSnapshotCleanupResult = {
|
||||
snapshotDir: string;
|
||||
};
|
||||
|
||||
export type LegacyUpdateCheckCleanupResult = {
|
||||
status: 'quarantined' | 'missing' | 'deferred';
|
||||
sourcePath: string;
|
||||
backupPath?: string;
|
||||
};
|
||||
|
||||
type SnapshotOptions = {
|
||||
stateDir?: string;
|
||||
configPath?: string;
|
||||
@@ -57,6 +64,44 @@ async function copyFileIfPresent(source: string, destination: string, copied: st
|
||||
copied.push(destination);
|
||||
}
|
||||
|
||||
async function pathExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await lstat(path);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveAvailableBackupPath(basePath: string): Promise<string> {
|
||||
if (!await pathExists(basePath)) return basePath;
|
||||
|
||||
const timestamp = Date.now();
|
||||
for (let suffix = 0; suffix < 100; suffix += 1) {
|
||||
const candidate = `${basePath}.${timestamp}${suffix === 0 ? '' : `-${suffix}`}`;
|
||||
if (!await pathExists(candidate)) return candidate;
|
||||
}
|
||||
throw new Error(`Could not allocate backup path for ${basePath}`);
|
||||
}
|
||||
|
||||
function hasCanonicalUpdateCheckState(sqlitePath: string): boolean {
|
||||
let db: DatabaseSync | undefined;
|
||||
try {
|
||||
db = new DatabaseSync(sqlitePath, { readOnly: true });
|
||||
const row = db.prepare(`
|
||||
SELECT 1 AS present
|
||||
FROM update_check_state
|
||||
WHERE state_key = ?
|
||||
LIMIT 1
|
||||
`).get('default') as { present?: number } | undefined;
|
||||
return row?.present === 1;
|
||||
} catch {
|
||||
return false;
|
||||
} finally {
|
||||
db?.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function copyTree(
|
||||
sourceRoot: string,
|
||||
destinationRoot: string,
|
||||
@@ -161,6 +206,46 @@ export async function ensureOpenClaw2026_7_1UpgradeSnapshot(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenClaw 2026.7.1 refuses Gateway readiness when the legacy update-check JSON
|
||||
* differs from an existing canonical SQLite row. The JSON contains updater
|
||||
* bookkeeping only, and upstream would archive it when both copies match. Once
|
||||
* SQLite has the canonical row, move the legacy file out of the active state
|
||||
* root so a harmless mismatch cannot trap startup or an ineffective doctor
|
||||
* retry loop. If SQLite has no row yet, leave the JSON for upstream to import.
|
||||
*/
|
||||
export async function quarantineLegacyUpdateCheckState(
|
||||
options: Pick<SnapshotOptions, 'stateDir'> = {},
|
||||
): Promise<LegacyUpdateCheckCleanupResult> {
|
||||
const stateDir = resolve(options.stateDir ?? resolveOpenClawStateDir());
|
||||
const sourcePath = join(stateDir, 'update-check.json');
|
||||
let sourceInfo;
|
||||
try {
|
||||
sourceInfo = await lstat(sourcePath);
|
||||
} catch {
|
||||
return { status: 'missing', sourcePath };
|
||||
}
|
||||
if (!sourceInfo.isFile() && !sourceInfo.isSymbolicLink()) {
|
||||
return { status: 'deferred', sourcePath };
|
||||
}
|
||||
|
||||
const sqlitePath = join(stateDir, 'state', 'openclaw.sqlite');
|
||||
if (!hasCanonicalUpdateCheckState(sqlitePath)) {
|
||||
return { status: 'deferred', sourcePath };
|
||||
}
|
||||
|
||||
const backupDir = join(stateDir, 'backups');
|
||||
await mkdir(backupDir, { recursive: true, mode: SNAPSHOT_DIR_MODE });
|
||||
const backupPath = await resolveAvailableBackupPath(
|
||||
join(backupDir, `clawx-${UPGRADE_ID}-legacy-update-check.json`),
|
||||
);
|
||||
await rename(sourcePath, backupPath);
|
||||
if (sourceInfo.isFile()) {
|
||||
await chmod(backupPath, SNAPSHOT_FILE_MODE);
|
||||
}
|
||||
return { status: 'quarantined', sourcePath, backupPath };
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the one-time OpenClaw 2026.7.1 pre-migration snapshot after Gateway
|
||||
* startup succeeds so duplicated config/auth/SQLite secrets do not linger.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# OpenClaw Config Delivery
|
||||
|
||||
ClawX bundles OpenClaw 2026.7.1. OpenClaw owns the field-level decision between a no-op snapshot update, hot application, subsystem restart, and in-process Gateway restart.
|
||||
ClawX bundles OpenClaw 2026.7.1-2. OpenClaw owns the field-level decision between a no-op snapshot update, hot application, subsystem restart, and in-process Gateway restart.
|
||||
|
||||
Provider, Agent, Channel, skill, proxy, image-generation, and plugin-install helpers express config changes as mutators. One Main-owned coordinator owns selection of the authoritative baseline and the commit:
|
||||
|
||||
@@ -16,6 +16,8 @@ Gateway WebSocket tracing must redact the complete serialized `raw` payload for
|
||||
|
||||
Coordinator-backed reads follow the same authority rule: prefer the runtime-shaped `config.get.config` object while Gateway is running and use JSON5 file parsing while it is not. Compound views derive all config-backed fields from one snapshot.
|
||||
|
||||
OpenClaw 2026.7.1 keeps auth-profile SQLite snapshots in memory. After a completed auth-store write batch, ClawX calls `secrets.reload` once when Gateway is running. `config.set` does not replace this refresh. Agent `models.json` needs no explicit RPC because OpenClaw re-reads it when its file fingerprint changes.
|
||||
OpenClaw 2026.7.1-2 keeps auth-profile SQLite snapshots in memory. After a completed auth-store write batch, ClawX calls `secrets.reload` once when Gateway is running. `config.set` does not replace this refresh. Agent `models.json` needs no explicit RPC because OpenClaw re-reads it when its file fingerprint changes.
|
||||
|
||||
Before launch, upgrade compatibility cleanup checks the canonical `state/openclaw.sqlite` update-check row. If it exists, the SQLite row is authoritative and any legacy root `update-check.json` is moved with restrictive permissions under `backups/`; otherwise the JSON remains in place for OpenClaw to import. This cleanup runs after the one-time upgrade snapshot and prevents harmless updater-bookkeeping differences from blocking Gateway readiness or triggering an ineffective doctor retry. The snapshot is removed after either the native ready event or a successful RPC-router readiness fallback, covering the race where a fast Gateway emits readiness before ClawX attaches its WebSocket client.
|
||||
|
||||
Full ClawX process replacement remains necessary after a successful coordinator commit when values are injected only at process creation, including proxy environment changes, or for explicit manual lifecycle and health/crash recovery. OpenClaw config categories must not be duplicated as a ClawX restart whitelist.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
id: upgrade-openclaw-2026-7-1-2
|
||||
title: Upgrade the bundled OpenClaw runtime to 2026.7.1-2
|
||||
scenario: gateway-backend-communication
|
||||
taskType: runtime-bridge
|
||||
intent: Apply the OpenClaw 2026.7.1 correction releases without regressing ClawX channels, providers, models, ACP chat, or packaged runtime behavior.
|
||||
touchedAreas:
|
||||
- package.json
|
||||
- pnpm-lock.yaml
|
||||
- electron/gateway/config-sync.ts
|
||||
- electron/gateway/manager.ts
|
||||
- electron/utils/openclaw-upgrade-snapshot.ts
|
||||
- tests/unit/gateway-ready-fallback.test.ts
|
||||
- tests/unit/openclaw-bundle-config.test.ts
|
||||
- tests/unit/openclaw-upgrade-snapshot.test.ts
|
||||
- harness/reference/openclaw-config-delivery.md
|
||||
- harness/specs/tasks/upgrade-openclaw-2026-7-1.md
|
||||
- harness/specs/tasks/upgrade-openclaw-2026-7-1-2.md
|
||||
expectedUserBehavior:
|
||||
- A direct ClawX 0.5.1 upgrade from OpenClaw 2026.6.10 preserves configuration, authentication, sessions, ClawX-managed selected models, channel credentials, and managed channel plugin identities through the inherited 2026.7.1 migration.
|
||||
- Manual catalog-only model references require explicit upgrade preflight because the upstream unconfigured global model catalog is smaller in 2026.7.1 than in 2026.6.10; explicit ClawX-managed provider models are not dependent on that catalog entry remaining built in.
|
||||
- Existing configuration, authentication, sessions, models, and channel credentials remain usable after upgrading from OpenClaw 2026.7.1.
|
||||
- The supported ClawX channel catalog and effective plugin identities remain unchanged.
|
||||
- Official managed plugin updates tolerate repaired npm lock metadata and singleton-array npm view responses.
|
||||
- Codex turns continue to their authoritative terminal result after progress replies.
|
||||
- Legacy migration residue, Memory Core derived-sidecar conflicts, and guarded WSL EROFS permission results do not cause avoidable Gateway startup failures.
|
||||
- When canonical SQLite update-check state already exists, conflicting legacy update-check JSON is backed up outside the active state root before launch instead of causing a doctor retry loop.
|
||||
requiredProfiles:
|
||||
- fast
|
||||
- comms
|
||||
requiredTests:
|
||||
- tests/unit/gateway-ready-fallback.test.ts
|
||||
- tests/unit/openclaw-bundle-config.test.ts
|
||||
- tests/unit/openclaw-upgrade-snapshot.test.ts
|
||||
- tests/unit/plugin-install.test.ts
|
||||
- tests/unit/plugin-install-index.test.ts
|
||||
- tests/unit/channel-config.test.ts
|
||||
- tests/unit/openclaw-auth.test.ts
|
||||
- tests/unit/acp-chat-service.test.ts
|
||||
- tests/unit/gateway-startup-orchestrator.test.ts
|
||||
acceptance:
|
||||
- The OpenClaw runtime is pinned to 2026.7.1-2 and resolves @openclaw/ai 2026.7.1-2.
|
||||
- External channel plugin package versions and ClawX's supported channel catalog remain unchanged because the correction release does not change channel APIs or manifests.
|
||||
- Provider and model configuration behavior remains unchanged because the correction release does not change provider or model catalog sources.
|
||||
- The bundled-runtime patch and pruning pipeline succeeds against OpenClaw 2026.7.1-2.
|
||||
- Prelaunch preserves legacy update-check JSON for upstream import when SQLite has no canonical row, and quarantines it with restrictive permissions when SQLite is already authoritative.
|
||||
- The pre-migration snapshot is removed after either the native ready event or an equivalent successful RPC-router readiness fallback, so a missed early event does not leave duplicated auth/SQLite secrets behind.
|
||||
- Type checks, targeted channel/provider/ACP tests, communication regression checks, and harness validation pass.
|
||||
docs:
|
||||
required: false
|
||||
---
|
||||
|
||||
Use this task spec for the correction-release upgrade from OpenClaw 2026.7.1
|
||||
to 2026.7.1-2. It inherits the runtime and migration compatibility work captured
|
||||
in `upgrade-openclaw-2026-7-1.md` and focuses on proving that the correction
|
||||
release does not widen ClawX's channel or model surface.
|
||||
@@ -29,6 +29,7 @@ touchedAreas:
|
||||
- tests/e2e/cron-run-live-status.spec.ts
|
||||
- tests/unit/gateway-startup-recovery.test.ts
|
||||
- tests/unit/gateway-startup-orchestrator.test.ts
|
||||
- tests/unit/gateway-ready-fallback.test.ts
|
||||
- tests/unit/openclaw-cli.test.ts
|
||||
- tests/unit/openclaw-bundle-config.test.ts
|
||||
- tests/unit/openclaw-upgrade-snapshot.test.ts
|
||||
@@ -39,11 +40,13 @@ touchedAreas:
|
||||
- README.zh-CN.md
|
||||
- README.ja-JP.md
|
||||
- README.ru-RU.md
|
||||
- harness/reference/openclaw-config-delivery.md
|
||||
- harness/specs/scenarios/gateway-backend-communication.md
|
||||
- harness/specs/rules/acp-chat-state-and-history.md
|
||||
- harness/specs/tasks/upgrade-openclaw-2026-7-1.md
|
||||
- harness/specs/tasks/upgrade-openclaw-2026-7-1-2.md
|
||||
expectedUserBehavior:
|
||||
- Existing OpenClaw 2026.6.10 configuration, authentication, sessions, and channel credentials remain usable after upgrade, with a one-time pre-migration snapshot of migration-critical config/auth/SQLite state that is removed after Gateway startup succeeds.
|
||||
- Existing ClawX-managed OpenClaw 2026.6.10 configuration, authentication, sessions, selected provider models, and channel credentials remain usable after upgrade, with a one-time pre-migration snapshot of migration-critical config/auth/SQLite state that is removed after Gateway startup succeeds.
|
||||
- ClawX reconciles old managed channel-plugin install records with its current mirrored extensions, removes records for unconfigured mirrors, and links declared `openclaw` peers to the bundled runtime before OpenClaw's post-core payload smoke check.
|
||||
- ClawX starts and communicates with the bundled OpenClaw 2026.7.1 Gateway, including migration and control-plane safe-mode startup states.
|
||||
- ClawX registers the compatibility-patched WeCom mirror as a local-path install with static channel metadata so OpenClaw startup migration does not replace it with the raw mismatched npm package.
|
||||
@@ -79,6 +82,7 @@ acceptance:
|
||||
- Configured mirrored plugins that declare an `openclaw` peer have a runtime link to the current bundled OpenClaw package before migration validation; stale install records for unconfigured mirrors are removed so missing directories cannot block startup.
|
||||
- Gateway recovery performs at most one doctor repair per startup flow and does not retry fatal runtime, EX_CONFIG, invalid migration, or active migration-lease failures indefinitely.
|
||||
- Electron Main reads current cron history through Gateway `cron.runs`, retains legacy JSONL as a compatibility fallback, and supplements only empty cron ACP replay in memory without replacing non-empty replay.
|
||||
- ClawX-managed selected models remain explicit in provider configuration, but upgrade preflight treats manual catalog-only model references separately: OpenClaw's unconfigured `models list --all` catalog changes from 140 entries in 2026.6.10 to 86 in 2026.7.1, removing the built-in Venice, Fireworks, Tencent TokenHub, and Z.AI catalog groups plus several older Moonshot entries.
|
||||
- ACP 1.1 type checks, targeted runtime tests, communication regression checks, and harness validation pass.
|
||||
docs:
|
||||
required: true
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@
|
||||
"monaco-editor": "^0.55.1",
|
||||
"mpg123-decoder": "^1.0.3",
|
||||
"ms": "^2.1.3",
|
||||
"openclaw": "2026.7.1",
|
||||
"openclaw": "2026.7.1-2",
|
||||
"opusscript": "^0.1.1",
|
||||
"pdfjs-dist": "^5.7.284",
|
||||
"playwright-core": "1.59.1",
|
||||
|
||||
Generated
+33
-45
@@ -65,7 +65,7 @@ importers:
|
||||
version: 1.3.7
|
||||
'@larksuite/openclaw-lark':
|
||||
specifier: 2026.7.9
|
||||
version: 2026.7.9(openclaw@2026.7.1(encoding@0.1.13))
|
||||
version: 2026.7.9(openclaw@2026.7.1-2(encoding@0.1.13))
|
||||
'@larksuiteoapi/node-sdk':
|
||||
specifier: ^1.61.1
|
||||
version: 1.62.0
|
||||
@@ -74,13 +74,13 @@ importers:
|
||||
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@openclaw/discord':
|
||||
specifier: 2026.7.1
|
||||
version: 2026.7.1(openclaw@2026.7.1(encoding@0.1.13))
|
||||
version: 2026.7.1(openclaw@2026.7.1-2(encoding@0.1.13))
|
||||
'@openclaw/qqbot':
|
||||
specifier: 2026.7.1
|
||||
version: 2026.7.1(openclaw@2026.7.1(encoding@0.1.13))
|
||||
version: 2026.7.1(openclaw@2026.7.1-2(encoding@0.1.13))
|
||||
'@openclaw/whatsapp':
|
||||
specifier: 2026.7.1
|
||||
version: 2026.7.1(openclaw@2026.7.1(encoding@0.1.13))
|
||||
version: 2026.7.1(openclaw@2026.7.1-2(encoding@0.1.13))
|
||||
'@playwright/test':
|
||||
specifier: ^1.56.1
|
||||
version: 1.59.0
|
||||
@@ -125,7 +125,7 @@ importers:
|
||||
version: 0.34.48
|
||||
'@soimy/dingtalk':
|
||||
specifier: 3.6.6
|
||||
version: 3.6.6(openclaw@2026.7.1(encoding@0.1.13))
|
||||
version: 3.6.6(openclaw@2026.7.1-2(encoding@0.1.13))
|
||||
'@streamdown/cjk':
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.4)(unified@11.0.5)
|
||||
@@ -140,7 +140,7 @@ importers:
|
||||
version: 1.1.0
|
||||
'@tencent-weixin/openclaw-weixin':
|
||||
specifier: ^2.4.6
|
||||
version: 2.4.6(openclaw@2026.7.1(encoding@0.1.13))
|
||||
version: 2.4.6(openclaw@2026.7.1-2(encoding@0.1.13))
|
||||
'@testing-library/jest-dom':
|
||||
specifier: ^6.9.1
|
||||
version: 6.9.1
|
||||
@@ -173,7 +173,7 @@ importers:
|
||||
version: 5.2.0(vite@7.3.1(@types/node@25.5.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.9.0))
|
||||
'@wecom/wecom-openclaw-plugin':
|
||||
specifier: 2026.7.2
|
||||
version: 2026.7.2(openclaw@2026.7.1(encoding@0.1.13))
|
||||
version: 2026.7.2(openclaw@2026.7.1-2(encoding@0.1.13))
|
||||
'@whiskeysockets/baileys':
|
||||
specifier: 7.0.0-rc.9
|
||||
version: 7.0.0-rc.9(audio-decode@2.2.3)(jimp@1.6.1)(sharp@0.34.5)
|
||||
@@ -262,8 +262,8 @@ importers:
|
||||
specifier: ^2.1.3
|
||||
version: 2.1.3
|
||||
openclaw:
|
||||
specifier: 2026.7.1
|
||||
version: 2026.7.1(encoding@0.1.13)
|
||||
specifier: 2026.7.1-2
|
||||
version: 2026.7.1-2(encoding@0.1.13)
|
||||
opusscript:
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1
|
||||
@@ -1496,8 +1496,8 @@ packages:
|
||||
resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
'@openclaw/ai@2026.7.1':
|
||||
resolution: {integrity: sha512-FsKy5DXSHf4qyN8Huoz/10HZRgoEwLF4uk8UWaCafaIler+q5Fsl51HcrIqIrEe0S38OT7LOaxnR++MOshAlmw==}
|
||||
'@openclaw/ai@2026.7.1-2':
|
||||
resolution: {integrity: sha512-st+NH0cxlQqdbEur//yYqM7WlYBjeEBnop3cztJTSCONKjv6LNoGguI9cH65asZG94FdM/39z857isRGPnZvEw==}
|
||||
engines: {node: '>=22.19.0'}
|
||||
|
||||
'@openclaw/discord@2026.7.1':
|
||||
@@ -5315,8 +5315,8 @@ packages:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
openclaw@2026.7.1:
|
||||
resolution: {integrity: sha512-ge/Xss99CHAjPL/ikmH/UFoiOrjcxDB4sW3y9mhyCD+dYW3wzV7TKbAVdkrXFgAG2d2BjpJofP97zUZ+umxo8g==}
|
||||
openclaw@2026.7.1-2:
|
||||
resolution: {integrity: sha512-ycF3yPcbjN6bUPeaUx6Mh6vze1hQWoD3CT/wWcmD7a8xaHHHRUaAlaq+lFxMHf1ssEgODVAwjlzYqp2twkYZ7g==}
|
||||
engines: {node: '>=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -6366,10 +6366,6 @@ packages:
|
||||
resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tar@7.5.15:
|
||||
resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tar@7.5.19:
|
||||
resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -7369,7 +7365,7 @@ snapshots:
|
||||
ora: 5.4.1
|
||||
read-binary-file-arch: 1.0.6
|
||||
semver: 7.7.4
|
||||
tar: 7.5.15
|
||||
tar: 7.5.19
|
||||
yargs: 17.7.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -8021,7 +8017,7 @@ snapshots:
|
||||
|
||||
'@kurkle/color@0.3.4': {}
|
||||
|
||||
'@larksuite/openclaw-lark@2026.7.9(openclaw@2026.7.1(encoding@0.1.13))':
|
||||
'@larksuite/openclaw-lark@2026.7.9(openclaw@2026.7.1-2(encoding@0.1.13))':
|
||||
dependencies:
|
||||
'@larksuiteoapi/node-sdk': 1.66.1
|
||||
'@sinclair/typebox': 0.34.49
|
||||
@@ -8029,7 +8025,7 @@ snapshots:
|
||||
undici-types: 8.3.0
|
||||
zod: 4.4.3
|
||||
optionalDependencies:
|
||||
openclaw: 2026.7.1(encoding@0.1.13)
|
||||
openclaw: 2026.7.1-2(encoding@0.1.13)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- debug
|
||||
@@ -8281,7 +8277,7 @@ snapshots:
|
||||
dependencies:
|
||||
semver: 7.7.4
|
||||
|
||||
'@openclaw/ai@2026.7.1(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)':
|
||||
'@openclaw/ai@2026.7.1-2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)':
|
||||
dependencies:
|
||||
'@anthropic-ai/sdk': 0.109.1(zod@4.4.3)
|
||||
'@google/genai': 2.10.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))
|
||||
@@ -8301,9 +8297,9 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@openclaw/discord@2026.7.1(openclaw@2026.7.1(encoding@0.1.13))':
|
||||
'@openclaw/discord@2026.7.1(openclaw@2026.7.1-2(encoding@0.1.13))':
|
||||
optionalDependencies:
|
||||
openclaw: 2026.7.1(encoding@0.1.13)
|
||||
openclaw: 2026.7.1-2(encoding@0.1.13)
|
||||
|
||||
'@openclaw/fs-safe@0.4.1':
|
||||
optionalDependencies:
|
||||
@@ -8314,13 +8310,13 @@ snapshots:
|
||||
dependencies:
|
||||
undici: 8.5.0
|
||||
|
||||
'@openclaw/qqbot@2026.7.1(openclaw@2026.7.1(encoding@0.1.13))':
|
||||
'@openclaw/qqbot@2026.7.1(openclaw@2026.7.1-2(encoding@0.1.13))':
|
||||
optionalDependencies:
|
||||
openclaw: 2026.7.1(encoding@0.1.13)
|
||||
openclaw: 2026.7.1-2(encoding@0.1.13)
|
||||
|
||||
'@openclaw/whatsapp@2026.7.1(openclaw@2026.7.1(encoding@0.1.13))':
|
||||
'@openclaw/whatsapp@2026.7.1(openclaw@2026.7.1-2(encoding@0.1.13))':
|
||||
optionalDependencies:
|
||||
openclaw: 2026.7.1(encoding@0.1.13)
|
||||
openclaw: 2026.7.1-2(encoding@0.1.13)
|
||||
|
||||
'@opentelemetry/semantic-conventions@1.43.0': {}
|
||||
|
||||
@@ -9004,7 +9000,7 @@ snapshots:
|
||||
- '@emnapi/core'
|
||||
- '@emnapi/runtime'
|
||||
|
||||
'@soimy/dingtalk@3.6.6(openclaw@2026.7.1(encoding@0.1.13))':
|
||||
'@soimy/dingtalk@3.6.6(openclaw@2026.7.1-2(encoding@0.1.13))':
|
||||
dependencies:
|
||||
axios: 1.13.6(debug@4.4.3)
|
||||
dingtalk-stream: 2.1.5
|
||||
@@ -9013,7 +9009,7 @@ snapshots:
|
||||
pdf-parse: 2.4.5
|
||||
zod: 4.4.3
|
||||
optionalDependencies:
|
||||
openclaw: 2026.7.1(encoding@0.1.13)
|
||||
openclaw: 2026.7.1-2(encoding@0.1.13)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- debug
|
||||
@@ -9059,9 +9055,9 @@ snapshots:
|
||||
dependencies:
|
||||
qrcode-terminal: 0.12.0
|
||||
|
||||
'@tencent-weixin/openclaw-weixin@2.4.6(openclaw@2026.7.1(encoding@0.1.13))':
|
||||
'@tencent-weixin/openclaw-weixin@2.4.6(openclaw@2026.7.1-2(encoding@0.1.13))':
|
||||
dependencies:
|
||||
openclaw: 2026.7.1(encoding@0.1.13)
|
||||
openclaw: 2026.7.1-2(encoding@0.1.13)
|
||||
qrcode-terminal: 0.12.0
|
||||
zod: 4.4.3
|
||||
|
||||
@@ -9550,7 +9546,7 @@ snapshots:
|
||||
- debug
|
||||
- utf-8-validate
|
||||
|
||||
'@wecom/wecom-openclaw-plugin@2026.7.2(openclaw@2026.7.1(encoding@0.1.13))':
|
||||
'@wecom/wecom-openclaw-plugin@2026.7.2(openclaw@2026.7.1-2(encoding@0.1.13))':
|
||||
dependencies:
|
||||
'@wecom/aibot-node-sdk': 1.0.6
|
||||
fast-xml-parser: 5.7.3
|
||||
@@ -9558,7 +9554,7 @@ snapshots:
|
||||
undici: 7.24.6
|
||||
zod: 4.4.3
|
||||
optionalDependencies:
|
||||
openclaw: 2026.7.1(encoding@0.1.13)
|
||||
openclaw: 2026.7.1-2(encoding@0.1.13)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- debug
|
||||
@@ -9980,7 +9976,7 @@ snapshots:
|
||||
minipass-pipeline: 1.2.4
|
||||
p-map: 7.0.4
|
||||
ssri: 12.0.0
|
||||
tar: 7.5.15
|
||||
tar: 7.5.19
|
||||
unique-filename: 4.0.0
|
||||
|
||||
cacheable-lookup@5.0.4: {}
|
||||
@@ -12625,7 +12621,7 @@ snapshots:
|
||||
nopt: 8.1.0
|
||||
proc-log: 5.0.0
|
||||
semver: 7.7.4
|
||||
tar: 7.5.15
|
||||
tar: 7.5.19
|
||||
tinyglobby: 0.2.15
|
||||
which: 5.0.0
|
||||
transitivePeerDependencies:
|
||||
@@ -12699,7 +12695,7 @@ snapshots:
|
||||
ws: 8.21.0
|
||||
zod: 4.4.3
|
||||
|
||||
openclaw@2026.7.1(encoding@0.1.13):
|
||||
openclaw@2026.7.1-2(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@agentclientprotocol/sdk': 1.1.0(zod@4.4.3)
|
||||
'@anthropic-ai/sdk': 0.109.1(zod@4.4.3)
|
||||
@@ -12714,7 +12710,7 @@ snapshots:
|
||||
'@mistralai/mistralai': 2.4.0
|
||||
'@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3)
|
||||
'@mozilla/readability': 0.6.0
|
||||
'@openclaw/ai': 2026.7.1(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)
|
||||
'@openclaw/ai': 2026.7.1-2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)
|
||||
'@openclaw/fs-safe': 0.4.1
|
||||
'@openclaw/proxyline': 0.3.3(undici@8.5.0)
|
||||
'@silvia-odwyer/photon-node': 0.3.4
|
||||
@@ -13971,14 +13967,6 @@ snapshots:
|
||||
minizlib: 3.1.0
|
||||
yallist: 5.0.0
|
||||
|
||||
tar@7.5.15:
|
||||
dependencies:
|
||||
'@isaacs/fs-minipass': 4.0.1
|
||||
chownr: 3.0.0
|
||||
minipass: 7.1.3
|
||||
minizlib: 3.1.0
|
||||
yallist: 5.0.0
|
||||
|
||||
tar@7.5.19:
|
||||
dependencies:
|
||||
'@isaacs/fs-minipass': 4.0.1
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const { removeUpgradeSnapshotMock } = vi.hoisted(() => ({
|
||||
removeUpgradeSnapshotMock: vi.fn(async () => ({ status: 'removed' as const, snapshotDir: '/tmp/snapshot' })),
|
||||
}));
|
||||
|
||||
vi.mock('electron', () => ({
|
||||
app: {
|
||||
getPath: () => '/tmp',
|
||||
@@ -23,6 +27,10 @@ vi.mock('@electron/utils/config', () => ({
|
||||
PORTS: { OPENCLAW_GATEWAY: 18789 },
|
||||
}));
|
||||
|
||||
vi.mock('@electron/utils/openclaw-upgrade-snapshot', () => ({
|
||||
removeOpenClaw2026_7_1UpgradeSnapshot: removeUpgradeSnapshotMock,
|
||||
}));
|
||||
|
||||
vi.mock('@electron/gateway/startup-orchestrator', () => ({
|
||||
runGatewayStartupSequence: vi.fn(async () => {
|
||||
throw new Error('startup unavailable in unit test');
|
||||
@@ -103,6 +111,7 @@ describe('GatewayManager gatewayReady fallback', () => {
|
||||
const readyUpdate = statusUpdates.find((u) => u.gatewayReady === true);
|
||||
expect(readyUpdate).toBeDefined();
|
||||
expect(rpcSpy).toHaveBeenCalledWith('system-presence', {}, 5_000);
|
||||
expect(removeUpgradeSnapshotMock).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('keeps gatewayReady=false when fallback RPC router probe fails', async () => {
|
||||
|
||||
@@ -8,14 +8,14 @@ import { describe, expect, it } from 'vitest';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
describe('openclaw bundle config', () => {
|
||||
it('pins the OpenClaw 2026.7.1 runtime compatibility matrix', () => {
|
||||
it('pins the OpenClaw 2026.7.1-2 runtime compatibility matrix', () => {
|
||||
const packageJson = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json'), 'utf8')) as {
|
||||
dependencies?: Record<string, string>;
|
||||
devDependencies?: Record<string, string>;
|
||||
};
|
||||
expect(packageJson.dependencies?.['@agentclientprotocol/sdk']).toBe('1.1.0');
|
||||
expect(packageJson.devDependencies).toMatchObject({
|
||||
openclaw: '2026.7.1',
|
||||
openclaw: '2026.7.1-2',
|
||||
electron: '40.10.6',
|
||||
'@openclaw/discord': '2026.7.1',
|
||||
'@openclaw/qqbot': '2026.7.1',
|
||||
@@ -66,7 +66,8 @@ describe('openclaw bundle config', () => {
|
||||
expect(lockfile).not.toContain("'@soimy/dingtalk@3.6.4':");
|
||||
expect(lockfile).not.toContain("'@wecom/wecom-openclaw-plugin@2026.6.23':");
|
||||
expect(lockfile).not.toContain("'@larksuite/openclaw-lark@2026.6.10':");
|
||||
expect(lockfile).toContain("'@openclaw/ai@2026.7.1':");
|
||||
expect(lockfile).not.toContain("'@openclaw/ai@2026.7.1':");
|
||||
expect(lockfile).toContain("'@openclaw/ai@2026.7.1-2':");
|
||||
});
|
||||
|
||||
it('includes Electron runtime-only packages needed in packaged builds', async () => {
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
import { chmod, mkdtemp, mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { DatabaseSync } from 'node:sqlite';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import {
|
||||
ensureOpenClaw2026_7_1UpgradeSnapshot,
|
||||
quarantineLegacyUpdateCheckState,
|
||||
removeOpenClaw2026_7_1UpgradeSnapshot,
|
||||
} from '@electron/utils/openclaw-upgrade-snapshot';
|
||||
|
||||
@@ -64,6 +66,57 @@ describe('OpenClaw 2026.7.1 upgrade snapshot', () => {
|
||||
.resolves.toBe('{"version":"old"}\n');
|
||||
});
|
||||
|
||||
it('quarantines legacy update-check JSON when SQLite is already authoritative', async () => {
|
||||
const stateDir = await createTempStateDir();
|
||||
const sourcePath = join(stateDir, 'update-check.json');
|
||||
const sqliteDir = join(stateDir, 'state');
|
||||
const sqlitePath = join(sqliteDir, 'openclaw.sqlite');
|
||||
await mkdir(sqliteDir, { recursive: true });
|
||||
await writeFile(sourcePath, '{"lastCheckedAt":"legacy"}\n');
|
||||
|
||||
const db = new DatabaseSync(sqlitePath);
|
||||
db.exec(`
|
||||
CREATE TABLE update_check_state (
|
||||
state_key TEXT PRIMARY KEY,
|
||||
last_checked_at TEXT
|
||||
);
|
||||
INSERT INTO update_check_state (state_key, last_checked_at)
|
||||
VALUES ('default', 'canonical');
|
||||
`);
|
||||
db.close();
|
||||
|
||||
const result = await quarantineLegacyUpdateCheckState({ stateDir });
|
||||
expect(result.status).toBe('quarantined');
|
||||
expect(result.backupPath).toContain('clawx-openclaw-2026.7.1-legacy-update-check.json');
|
||||
await expect(stat(sourcePath)).rejects.toThrow();
|
||||
await expect(readFile(result.backupPath!, 'utf8')).resolves.toBe('{"lastCheckedAt":"legacy"}\n');
|
||||
expect((await stat(result.backupPath!)).mode & 0o777).toBe(0o600);
|
||||
});
|
||||
|
||||
it('leaves legacy update-check JSON for upstream import when SQLite has no canonical row', async () => {
|
||||
const stateDir = await createTempStateDir();
|
||||
const sourcePath = join(stateDir, 'update-check.json');
|
||||
const sqliteDir = join(stateDir, 'state');
|
||||
const sqlitePath = join(sqliteDir, 'openclaw.sqlite');
|
||||
await mkdir(sqliteDir, { recursive: true });
|
||||
await writeFile(sourcePath, '{"lastCheckedAt":"legacy"}\n');
|
||||
|
||||
const db = new DatabaseSync(sqlitePath);
|
||||
db.exec(`
|
||||
CREATE TABLE update_check_state (
|
||||
state_key TEXT PRIMARY KEY,
|
||||
last_checked_at TEXT
|
||||
);
|
||||
`);
|
||||
db.close();
|
||||
|
||||
await expect(quarantineLegacyUpdateCheckState({ stateDir })).resolves.toMatchObject({
|
||||
status: 'deferred',
|
||||
sourcePath,
|
||||
});
|
||||
await expect(readFile(sourcePath, 'utf8')).resolves.toBe('{"lastCheckedAt":"legacy"}\n');
|
||||
});
|
||||
|
||||
it('removes the snapshot directory after successful cleanup', async () => {
|
||||
const stateDir = await createTempStateDir();
|
||||
const configPath = join(stateDir, 'openclaw.json');
|
||||
|
||||
Reference in New Issue
Block a user