@triflux/remote 10.30.0 → 10.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hub/workers/codex-mcp.mjs +87 -3
- package/package.json +1 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// hub/workers/codex-mcp.mjs — Codex MCP 서버 래퍼
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
2
5
|
import process from "node:process";
|
|
3
6
|
import { fileURLToPath } from "node:url";
|
|
4
7
|
|
|
@@ -94,13 +97,78 @@ function normalizeStructuredContent(structuredContent, fallbackText = "") {
|
|
|
94
97
|
return { threadId, content };
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Read a top-level TOML scalar (`key = "value"` / `key = 'value'` /
|
|
102
|
+
* `key = value`), tolerating an inline `# comment` and surrounding whitespace.
|
|
103
|
+
* Returns the trimmed value, or null when the key is absent/empty. Intentionally
|
|
104
|
+
* tiny — the per-profile files only carry `model` + `model_reasoning_effort`.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} raw
|
|
107
|
+
* @param {string} key
|
|
108
|
+
* @returns {string|null}
|
|
109
|
+
*/
|
|
110
|
+
function readTomlScalar(raw, key) {
|
|
111
|
+
const re = new RegExp(
|
|
112
|
+
`^\\s*${key}\\s*=\\s*(?:"([^"\\n]*)"|'([^'\\n]*)'|([^#\\n]*?))\\s*(?:#.*)?$`,
|
|
113
|
+
"m",
|
|
114
|
+
);
|
|
115
|
+
const match = raw.match(re);
|
|
116
|
+
if (!match) return null;
|
|
117
|
+
const value = (match[1] ?? match[2] ?? match[3] ?? "").trim();
|
|
118
|
+
return value || null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Resolve a Codex effort profile name (e.g. "gpt55_high") to its concrete
|
|
123
|
+
* `model` + reasoning effort. SSOT = the per-profile file written by
|
|
124
|
+
* `tfx setup` at `~/.codex/<profile>.config.toml`. Falls back to deriving the
|
|
125
|
+
* effort from the `<...>_<effort>` naming convention when the file is absent.
|
|
126
|
+
*
|
|
127
|
+
* codex 0.137 removed `profile` from the Codex MCP tool schema, so we translate
|
|
128
|
+
* the profile into the still-accepted `model` + `config` fields instead of
|
|
129
|
+
* forwarding `profile` (which now hard-fails with "unknown field `profile`").
|
|
130
|
+
*
|
|
131
|
+
* @param {string} profileName
|
|
132
|
+
* @returns {{ model: string|null, reasoningEffort: string|null }}
|
|
133
|
+
*/
|
|
134
|
+
export function resolveCodexProfileConfig(profileName) {
|
|
135
|
+
const result = { model: null, reasoningEffort: null };
|
|
136
|
+
if (typeof profileName !== "string" || !profileName) return result;
|
|
137
|
+
|
|
138
|
+
const codexHome = process.env.CODEX_HOME || join(homedir(), ".codex");
|
|
139
|
+
try {
|
|
140
|
+
const raw = readFileSync(
|
|
141
|
+
join(codexHome, `${profileName}.config.toml`),
|
|
142
|
+
"utf8",
|
|
143
|
+
);
|
|
144
|
+
result.model = readTomlScalar(raw, "model");
|
|
145
|
+
result.reasoningEffort = readTomlScalar(raw, "model_reasoning_effort");
|
|
146
|
+
} catch {
|
|
147
|
+
// Profile file absent (non-file alias). Derive effort from the naming
|
|
148
|
+
// convention; leave model unset so codex uses its config.toml default.
|
|
149
|
+
const suffix = /_(xhigh|high|med|medium|low)$/.exec(profileName);
|
|
150
|
+
if (suffix) {
|
|
151
|
+
const map = {
|
|
152
|
+
xhigh: "xhigh",
|
|
153
|
+
high: "high",
|
|
154
|
+
med: "medium",
|
|
155
|
+
medium: "medium",
|
|
156
|
+
low: "low",
|
|
157
|
+
};
|
|
158
|
+
result.reasoningEffort = map[suffix[1]] || null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function buildCodexArguments(prompt, opts = {}) {
|
|
98
165
|
const args = { prompt };
|
|
99
166
|
|
|
100
167
|
if (typeof opts.cwd === "string" && opts.cwd) args.cwd = opts.cwd;
|
|
101
168
|
if (typeof opts.model === "string" && opts.model) args.model = opts.model;
|
|
102
|
-
|
|
103
|
-
|
|
169
|
+
// NOTE: `profile` is intentionally NOT forwarded as a Codex tool argument.
|
|
170
|
+
// codex 0.137 dropped it from the tool schema; it is resolved into
|
|
171
|
+
// `model` + `config.model_reasoning_effort` just before return (see below).
|
|
104
172
|
if (typeof opts.approvalPolicy === "string" && opts.approvalPolicy) {
|
|
105
173
|
args["approval-policy"] = opts.approvalPolicy;
|
|
106
174
|
}
|
|
@@ -120,6 +188,22 @@ function buildCodexArguments(prompt, opts = {}) {
|
|
|
120
188
|
args["compact-prompt"] = opts.compactPrompt;
|
|
121
189
|
}
|
|
122
190
|
|
|
191
|
+
// Map the effort profile to model + reasoning effort instead of forwarding the
|
|
192
|
+
// (0.137-rejected) `profile` field. Explicit opts.model wins over the profile;
|
|
193
|
+
// profile effort merges on top of any opts.config.
|
|
194
|
+
if (typeof opts.profile === "string" && opts.profile) {
|
|
195
|
+
const resolved = resolveCodexProfileConfig(opts.profile);
|
|
196
|
+
if (resolved.model && typeof args.model !== "string") {
|
|
197
|
+
args.model = resolved.model;
|
|
198
|
+
}
|
|
199
|
+
if (resolved.reasoningEffort) {
|
|
200
|
+
args.config = {
|
|
201
|
+
...(args.config || {}),
|
|
202
|
+
model_reasoning_effort: resolved.reasoningEffort,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
123
207
|
return args;
|
|
124
208
|
}
|
|
125
209
|
|