Files
cobble/src/harness/tools/computer/with-computer-env.ts
T
tbenandClaude Opus 5.5 6a2665dae9 Add the harness, envs, per-bot models and chat attachments
- Harness: providers, model catalog synced hourly, automatic free picks,
  cooldowns, tool loop with context trimming, MCP computer tools
- Bots queue runs replies and tasks through the harness; jobs wait
  (attempt refunded) while every model is resting
- Envs: Settings > Environment, scoped general/workspace/computer,
  written to computers and redacted from tool output
- Models: Settings > Models, per-harness provider select defaulting to
  OpenRouter, and a models modal on each bot's chat (the coordinator's
  edits the defaults)
- Chat: thinking row while a reply runs, file attachments (upload,
  paste, drop) that replies read as images, audio and document text
- Fix media uploads failing after a user's first file, and private
  files being served from the public media path

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 03:38:38 +01:00

67 lines
2.1 KiB
TypeScript

import type { HarnessTool } from "../../types";
type Params = {
tool: HarnessTool;
/**
* Absolute paths of the env files on the computer.
*/
shell_path: string;
json_path: string;
};
/**
* Makes a computer tool run with the computer's envs. The sandbox's shells
* don't read a profile, so bash commands source the env file first and
* Python or JavaScript code loads the JSON one. The model never sees the
* addition; other tools pass through.
*/
export default function withComputerEnv({
tool,
shell_path,
json_path,
}: Params): HarnessTool {
const shell_prefix = `[ -f ${shell_path} ] && . ${shell_path}; `;
if (tool.name == "sandbox_execute_bash") {
return {
...tool,
run: ({ args, signal }) =>
tool.run({
args: typeof args?.cmd == "string" ? { ...args, cmd: `${shell_prefix}${args.cmd}` } : args,
signal,
}),
};
}
if (tool.name == "sandbox_execute_code") {
return {
...tool,
run: ({ args, signal }) =>
tool.run({
args:
typeof args?.code == "string"
? { ...args, code: `${codePrefix({ language: args.language, json_path })}${args.code}` }
: args,
signal,
}),
};
}
return tool;
}
function codePrefix({ language, json_path }: { language?: unknown; json_path: string }) {
const path = JSON.stringify(json_path);
const lang = typeof language == "string" ? language.toLowerCase() : "python";
if (lang.startsWith("py")) {
return `import json as _cobble_json, os as _cobble_os; _cobble_os.environ.update(_cobble_json.load(open(${path})) if _cobble_os.path.exists(${path}) else {})\n`;
}
if (lang.startsWith("js") || lang.startsWith("javascript") || lang.startsWith("node")) {
return `try { Object.assign(process.env, JSON.parse(require("fs").readFileSync(${path}, "utf8"))); } catch {}\n`;
}
return "";
}