Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qjclaw-dmg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qjclaw-dmg
Commits
945b8992
Commit
945b8992
authored
Apr 30, 2026
by
AI-甘富林
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(desktop): resolve workspace runner deps from app data
parent
fc12bfc3
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
100 additions
and
11 deletions
+100
-11
project-workspace-agent-runner.ts
apps/desktop/src/main/project-workspace-agent-runner.ts
+98
-11
project-workspace-executor.ts
apps/desktop/src/main/services/project-workspace-executor.ts
+2
-0
No files found.
apps/desktop/src/main/project-workspace-agent-runner.ts
View file @
945b8992
import
{
createHash
,
randomUUID
}
from
"node:crypto"
;
import
{
createHash
,
randomUUID
}
from
"node:crypto"
;
import
{
mkdir
,
readFile
,
readdir
,
writeFile
}
from
"node:fs/promises"
;
import
{
lstat
,
mkdir
,
readFile
,
readdir
,
readlink
,
rm
,
symlink
,
writeFile
}
from
"node:fs/promises"
;
import
path
from
"node:path"
;
import
path
from
"node:path"
;
import
process
from
"node:process"
;
import
process
from
"node:process"
;
import
{
pathToFileURL
}
from
"node:url"
;
import
{
pathToFileURL
}
from
"node:url"
;
...
@@ -7,6 +7,7 @@ import type { ProjectResolvedAttachment } from "@qjclaw/shared-types";
...
@@ -7,6 +7,7 @@ import type { ProjectResolvedAttachment } from "@qjclaw/shared-types";
interface
RunnerInput
{
interface
RunnerInput
{
vendorPackageDir
:
string
;
vendorPackageDir
:
string
;
instrumentationDir
?:
string
;
projectRoot
:
string
;
projectRoot
:
string
;
sessionId
:
string
;
sessionId
:
string
;
prompt
:
string
;
prompt
:
string
;
...
@@ -74,7 +75,91 @@ function buildInstrumentationKey(agentModulePath: string, modelSelectionSpecifie
...
@@ -74,7 +75,91 @@ function buildInstrumentationKey(agentModulePath: string, modelSelectionSpecifie
.
slice
(
0
,
12
);
.
slice
(
0
,
12
);
}
}
async
function
ensureInstrumentedWorkspaceModules
(
agentModulePath
:
string
):
Promise
<
{
function
resolveInstrumentationDir
(
input
:
RunnerInput
):
string
{
const
explicitDir
=
input
.
instrumentationDir
?.
trim
();
if
(
explicitDir
)
{
return
explicitDir
;
}
const
stateDir
=
process
.
env
.
OPENCLAW_STATE_DIR
?.
trim
();
if
(
stateDir
)
{
return
path
.
join
(
stateDir
,
"workspace-runner"
,
"instrumented-modules"
);
}
return
path
.
join
(
input
.
projectRoot
,
"memory"
,
"workspace-runner"
,
"instrumented-modules"
);
}
function
rewriteModuleSpecifiers
(
source
:
string
,
baseDir
:
string
,
overrides
:
Record
<
string
,
string
>
=
{}
):
string
{
const
toModuleUrl
=
(
specifier
:
string
):
string
=>
{
const
override
=
overrides
[
specifier
];
if
(
override
)
{
return
override
;
}
if
(
specifier
.
startsWith
(
"."
))
{
return
pathToFileURL
(
path
.
resolve
(
baseDir
,
specifier
)).
href
;
}
return
specifier
;
};
return
source
.
replace
(
/
(
^|
[\r\n])(\s
*
(?:
import|export
)\s
+
[^
"'
\r\n
;
]
+
?\s
+from
\s
*
)([
"'
])([^
"'
\r\n]
+
)\3
/g
,
(
_match
,
linePrefix
:
string
,
prefix
:
string
,
quote
:
string
,
specifier
:
string
)
=>
{
return
`
${
linePrefix
}${
prefix
}${
quote
}${
toModuleUrl
(
specifier
)}${
quote
}
`
;
})
.
replace
(
/
(
^|
[\r\n])(\s
*import
\s
*
)([
"'
])([^
"'
\r\n]
+
)\3
/g
,
(
_match
,
linePrefix
:
string
,
prefix
:
string
,
quote
:
string
,
specifier
:
string
)
=>
{
return
`
${
linePrefix
}${
prefix
}${
quote
}${
toModuleUrl
(
specifier
)}${
quote
}
`
;
})
.
replace
(
/
\b(
import
\s
*
\(\s
*
)([
"'
])([^
"'
\r\n]
+
)\2
/g
,
(
_match
,
prefix
:
string
,
quote
:
string
,
specifier
:
string
)
=>
{
return
`
${
prefix
}${
quote
}${
toModuleUrl
(
specifier
)}${
quote
}
`
;
});
}
async
function
ensureInstrumentationNodeModulesLink
(
instrumentationDir
:
string
,
vendorPackageDir
:
string
):
Promise
<
void
>
{
const
vendorNodeModulesPath
=
path
.
join
(
vendorPackageDir
,
"node_modules"
);
try
{
const
vendorNodeModules
=
await
lstat
(
vendorNodeModulesPath
);
if
(
!
vendorNodeModules
.
isDirectory
())
{
throw
new
Error
(
`
${
vendorNodeModulesPath
}
is not a directory.`
);
}
}
catch
(
error
)
{
throw
new
Error
(
`Unable to locate OpenClaw node_modules at
${
vendorNodeModulesPath
}
:
${
toErrorMessage
(
error
)}
`
);
}
const
linkPath
=
path
.
join
(
instrumentationDir
,
"node_modules"
);
const
normalizeForCompare
=
(
value
:
string
):
string
=>
path
.
resolve
(
value
).
toLowerCase
();
const
expectedTarget
=
normalizeForCompare
(
vendorNodeModulesPath
);
const
existing
=
await
lstat
(
linkPath
).
catch
(()
=>
undefined
);
if
(
existing
)
{
if
(
existing
.
isSymbolicLink
())
{
const
currentTarget
=
await
readlink
(
linkPath
);
const
resolvedTarget
=
normalizeForCompare
(
path
.
resolve
(
path
.
dirname
(
linkPath
),
currentTarget
));
if
(
resolvedTarget
===
expectedTarget
)
{
return
;
}
}
await
rm
(
linkPath
,
{
recursive
:
true
,
force
:
true
});
}
await
symlink
(
vendorNodeModulesPath
,
linkPath
,
"junction"
);
}
async
function
ensureInstrumentedWorkspaceModules
(
agentModulePath
:
string
,
instrumentationDir
:
string
):
Promise
<
{
agentModuleUrl
:
string
;
agentModuleUrl
:
string
;
modelSelectionModuleUrl
:
string
;
modelSelectionModuleUrl
:
string
;
}
>
{
}
>
{
...
@@ -86,23 +171,25 @@ async function ensureInstrumentedWorkspaceModules(agentModulePath: string): Prom
...
@@ -86,23 +171,25 @@ async function ensureInstrumentedWorkspaceModules(agentModulePath: string): Prom
const
modelSelectionSpecifier
=
modelSelectionImportMatch
[
1
];
const
modelSelectionSpecifier
=
modelSelectionImportMatch
[
1
];
const
distDir
=
path
.
dirname
(
agentModulePath
);
const
distDir
=
path
.
dirname
(
agentModulePath
);
const
vendorPackageDir
=
path
.
dirname
(
distDir
);
const
modelSelectionPath
=
path
.
resolve
(
distDir
,
modelSelectionSpecifier
);
const
modelSelectionPath
=
path
.
resolve
(
distDir
,
modelSelectionSpecifier
);
const
instrumentationKey
=
buildInstrumentationKey
(
agentModulePath
,
modelSelectionSpecifier
);
const
instrumentationKey
=
buildInstrumentationKey
(
agentModulePath
,
modelSelectionSpecifier
);
const
instrumentedAgentFileName
=
`.qjc-agent-
${
instrumentationKey
}
.js`
;
const
instrumentedAgentFileName
=
`.qjc-agent-
${
instrumentationKey
}
.js`
;
const
instrumentedModelSelectionFileName
=
`.qjc-model-selection-
${
instrumentationKey
}
.js`
;
const
instrumentedModelSelectionFileName
=
`.qjc-model-selection-
${
instrumentationKey
}
.js`
;
const
instrumentedAgentPath
=
path
.
join
(
distDir
,
instrumentedAgentFileName
);
const
instrumentedAgentPath
=
path
.
join
(
instrumentationDir
,
instrumentedAgentFileName
);
const
instrumentedModelSelectionPath
=
path
.
join
(
distDir
,
instrumentedModelSelectionFileName
);
const
instrumentedModelSelectionPath
=
path
.
join
(
instrumentationDir
,
instrumentedModelSelectionFileName
);
const
instrumentedModelSelectionUrl
=
pathToFileURL
(
instrumentedModelSelectionPath
).
href
;
await
mkdir
(
distDir
,
{
recursive
:
true
});
await
mkdir
(
instrumentationDir
,
{
recursive
:
true
});
await
ensureInstrumentationNodeModulesLink
(
instrumentationDir
,
vendorPackageDir
);
const
modelSelectionSource
=
await
readFile
(
modelSelectionPath
,
"utf8"
);
const
modelSelectionSource
=
await
readFile
(
modelSelectionPath
,
"utf8"
);
const
instrumentedModelSelectionSource
=
`
${
modelSelectionSource
}
\nexport { onAgentEvent as __qjcOnAgentEvent };\n`
;
const
instrumentedModelSelectionSource
=
`
${
rewriteModuleSpecifiers
(
modelSelectionSource
,
distDir
)
}
\nexport { onAgentEvent as __qjcOnAgentEvent };\n`
;
await
writeFile
(
instrumentedModelSelectionPath
,
instrumentedModelSelectionSource
,
"utf8"
);
await
writeFile
(
instrumentedModelSelectionPath
,
instrumentedModelSelectionSource
,
"utf8"
);
const
instrumentedAgentSource
=
agentSource
.
replace
(
const
instrumentedAgentSource
=
rewriteModuleSpecifiers
(
agentSource
,
distDir
,
{
modelSelectionSpecifier
,
[
modelSelectionSpecifier
]:
instrumentedModelSelectionUrl
`./
${
instrumentedModelSelectionFileName
}
`
});
);
await
writeFile
(
instrumentedAgentPath
,
instrumentedAgentSource
,
"utf8"
);
await
writeFile
(
instrumentedAgentPath
,
instrumentedAgentSource
,
"utf8"
);
return
{
return
{
...
@@ -191,7 +278,7 @@ async function main(): Promise<void> {
...
@@ -191,7 +278,7 @@ async function main(): Promise<void> {
});
});
const
agentModulePath
=
await
resolveAgentModulePath
(
input
.
vendorPackageDir
);
const
agentModulePath
=
await
resolveAgentModulePath
(
input
.
vendorPackageDir
);
const
instrumentedModules
=
await
ensureInstrumentedWorkspaceModules
(
agentModulePath
);
const
instrumentedModules
=
await
ensureInstrumentedWorkspaceModules
(
agentModulePath
,
resolveInstrumentationDir
(
input
)
);
const
agentModule
=
await
import
(
instrumentedModules
.
agentModuleUrl
)
as
AgentCommandModule
;
const
agentModule
=
await
import
(
instrumentedModules
.
agentModuleUrl
)
as
AgentCommandModule
;
const
modelSelectionModule
=
await
import
(
instrumentedModules
.
modelSelectionModuleUrl
)
as
InstrumentedModelSelectionModule
;
const
modelSelectionModule
=
await
import
(
instrumentedModules
.
modelSelectionModuleUrl
)
as
InstrumentedModelSelectionModule
;
if
(
typeof
agentModule
.
t
!==
"function"
)
{
if
(
typeof
agentModule
.
t
!==
"function"
)
{
...
...
apps/desktop/src/main/services/project-workspace-executor.ts
View file @
945b8992
...
@@ -326,6 +326,7 @@ export class ProjectWorkspaceExecutorService {
...
@@ -326,6 +326,7 @@ export class ProjectWorkspaceExecutorService {
const
runnerScriptPath
=
automationCommand
?
null
:
await
resolveRunnerScriptPath
();
const
runnerScriptPath
=
automationCommand
?
null
:
await
resolveRunnerScriptPath
();
const
vendorPackageDir
=
path
.
join
(
paths
.
runtimeDir
,
"openclaw"
,
"package"
);
const
vendorPackageDir
=
path
.
join
(
paths
.
runtimeDir
,
"openclaw"
,
"package"
);
const
instrumentationDir
=
path
.
join
(
paths
.
runtimeDataDir
,
"workspace-runner"
,
"instrumented-modules"
);
const
runId
=
input
.
runId
?.
trim
()
||
randomUUID
();
const
runId
=
input
.
runId
?.
trim
()
||
randomUUID
();
callbacks
.
onStatus
?.(
callbacks
.
onStatus
?.(
...
@@ -516,6 +517,7 @@ export class ProjectWorkspaceExecutorService {
...
@@ -516,6 +517,7 @@ export class ProjectWorkspaceExecutorService {
const payload = JSON.stringify({
const payload = JSON.stringify({
vendorPackageDir,
vendorPackageDir,
instrumentationDir,
projectRoot: input.projectRoot,
projectRoot: input.projectRoot,
sessionId: input.sessionId,
sessionId: input.sessionId,
prompt: input.prompt,
prompt: input.prompt,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment