Add local Codex skill directory support
This commit is contained in:
parent
b4d869bd0d
commit
054e77a370
4 changed files with 33 additions and 3 deletions
|
|
@ -3,6 +3,7 @@ BOT_API_ROOT=
|
|||
BOT_CONNECTOR=local-codex
|
||||
LOCAL_CODEX_COMMAND=codex
|
||||
LOCAL_CODEX_WORKDIR=
|
||||
LOCAL_CODEX_SKILL_DIR=
|
||||
LOCAL_CODEX_SANDBOX=workspace-write
|
||||
LOCAL_CODEX_MODEL=
|
||||
LOCAL_CODEX_PROFILE=
|
||||
|
|
|
|||
4
setup.md
4
setup.md
|
|
@ -22,6 +22,7 @@ BOT_API_ROOT=
|
|||
BOT_CONNECTOR=local-codex
|
||||
LOCAL_CODEX_COMMAND=codex
|
||||
LOCAL_CODEX_WORKDIR=
|
||||
LOCAL_CODEX_SKILL_DIR=
|
||||
LOCAL_CODEX_SANDBOX=workspace-write
|
||||
LOCAL_CODEX_MODEL=
|
||||
LOCAL_CODEX_PROFILE=
|
||||
|
|
@ -70,12 +71,15 @@ Relevant variables:
|
|||
- `BOT_CONNECTOR=local-codex`
|
||||
- `LOCAL_CODEX_COMMAND=codex`
|
||||
- `LOCAL_CODEX_WORKDIR=`: working directory for Codex. Empty means this project root.
|
||||
- `LOCAL_CODEX_SKILL_DIR=`: optional path to a local skill directory. `~` is supported.
|
||||
- `LOCAL_CODEX_SANDBOX=workspace-write`: sandbox mode passed to `codex exec`
|
||||
- `LOCAL_CODEX_MODEL=`: optional model override
|
||||
- `LOCAL_CODEX_PROFILE=`: optional Codex profile
|
||||
- `LOCAL_CODEX_SKIP_GIT_REPO_CHECK=`: set to `true` if the Codex workdir is not a git repo
|
||||
- `LOCAL_CODEX_ADD_DIRS=`: comma-separated extra directories for Codex access
|
||||
|
||||
If `LOCAL_CODEX_SKILL_DIR` is set, the directory is passed to Codex as an accessible path and the agent is explicitly instructed to inspect and use that skill for media-generation tasks.
|
||||
|
||||
The connector queue is sequential, so messages are processed one by one in arrival order.
|
||||
|
||||
## Optional: Local Bot API Server For Files Larger Than 20 MB
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
const os = require("node:os");
|
||||
const path = require("node:path");
|
||||
const dotenv = require("dotenv");
|
||||
|
||||
|
|
@ -36,8 +37,20 @@ function readList(value) {
|
|||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function expandHomePath(value) {
|
||||
if (value === "~") {
|
||||
return os.homedir();
|
||||
}
|
||||
|
||||
if (typeof value === "string" && value.startsWith("~/")) {
|
||||
return path.join(os.homedir(), value.slice(2));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function resolveFromRoot(rootDir, value, fallback) {
|
||||
const resolvedValue = readString(value, fallback);
|
||||
const resolvedValue = expandHomePath(readString(value, fallback));
|
||||
|
||||
if (!resolvedValue) {
|
||||
return null;
|
||||
|
|
@ -62,6 +75,7 @@ const connector = {
|
|||
localCodex: {
|
||||
command: readString(process.env.LOCAL_CODEX_COMMAND, "codex"),
|
||||
workdir: resolveFromRoot(rootDir, process.env.LOCAL_CODEX_WORKDIR, rootDir),
|
||||
skillDir: resolveFromRoot(rootDir, process.env.LOCAL_CODEX_SKILL_DIR),
|
||||
sandbox: readString(process.env.LOCAL_CODEX_SANDBOX, "workspace-write"),
|
||||
model: readString(process.env.LOCAL_CODEX_MODEL),
|
||||
profile: readString(process.env.LOCAL_CODEX_PROFILE),
|
||||
|
|
|
|||
|
|
@ -62,10 +62,18 @@ function formatAttachmentBlock(attachments) {
|
|||
.join("\n");
|
||||
}
|
||||
|
||||
function buildPrompt(request) {
|
||||
function buildPrompt(request, config) {
|
||||
const userText = request.text?.trim() || "(empty)";
|
||||
const username = request.source.username || "(unknown)";
|
||||
const displayName = request.source.displayName || "(unknown)";
|
||||
const skillDirBlock = config.skillDir
|
||||
? [
|
||||
"",
|
||||
"Local media skill:",
|
||||
`- path: ${config.skillDir}`,
|
||||
"- If the task requires generating or editing images, video, or other media, inspect this skill and use it.",
|
||||
]
|
||||
: [];
|
||||
|
||||
return [
|
||||
"You are answering a Telegram user through a bot connector.",
|
||||
|
|
@ -92,6 +100,7 @@ function buildPrompt(request) {
|
|||
"",
|
||||
"Attachments:",
|
||||
formatAttachmentBlock(request.attachments),
|
||||
...skillDirBlock,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
|
|
@ -127,6 +136,7 @@ function buildCodexArgs(config, request, outputPath) {
|
|||
.map((attachment) => path.dirname(attachment.path))
|
||||
.filter((attachmentDir) => !isInsideDirectory(config.workdir, attachmentDir));
|
||||
const addDirs = dedupePaths([
|
||||
config.skillDir,
|
||||
...config.addDirs,
|
||||
...attachmentDirs,
|
||||
]);
|
||||
|
|
@ -181,6 +191,7 @@ function inferAttachmentKind(filePath, rawKind) {
|
|||
function getAllowedFileRoots(config, request) {
|
||||
return dedupePaths([
|
||||
config.workdir,
|
||||
config.skillDir,
|
||||
...config.addDirs,
|
||||
...request.attachments.map((attachment) => path.dirname(attachment.path)),
|
||||
]);
|
||||
|
|
@ -295,7 +306,7 @@ class LocalCodexConnectorStrategy extends ConnectorStrategy {
|
|||
"last-message.txt",
|
||||
);
|
||||
const args = buildCodexArgs(this.config, request, outputPath);
|
||||
const prompt = buildPrompt(request);
|
||||
const prompt = buildPrompt(request, this.config);
|
||||
|
||||
try {
|
||||
await runCodex(this.config.command, args, this.config.workdir, prompt);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue