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
|
BOT_CONNECTOR=local-codex
|
||||||
LOCAL_CODEX_COMMAND=codex
|
LOCAL_CODEX_COMMAND=codex
|
||||||
LOCAL_CODEX_WORKDIR=
|
LOCAL_CODEX_WORKDIR=
|
||||||
|
LOCAL_CODEX_SKILL_DIR=
|
||||||
LOCAL_CODEX_SANDBOX=workspace-write
|
LOCAL_CODEX_SANDBOX=workspace-write
|
||||||
LOCAL_CODEX_MODEL=
|
LOCAL_CODEX_MODEL=
|
||||||
LOCAL_CODEX_PROFILE=
|
LOCAL_CODEX_PROFILE=
|
||||||
|
|
|
||||||
4
setup.md
4
setup.md
|
|
@ -22,6 +22,7 @@ BOT_API_ROOT=
|
||||||
BOT_CONNECTOR=local-codex
|
BOT_CONNECTOR=local-codex
|
||||||
LOCAL_CODEX_COMMAND=codex
|
LOCAL_CODEX_COMMAND=codex
|
||||||
LOCAL_CODEX_WORKDIR=
|
LOCAL_CODEX_WORKDIR=
|
||||||
|
LOCAL_CODEX_SKILL_DIR=
|
||||||
LOCAL_CODEX_SANDBOX=workspace-write
|
LOCAL_CODEX_SANDBOX=workspace-write
|
||||||
LOCAL_CODEX_MODEL=
|
LOCAL_CODEX_MODEL=
|
||||||
LOCAL_CODEX_PROFILE=
|
LOCAL_CODEX_PROFILE=
|
||||||
|
|
@ -70,12 +71,15 @@ Relevant variables:
|
||||||
- `BOT_CONNECTOR=local-codex`
|
- `BOT_CONNECTOR=local-codex`
|
||||||
- `LOCAL_CODEX_COMMAND=codex`
|
- `LOCAL_CODEX_COMMAND=codex`
|
||||||
- `LOCAL_CODEX_WORKDIR=`: working directory for Codex. Empty means this project root.
|
- `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_SANDBOX=workspace-write`: sandbox mode passed to `codex exec`
|
||||||
- `LOCAL_CODEX_MODEL=`: optional model override
|
- `LOCAL_CODEX_MODEL=`: optional model override
|
||||||
- `LOCAL_CODEX_PROFILE=`: optional Codex profile
|
- `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_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
|
- `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.
|
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
|
## 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 path = require("node:path");
|
||||||
const dotenv = require("dotenv");
|
const dotenv = require("dotenv");
|
||||||
|
|
||||||
|
|
@ -36,8 +37,20 @@ function readList(value) {
|
||||||
.filter(Boolean);
|
.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) {
|
function resolveFromRoot(rootDir, value, fallback) {
|
||||||
const resolvedValue = readString(value, fallback);
|
const resolvedValue = expandHomePath(readString(value, fallback));
|
||||||
|
|
||||||
if (!resolvedValue) {
|
if (!resolvedValue) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -62,6 +75,7 @@ const connector = {
|
||||||
localCodex: {
|
localCodex: {
|
||||||
command: readString(process.env.LOCAL_CODEX_COMMAND, "codex"),
|
command: readString(process.env.LOCAL_CODEX_COMMAND, "codex"),
|
||||||
workdir: resolveFromRoot(rootDir, process.env.LOCAL_CODEX_WORKDIR, rootDir),
|
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"),
|
sandbox: readString(process.env.LOCAL_CODEX_SANDBOX, "workspace-write"),
|
||||||
model: readString(process.env.LOCAL_CODEX_MODEL),
|
model: readString(process.env.LOCAL_CODEX_MODEL),
|
||||||
profile: readString(process.env.LOCAL_CODEX_PROFILE),
|
profile: readString(process.env.LOCAL_CODEX_PROFILE),
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,18 @@ function formatAttachmentBlock(attachments) {
|
||||||
.join("\n");
|
.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPrompt(request) {
|
function buildPrompt(request, config) {
|
||||||
const userText = request.text?.trim() || "(empty)";
|
const userText = request.text?.trim() || "(empty)";
|
||||||
const username = request.source.username || "(unknown)";
|
const username = request.source.username || "(unknown)";
|
||||||
const displayName = request.source.displayName || "(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 [
|
return [
|
||||||
"You are answering a Telegram user through a bot connector.",
|
"You are answering a Telegram user through a bot connector.",
|
||||||
|
|
@ -92,6 +100,7 @@ function buildPrompt(request) {
|
||||||
"",
|
"",
|
||||||
"Attachments:",
|
"Attachments:",
|
||||||
formatAttachmentBlock(request.attachments),
|
formatAttachmentBlock(request.attachments),
|
||||||
|
...skillDirBlock,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,6 +136,7 @@ function buildCodexArgs(config, request, outputPath) {
|
||||||
.map((attachment) => path.dirname(attachment.path))
|
.map((attachment) => path.dirname(attachment.path))
|
||||||
.filter((attachmentDir) => !isInsideDirectory(config.workdir, attachmentDir));
|
.filter((attachmentDir) => !isInsideDirectory(config.workdir, attachmentDir));
|
||||||
const addDirs = dedupePaths([
|
const addDirs = dedupePaths([
|
||||||
|
config.skillDir,
|
||||||
...config.addDirs,
|
...config.addDirs,
|
||||||
...attachmentDirs,
|
...attachmentDirs,
|
||||||
]);
|
]);
|
||||||
|
|
@ -181,6 +191,7 @@ function inferAttachmentKind(filePath, rawKind) {
|
||||||
function getAllowedFileRoots(config, request) {
|
function getAllowedFileRoots(config, request) {
|
||||||
return dedupePaths([
|
return dedupePaths([
|
||||||
config.workdir,
|
config.workdir,
|
||||||
|
config.skillDir,
|
||||||
...config.addDirs,
|
...config.addDirs,
|
||||||
...request.attachments.map((attachment) => path.dirname(attachment.path)),
|
...request.attachments.map((attachment) => path.dirname(attachment.path)),
|
||||||
]);
|
]);
|
||||||
|
|
@ -295,7 +306,7 @@ class LocalCodexConnectorStrategy extends ConnectorStrategy {
|
||||||
"last-message.txt",
|
"last-message.txt",
|
||||||
);
|
);
|
||||||
const args = buildCodexArgs(this.config, request, outputPath);
|
const args = buildCodexArgs(this.config, request, outputPath);
|
||||||
const prompt = buildPrompt(request);
|
const prompt = buildPrompt(request, this.config);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await runCodex(this.config.command, args, this.config.workdir, prompt);
|
await runCodex(this.config.command, args, this.config.workdir, prompt);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue