fix(cursor-local): resolve sandbox agent installs from cursor bin (#5686)
> _Stacked on top of #5685 (Harden remote sandbox runtime). Diff against master includes commits from earlier PRs in the stack — review focuses on the new commit only._ ## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - The cursor-local adapter wraps the Cursor Agent CLI so a Paperclip workflow can drive it inside a sandbox > - When the adapter runs in a remote sandbox, the Cursor Agent CLI installs under `$HOME/.local/bin/cursor-agent` (or wherever `$XDG_BIN_HOME` points), not on the global PATH > - The existing post-install resolution assumed `cursor-agent` would resolve via the sandbox's login shell PATH after `npm install -g`, which fails on sandboxes where the install lands in a user-prefixed directory that isn't on PATH at probe time > - This pull request resolves the agent CLI from the cursor binary's own directory (`dirname "$(command -v cursor)"`) so the install probe and execute path agree on a real binary location > - The benefit is that cursor-local works correctly on any sandbox provider where `npm install` lands in a user-prefixed directory ## What Changed - `packages/adapters/cursor-local/src/server/remote-command.ts`: resolve the cursor-agent binary from the cursor bin directory after install, instead of relying on PATH. - `packages/adapters/cursor-local/src/server/test.ts`: corresponding probe tweak. - `packages/adapters/cursor-local/src/server/test.test.ts` (new) + `remote-command.test.ts`: focused coverage that exercises the install + resolve path against a sandbox runner that places the binary in a user-prefixed directory. ## Verification - `pnpm exec vitest run --no-coverage packages/adapters/cursor-local/src/server/test.test.ts packages/adapters/cursor-local/src/server/remote-command.test.ts packages/adapters/cursor-local/src/server/execute.test.ts` All passing locally. ## Risks - Local cursor-local runs are unaffected — the resolution change only kicks in for the sandbox install path. - Low risk; isolated to one adapter. ## Model Used - Provider: Anthropic - Model: Claude Opus 4.7 (1M context) - Capabilities used: tool use (Read/Edit/Bash), no code execution beyond local repo commands ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots — N/A, no UI change - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -148,7 +148,6 @@ export async function testEnvironment(
|
||||
});
|
||||
command = sandboxCommand.command;
|
||||
env = sandboxCommand.env;
|
||||
const runtimeEnv = ensurePathInEnv({ ...process.env, ...env });
|
||||
const installCheck = await maybeRunSandboxInstallCommand({
|
||||
runId,
|
||||
target,
|
||||
@@ -158,6 +157,19 @@ export async function testEnvironment(
|
||||
env,
|
||||
});
|
||||
if (installCheck) checks.push(installCheck);
|
||||
const finalSandboxCommand = await prepareCursorSandboxCommand({
|
||||
runId,
|
||||
target,
|
||||
command,
|
||||
cwd,
|
||||
env,
|
||||
remoteSystemHomeDirHint: sandboxCommand.remoteSystemHomeDir,
|
||||
timeoutSec: 45,
|
||||
graceSec: 5,
|
||||
});
|
||||
command = finalSandboxCommand.command;
|
||||
env = finalSandboxCommand.env;
|
||||
const runtimeEnv = ensurePathInEnv({ ...process.env, ...env });
|
||||
try {
|
||||
await ensureAdapterExecutionTargetCommandResolvable(command, target, cwd, runtimeEnv);
|
||||
checks.push({
|
||||
@@ -218,6 +230,58 @@ export async function testEnvironment(
|
||||
hint: "Use `agent` or `cursor-agent` to run the automatic installation and auth probe.",
|
||||
});
|
||||
} else {
|
||||
const versionProbe = await runAdapterExecutionTargetProcess(
|
||||
runId,
|
||||
target,
|
||||
command,
|
||||
["--version"],
|
||||
{
|
||||
cwd,
|
||||
env,
|
||||
timeoutSec: 45,
|
||||
graceSec: 5,
|
||||
onLog: async () => {},
|
||||
},
|
||||
);
|
||||
const versionDetail = summarizeProbeDetail(versionProbe.stdout, versionProbe.stderr, null);
|
||||
if (versionProbe.timedOut) {
|
||||
checks.push({
|
||||
code: "cursor_version_probe_timed_out",
|
||||
level: "error",
|
||||
message: "Cursor version probe timed out.",
|
||||
hint: "Run `agent --version` manually in this working directory to confirm the installed CLI is reachable non-interactively.",
|
||||
});
|
||||
} else if ((versionProbe.exitCode ?? 1) === 0) {
|
||||
checks.push({
|
||||
code: "cursor_version_probe_passed",
|
||||
level: "info",
|
||||
message: "Cursor version probe succeeded.",
|
||||
...(versionDetail ? { detail: versionDetail } : {}),
|
||||
});
|
||||
} else {
|
||||
checks.push({
|
||||
code: "cursor_version_probe_failed",
|
||||
level: "error",
|
||||
message: "Cursor version probe failed.",
|
||||
...(versionDetail ? { detail: versionDetail } : {}),
|
||||
hint: "Run `agent --version` manually in this working directory to confirm the installed CLI is reachable non-interactively.",
|
||||
});
|
||||
}
|
||||
|
||||
const canRunHelloProbe = checks.every(
|
||||
(check) =>
|
||||
check.code !== "cursor_version_probe_failed" &&
|
||||
check.code !== "cursor_version_probe_timed_out",
|
||||
);
|
||||
if (!canRunHelloProbe) {
|
||||
return {
|
||||
adapterType: ctx.adapterType,
|
||||
status: summarizeStatus(checks),
|
||||
checks,
|
||||
testedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
const model = asString(config.model, DEFAULT_CURSOR_LOCAL_MODEL).trim();
|
||||
const extraArgs = (() => {
|
||||
const fromExtraArgs = asStringArray(config.extraArgs);
|
||||
|
||||
Reference in New Issue
Block a user