Commit f9e6de26 authored by AI-甘富林's avatar AI-甘富林

fix bundled runtime startup and installer packaging

parent 8d5b8ec7
node_modules/ node_modules/
dist/ dist/
.pnpm-store/ .pnpm-store/
.turbo/ .turbo/
...@@ -6,6 +6,8 @@ dist/ ...@@ -6,6 +6,8 @@ dist/
.tmp/ .tmp/
.tmp-gateway-probe/ .tmp-gateway-probe/
.claude/ .claude/
.codex/
Microsoft/
.DS_Store .DS_Store
Thumbs.db Thumbs.db
*.log *.log
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
"dev:build": "tsup --config tsup.config.ts --watch", "dev:build": "tsup --config tsup.config.ts --watch",
"dev:start": "wait-on tcp:5173 file:dist/main/index.js && electronmon .", "dev:start": "wait-on tcp:5173 file:dist/main/index.js && electronmon .",
"lint": "tsc --noEmit", "lint": "tsc --noEmit",
"package": "corepack pnpm run build && electron-builder --config electron-builder.yml", "package": "corepack pnpm --dir ../.. run materialize:runtime && corepack pnpm run build && electron-builder --config electron-builder.yml",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
......
import path from "node:path"; import path from "node:path";
import { appendFile, readFile, writeFile } from "node:fs/promises"; import { appendFile, readFile, writeFile } from "node:fs/promises";
import { BrowserWindow, app } from "electron"; import { BrowserWindow, app } from "electron";
import { GatewayClient } from "@qjclaw/gateway-client"; import { GatewayClient } from "@qjclaw/gateway-client";
...@@ -338,12 +338,12 @@ async function runSmokeTest(window: BrowserWindow, outputPath: string): Promise< ...@@ -338,12 +338,12 @@ async function runSmokeTest(window: BrowserWindow, outputPath: string): Promise<
const credits = session.state === "authenticated" ? await api.credits.getSummary() : null; const credits = session.state === "authenticated" ? await api.credits.getSummary() : null;
const skills = session.state === "authenticated" ? await api.skills.list() : []; const skills = session.state === "authenticated" ? await api.skills.list() : [];
const workspace = await api.workspace.getSummary(); const workspace = await api.workspace.getSummary();
const readyWorkspaceSkills = workspace.skills.filter((skill) => skill.ready);
const readySkills = skills.filter((skill) => skill.ready);
const selectedSkillId = preferredSkillId const selectedSkillId = preferredSkillId
? (workspace.skills.find((skill) => skill.id === preferredSkillId)?.id ? (readyWorkspaceSkills.find((skill) => skill.id === preferredSkillId)?.id
?? skills.find((skill) => skill.id === preferredSkillId)?.id ?? readySkills.find((skill) => skill.id === preferredSkillId)?.id)
?? workspace.skills[0]?.id : (readyWorkspaceSkills[0]?.id ?? readySkills[0]?.id);
?? skills[0]?.id)
: (workspace.skills[0]?.id ?? skills[0]?.id);
const sessions = await api.chat.listSessions(); const sessions = await api.chat.listSessions();
const sessionId = state?.activeSessionId || sessions[0]?.id || "desktop-main"; const sessionId = state?.activeSessionId || sessions[0]?.id || "desktop-main";
const system = await api.system.getSummary(); const system = await api.system.getSummary();
...@@ -494,9 +494,14 @@ async function bootstrap(): Promise<void> { ...@@ -494,9 +494,14 @@ async function bootstrap(): Promise<void> {
runtimeDataDir: path.join(systemSummary.userDataPath, "runtime"), runtimeDataDir: path.join(systemSummary.userDataPath, "runtime"),
logFilePath: path.join(systemSummary.logsPath, "runtime-manager.log"), logFilePath: path.join(systemSummary.logsPath, "runtime-manager.log"),
requestedMode: resolveRequestedRuntimeMode(config.runtimeMode), requestedMode: resolveRequestedRuntimeMode(config.runtimeMode),
managedConfigResolver: async ({ action, defaultConfig }) => runtimeCloudClient.buildManagedConfig(defaultConfig, action) managedConfigResolver: async ({ action, defaultConfig }) => runtimeCloudClient.buildManagedConfig(defaultConfig, action),
strictBundledRuntime: systemSummary.isPackaged
}); });
await runtimeManager.configure(); await runtimeManager.configure();
const runtimeStatus = await runtimeManager.status();
if (systemSummary.isPackaged && runtimeStatus.payloadState !== "ready") {
throw new Error(`Packaged app bundled runtime is not ready: ${runtimeStatus.payloadState}`);
}
const gatewayClient = new GatewayClient({ const gatewayClient = new GatewayClient({
url: resolveEffectiveGatewayUrl(config.gatewayUrl, localOpenClawConfig?.gatewayUrl), url: resolveEffectiveGatewayUrl(config.gatewayUrl, localOpenClawConfig?.gatewayUrl),
...@@ -534,6 +539,8 @@ async function bootstrap(): Promise<void> { ...@@ -534,6 +539,8 @@ async function bootstrap(): Promise<void> {
}); });
if (resolveRequestedRuntimeMode(config.runtimeMode) !== "external-gateway" && (await secretManager.getApiKey())) { if (resolveRequestedRuntimeMode(config.runtimeMode) !== "external-gateway" && (await secretManager.getApiKey())) {
try {
await runtimeCloudClient.fetchConfig("init");
await runtimeManager.start(); await runtimeManager.start();
const runtimeGatewayConnection = await runtimeManager.getGatewayConnection(); const runtimeGatewayConnection = await runtimeManager.getGatewayConnection();
if (runtimeGatewayConnection.url) { if (runtimeGatewayConnection.url) {
...@@ -544,6 +551,9 @@ async function bootstrap(): Promise<void> { ...@@ -544,6 +551,9 @@ async function bootstrap(): Promise<void> {
); );
} }
await runtimeCloudSupervisor.start(); await runtimeCloudSupervisor.start();
} catch (error) {
console.error("Bundled runtime bootstrap skipped:", error instanceof Error ? error.message : String(error));
}
} }
registerDesktopIpc({ registerDesktopIpc({
......
This diff is collapsed.
...@@ -11,6 +11,12 @@ interface PreparedSkillExecution { ...@@ -11,6 +11,12 @@ interface PreparedSkillExecution {
localPath: string; localPath: string;
} }
interface BundledRuntimeManifestShape {
packagedOpenClawEntry?: string;
packagedSkillsRoot?: string;
sourceOpenClawEntry?: string;
}
const MANAGED_SKILL_PREFIX = "qjclaw-cloud-"; const MANAGED_SKILL_PREFIX = "qjclaw-cloud-";
function slugify(value: string): string { function slugify(value: string): string {
...@@ -125,7 +131,17 @@ export class RuntimeSkillBridgeService { ...@@ -125,7 +131,17 @@ export class RuntimeSkillBridgeService {
const runtimePaths = this.runtimeManager.resolveBundledPaths(); const runtimePaths = this.runtimeManager.resolveBundledPaths();
const manifestPath = path.join(runtimePaths.runtimeDir, "runtime-manifest.json"); const manifestPath = path.join(runtimePaths.runtimeDir, "runtime-manifest.json");
const manifestRaw = await readFile(manifestPath, "utf8"); const manifestRaw = await readFile(manifestPath, "utf8");
const manifest = JSON.parse(stripBom(manifestRaw)) as { sourceOpenClawEntry?: string }; const manifest = JSON.parse(stripBom(manifestRaw)) as BundledRuntimeManifestShape;
const packagedSkillsRoot = typeof manifest.packagedSkillsRoot === "string" ? manifest.packagedSkillsRoot.trim() : "";
if (packagedSkillsRoot) {
return path.resolve(runtimePaths.runtimeDir, packagedSkillsRoot);
}
const packagedEntry = typeof manifest.packagedOpenClawEntry === "string" ? manifest.packagedOpenClawEntry.trim() : "";
if (packagedEntry) {
return path.join(path.dirname(path.resolve(runtimePaths.runtimeDir, packagedEntry)), "skills");
}
const sourceEntry = typeof manifest.sourceOpenClawEntry === "string" ? manifest.sourceOpenClawEntry.trim() : ""; const sourceEntry = typeof manifest.sourceOpenClawEntry === "string" ? manifest.sourceOpenClawEntry.trim() : "";
if (sourceEntry) { if (sourceEntry) {
return path.join(path.dirname(sourceEntry), "skills"); return path.join(path.dirname(sourceEntry), "skills");
......
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
- `vendor/openclaw-runtime` is reserved for the pinned runtime payload - `vendor/openclaw-runtime` is reserved for the pinned runtime payload
- `installer-smoke.ps1` performs a real silent NSIS install into `.tmp`, launches the installed app in smoke mode, and validates packaged paths plus diagnostics output - `installer-smoke.ps1` performs a real silent NSIS install into `.tmp`, launches the installed app in smoke mode, and validates packaged paths plus diagnostics output
- `electron-smoke.ps1` launches the desktop app directly under Electron with isolated `userData` and `logs` paths, then validates execution-policy smoke output - `electron-smoke.ps1` launches the desktop app directly under Electron with isolated `userData` and `logs` paths, then validates execution-policy smoke output
- `materialize-runtime-payload.ps1` generates a local bundled runtime payload under `vendor/openclaw-runtime/` from the machine's installed `node.exe`, `openclaw`, local OpenClaw config, and a locked Python dependency set - `materialize-runtime-payload.ps1` generates a local bundled runtime payload under `vendor/openclaw-runtime/` by copying the local `node.exe`, the installed OpenClaw package, a local OpenClaw config snapshot, and a self-contained Python runtime with the locked dependency set installed into it
- `bundled-runtime-smoke.ps1` materializes the local runtime payload, forces bundled-runtime mode, and validates that Electron can launch and use the managed runtime end to end - `bundled-runtime-smoke.ps1` materializes the local runtime payload, forces bundled-runtime mode, and validates that Electron can launch and use the managed runtime end to end
- `installer-smoke.ps1` also validates the packaged Python runtime by importing the preinstalled table/document/web dependencies from `resources/vendor/openclaw-runtime/python` - `installer-smoke.ps1` validates the packaged Python runtime by importing the preinstalled table/document/web dependencies from `resources/vendor/openclaw-runtime/python/python.exe`
\ No newline at end of file
...@@ -79,7 +79,7 @@ if ($setupProcess.ExitCode -ne 0) { ...@@ -79,7 +79,7 @@ if ($setupProcess.ExitCode -ne 0) {
$installedExe = Join-Path $InstallDir 'QianjiangClaw.exe' $installedExe = Join-Path $InstallDir 'QianjiangClaw.exe'
$resourcesAsar = Join-Path $InstallDir 'resources\app.asar' $resourcesAsar = Join-Path $InstallDir 'resources\app.asar'
$runtimeResourceDir = Join-Path $InstallDir 'resources\vendor\openclaw-runtime' $runtimeResourceDir = Join-Path $InstallDir 'resources\vendor\openclaw-runtime'
$packagedPythonExe = Join-Path $runtimeResourceDir 'python\Scripts\python.exe' $packagedPythonExe = Join-Path $runtimeResourceDir 'python\python.exe'
$packagedPythonManifest = Join-Path $runtimeResourceDir 'python\python-manifest.json' $packagedPythonManifest = Join-Path $runtimeResourceDir 'python\python-manifest.json'
if (-not (Test-Path $installedExe)) { if (-not (Test-Path $installedExe)) {
throw "Installed executable not found at $installedExe" throw "Installed executable not found at $installedExe"
...@@ -225,6 +225,12 @@ if (String(streamSmoke.finalContent || '') !== String(sendResult.lastMessage && ...@@ -225,6 +225,12 @@ if (String(streamSmoke.finalContent || '') !== String(sendResult.lastMessage &&
if (expectBundled === 'true') { if (expectBundled === 'true') {
const runtimeStatus = sendResult.runtimeStatusAfterProbe || {}; const runtimeStatus = sendResult.runtimeStatusAfterProbe || {};
const runtimeHealth = sendResult.runtimeHealthAfterProbe || {}; const runtimeHealth = sendResult.runtimeHealthAfterProbe || {};
if (runtimeStatus.selectedMode !== 'bundled-runtime') {
throw new Error('Installed smoke did not select bundled-runtime mode: ' + runtimeStatus.selectedMode);
}
if (runtimeStatus.payloadState !== 'ready') {
throw new Error('Installed smoke bundled runtime payload is not ready: ' + runtimeStatus.payloadState);
}
if (runtimeStatus.activeMode !== 'bundled-runtime') { if (runtimeStatus.activeMode !== 'bundled-runtime') {
throw new Error('Installed smoke did not switch to bundled-runtime mode: ' + runtimeStatus.activeMode); throw new Error('Installed smoke did not switch to bundled-runtime mode: ' + runtimeStatus.activeMode);
} }
...@@ -262,6 +268,8 @@ const summary = { ...@@ -262,6 +268,8 @@ const summary = {
runtimeActiveMode: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.activeMode || ''), runtimeActiveMode: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.activeMode || ''),
runtimeProcessState: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.processState || ''), runtimeProcessState: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.processState || ''),
runtimeGatewayUrl: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.gatewayUrl || ''), runtimeGatewayUrl: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.gatewayUrl || ''),
runtimePayloadState: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.payloadState || ''),
runtimeLastError: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.lastError || ''),
runtimePythonReady: Boolean(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.pythonReady), runtimePythonReady: Boolean(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.pythonReady),
runtimePythonVersion: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.pythonVersion || ''), runtimePythonVersion: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.pythonVersion || ''),
runtimePythonPackages: sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.installedPythonPackages || [], runtimePythonPackages: sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.installedPythonPackages || [],
...@@ -280,11 +288,21 @@ const summary = { ...@@ -280,11 +288,21 @@ const summary = {
console.log(JSON.stringify(summary, null, 2)); console.log(JSON.stringify(summary, null, 2));
"@ "@
$runtimeModeValue = if ($RuntimeMode) { $RuntimeMode } else { '' } $runtimeModeValue = if ($RuntimeMode) { $RuntimeMode } else { '' }
$summary = & node -e $validator $SmokeOutput $UserDataPath $LogsPath $runtimeModeValue $expectBundledValue $runtimeResourceDir $packagedPythonExe $packagedPythonManifest $SetupExe $InstallDir $installedExe ([string]$appExitCode) $runtimeManagerLog = Join-Path $LogsPath 'runtime-manager.log'
if ($LASTEXITCODE -ne 0) { try {
$summary = & node -e $validator $SmokeOutput $UserDataPath $LogsPath $runtimeModeValue $expectBundledValue $runtimeResourceDir $packagedPythonExe $packagedPythonManifest $SetupExe $InstallDir $installedExe ([string]$appExitCode)
if ($LASTEXITCODE -ne 0) {
throw 'Installed smoke validation failed.' throw 'Installed smoke validation failed.'
} }
Write-Output $summary Write-Output $summary
Stop-SmokeAppProcesses Stop-SmokeAppProcesses
exit 0 exit 0
\ No newline at end of file } catch {
if (Test-Path $runtimeManagerLog) {
Write-Host '==== runtime-manager.log ===='
Get-Content $runtimeManagerLog -Tail 200 | Write-Host
}
Stop-SmokeAppProcesses
throw
}
...@@ -4,11 +4,11 @@ ...@@ -4,11 +4,11 @@
"version": "0.1.0", "version": "0.1.0",
"packageManager": "pnpm@10.0.0", "packageManager": "pnpm@10.0.0",
"scripts": { "scripts": {
"build": "corepack pnpm --filter @qjclaw/shared-types build && corepack pnpm --filter @qjclaw/runtime-manager build && corepack pnpm --filter @qjclaw/gateway-client build && corepack pnpm --filter @qjclaw/ui build && corepack pnpm --filter @qjclaw/desktop build", "build": "corepack pnpm --filter @qjclaw/shared-types build && corepack pnpm --filter @qjclaw/gateway-client build && corepack pnpm --filter @qjclaw/runtime-manager build && corepack pnpm --filter @qjclaw/ui build && corepack pnpm --filter @qjclaw/desktop build",
"clean": "corepack pnpm -r run clean", "clean": "corepack pnpm -r run clean",
"dev": "corepack pnpm --parallel --filter @qjclaw/ui --filter @qjclaw/desktop dev", "dev": "corepack pnpm --parallel --filter @qjclaw/ui --filter @qjclaw/desktop dev",
"lint": "corepack pnpm -r run lint", "lint": "corepack pnpm -r run lint",
"package": "corepack pnpm build && corepack pnpm --filter @qjclaw/desktop exec electron-builder --config electron-builder.yml", "package": "corepack pnpm run materialize:runtime && corepack pnpm build && corepack pnpm --filter @qjclaw/desktop exec electron-builder --config electron-builder.yml",
"typecheck": "corepack pnpm -r run typecheck", "typecheck": "corepack pnpm -r run typecheck",
"smoke:installer": "powershell -ExecutionPolicy Bypass -File build/scripts/installer-smoke.ps1", "smoke:installer": "powershell -ExecutionPolicy Bypass -File build/scripts/installer-smoke.ps1",
"smoke:execution-policy": "powershell -ExecutionPolicy Bypass -File build/scripts/electron-smoke.ps1", "smoke:execution-policy": "powershell -ExecutionPolicy Bypass -File build/scripts/electron-smoke.ps1",
......
...@@ -126,10 +126,44 @@ export interface GatewayPromptStreamHandlers { ...@@ -126,10 +126,44 @@ export interface GatewayPromptStreamHandlers {
onError?: (value: { sessionId: string; runId?: string; error: Error }) => void; onError?: (value: { sessionId: string; runId?: string; error: Error }) => void;
} }
const CLIENT_ID = "gateway-client"; export interface GatewayConnectParamsInput {
const CLIENT_MODE = "backend"; token?: string;
const ROLE = "operator"; deviceToken?: string;
const SCOPES = ["operator.read", "operator.write"]; device?: SignedDeviceProof;
}
export function buildGatewayConnectParams(input: GatewayConnectParamsInput = {}) {
const auth = input.token || input.deviceToken
? {
token: input.token,
deviceToken: input.deviceToken
}
: undefined;
return {
minProtocol: 3,
maxProtocol: 3,
client: {
id: GATEWAY_CLIENT_ID,
version: "qianjiangclaw-desktop",
platform: process.platform,
mode: GATEWAY_CLIENT_MODE
},
role: GATEWAY_CLIENT_ROLE,
scopes: [...GATEWAY_CLIENT_SCOPES],
caps: [...GATEWAY_CLIENT_CAPS],
auth,
device: input.device,
locale: "zh-CN",
userAgent: "qianjiangclaw-desktop"
};
}
export const GATEWAY_CLIENT_ID = "gateway-client";
export const GATEWAY_CLIENT_MODE = "backend";
export const GATEWAY_CLIENT_ROLE = "operator";
export const GATEWAY_CLIENT_SCOPES = ["operator.read", "operator.write"] as const;
export const GATEWAY_CLIENT_CAPS = ["tool-events"] as const;
const ANSI_ESCAPE_PATTERN = /\u001B\[[0-9;?]*[ -/]*[@-~]/g; const ANSI_ESCAPE_PATTERN = /\u001B\[[0-9;?]*[ -/]*[@-~]/g;
const CONTROL_CHAR_PATTERN = /[\u0000-\u0008\u000B-\u001A\u001C-\u001F\u007F]/g; const CONTROL_CHAR_PATTERN = /[\u0000-\u0008\u000B-\u001A\u001C-\u001F\u007F]/g;
const LOG_PREFIX_PATTERN = /^[^A-Za-z0-9\[{(]+(?=[A-Za-z])/; const LOG_PREFIX_PATTERN = /^[^A-Za-z0-9\[{(]+(?=[A-Za-z])/;
...@@ -428,43 +462,24 @@ export class GatewayClient { ...@@ -428,43 +462,24 @@ export class GatewayClient {
const device = this.deviceIdentity const device = this.deviceIdentity
? await this.deviceIdentity.signConnectChallenge({ ? await this.deviceIdentity.signConnectChallenge({
clientId: CLIENT_ID, clientId: GATEWAY_CLIENT_ID,
clientMode: CLIENT_MODE, clientMode: GATEWAY_CLIENT_MODE,
role: ROLE, role: GATEWAY_CLIENT_ROLE,
scopes: SCOPES, scopes: [...GATEWAY_CLIENT_SCOPES],
token: this.token, token: this.token,
nonce nonce
}) })
: undefined; : undefined;
const auth = this.token || this.deviceToken
? {
token: this.token,
deviceToken: this.deviceToken
}
: undefined;
this.sendFrame({ this.sendFrame({
type: "req", type: "req",
id: "1", id: "1",
method: "connect", method: "connect",
params: { params: buildGatewayConnectParams({
minProtocol: 3, token: this.token,
maxProtocol: 3, deviceToken: this.deviceToken,
client: { device
id: CLIENT_ID, })
version: "qianjiangclaw-desktop",
platform: process.platform,
mode: CLIENT_MODE
},
role: ROLE,
scopes: SCOPES,
caps: ["tool-events"],
auth,
device,
locale: "zh-CN",
userAgent: "qianjiangclaw-desktop"
}
}); });
this.appendLog("info", "Sent Gateway connect handshake."); this.appendLog("info", "Sent Gateway connect handshake.");
return; return;
...@@ -978,7 +993,7 @@ export class GatewayClient { ...@@ -978,7 +993,7 @@ export class GatewayClient {
private stripStructuredLogPrefix(message: string): string { private stripStructuredLogPrefix(message: string): string {
return message return message
.replace(LOG_PREFIX_PATTERN, "") .replace(LOG_PREFIX_PATTERN, "")
.replace(/�\?/g, "ok ") .replace(/闁跨喓绁?/g, "ok ")
.replace(/[?]{2,}/g, "") .replace(/[?]{2,}/g, "")
.replace(/\s+/g, " ") .replace(/\s+/g, " ")
.trim(); .trim();
......
...@@ -18,10 +18,13 @@ ...@@ -18,10 +18,13 @@
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@qjclaw/shared-types": "workspace:*" "@qjclaw/gateway-client": "workspace:*",
"@qjclaw/shared-types": "workspace:*",
"ws": "^8.18.3"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.10.2", "@types/node": "^22.10.2",
"@types/ws": "^8.18.1",
"rimraf": "^6.0.1", "rimraf": "^6.0.1",
"tsup": "^8.3.5", "tsup": "^8.3.5",
"typescript": "^5.7.3" "typescript": "^5.7.3"
......
This diff is collapsed.
...@@ -10,6 +10,10 @@ importers: ...@@ -10,6 +10,10 @@ importers:
apps/desktop: apps/desktop:
dependencies: dependencies:
keytar:
specifier: ^7.9.0
version: 7.9.0
devDependencies:
'@qjclaw/gateway-client': '@qjclaw/gateway-client':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/gateway-client version: link:../../packages/gateway-client
...@@ -19,10 +23,6 @@ importers: ...@@ -19,10 +23,6 @@ importers:
'@qjclaw/shared-types': '@qjclaw/shared-types':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/shared-types version: link:../../packages/shared-types
keytar:
specifier: ^7.9.0
version: 7.9.0
devDependencies:
'@types/node': '@types/node':
specifier: ^22.10.2 specifier: ^22.10.2
version: 22.19.15 version: 22.19.15
...@@ -112,10 +112,16 @@ importers: ...@@ -112,10 +112,16 @@ importers:
'@qjclaw/shared-types': '@qjclaw/shared-types':
specifier: workspace:* specifier: workspace:*
version: link:../shared-types version: link:../shared-types
ws:
specifier: ^8.18.3
version: 8.19.0
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: ^22.10.2 specifier: ^22.10.2
version: 22.19.15 version: 22.19.15
'@types/ws':
specifier: ^8.18.1
version: 8.18.1
rimraf: rimraf:
specifier: ^6.0.1 specifier: ^6.0.1
version: 6.1.3 version: 6.1.3
......
# Bundled Runtime Payload # Bundled Runtime Payload
Immutable packaged payload under `vendor/openclaw-runtime/` now includes: Immutable packaged payload under endor/openclaw-runtime/ includes:
- `node/node.exe` -
- `openclaw/index.js` ode/node.exe
- `config/openclaw.json` - openclaw/index.js
- `python/Scripts/python.exe` - openclaw/package/openclaw.mjs
- `python/python-manifest.json` - config/openclaw.json
- `python/runtime-requirements.lock.txt` - python/python.exe
- python/python-manifest.json
- python/runtime-requirements.lock.txt
Mutable runtime data lives outside the installer payload and should be created under Electron `userData/runtime/`: Mutable runtime data lives outside the installer payload and should be created under Electron userData/runtime/.
- `runtime/logs/` The payload is considered ready only when the Node entry, OpenClaw package, Python executable, Python manifest, and locked Python imports all validate successfully on the target machine.
- `runtime/state/` \ No newline at end of file
- `runtime/workspace/`
- future writable caches and lock files
Current payload is generated locally for packaging and smoke validation by `build/scripts/materialize-runtime-payload.ps1`.
The Python layer is part of the bundled-runtime contract. A payload is not considered ready unless:
- the Node/OpenClaw files exist
- the Python executable and manifest exist
- the locked Python dependencies can all be imported successfully
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment