fix(codex-config): close whole-branch review defects in the Codex panel
Plugins/marketplaces GET routes were silently empty because codex-cli.js
never unwrapped the CLI's {installed,available}/{marketplaces} objects
into arrays; backup writes bypassed the symlink-escape guard entirely;
readFileSafe was stricter than every other Codex reader, showing a
symlinked skill/rule in the list and then refusing to open it;
marketplaceAdd only accepted http(s) URLs, rejecting owner/repo sources
the CLI itself takes. Also: docs drift, a dead i18n key, and a scope-cast
hazard in the shared Editor/ConfirmDelete modals.
Fixes, one per finding: unwrap installed/marketplaces in codex-cli.js so
every list route keeps returning a bare list; guard the backup path in
codex-mutate.js with the same assertNotSymlinkEscape the write path
already used (Claude's cc-mutate.js has no such guard on its primary
write path either, so left untouched there); relax readFileSafe's
containment to lexical-only, matching the other four Codex readers,
while leaving the write side's realpath check untouched; reuse
cc-plugin-cli's assertSource for marketplaceAdd instead of a third
validator (ssh remains out of scope, no credential story for it yet).
Server: 1557/1557 (was 1548). Client: tsc -b clean, 469/469 tests.
This commit is contained in:
@@ -159,8 +159,9 @@ export function CodexConfig() {
|
||||
data: CodexFileResponse | null;
|
||||
error: string | null;
|
||||
} | null>(null);
|
||||
const [editor, setEditor] = useState<EditorState<CodexArtifactType>>(null);
|
||||
const [confirmDelete, setConfirmDelete] = useState<ConfirmDeleteState<CodexArtifactType>>(null);
|
||||
const [editor, setEditor] = useState<EditorState<CodexArtifactType, "user" | "project">>(null);
|
||||
const [confirmDelete, setConfirmDelete] =
|
||||
useState<ConfirmDeleteState<CodexArtifactType, "user" | "project">>(null);
|
||||
const [toast, setToast] = useState<Toast>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -299,12 +300,12 @@ export function CodexConfig() {
|
||||
const handleSave = useCallback(
|
||||
async (args: {
|
||||
type: CodexArtifactType;
|
||||
targetScope: "user" | "project" | "auto-memory";
|
||||
targetScope: "user" | "project";
|
||||
name: string | undefined;
|
||||
content: string;
|
||||
}) => {
|
||||
const result: CodexMutationResult = await api.codexConfig.write({
|
||||
scope: args.targetScope as "user" | "project",
|
||||
scope: args.targetScope,
|
||||
type: args.type,
|
||||
name: args.name,
|
||||
content: args.content,
|
||||
@@ -325,7 +326,7 @@ export function CodexConfig() {
|
||||
if (!confirmDelete) return;
|
||||
try {
|
||||
const result = await api.codexConfig.remove({
|
||||
scope: confirmDelete.scope as "user" | "project",
|
||||
scope: confirmDelete.scope,
|
||||
type: confirmDelete.type,
|
||||
name: confirmDelete.name,
|
||||
});
|
||||
@@ -413,7 +414,7 @@ export function CodexConfig() {
|
||||
)}
|
||||
{viewer && <FileViewer state={viewer} onClose={() => setViewer(null)} />}
|
||||
{editor && (
|
||||
<EditorModal<CodexArtifactType>
|
||||
<EditorModal<CodexArtifactType, "user" | "project">
|
||||
state={editor}
|
||||
fetchContent={(p) => api.codexConfig.file(p).then((r) => r.text)}
|
||||
onClose={() => setEditor(null)}
|
||||
@@ -422,7 +423,7 @@ export function CodexConfig() {
|
||||
/>
|
||||
)}
|
||||
{confirmDelete && (
|
||||
<ConfirmDeleteModal<CodexArtifactType>
|
||||
<ConfirmDeleteModal<CodexArtifactType, "user" | "project">
|
||||
state={confirmDelete}
|
||||
onCancel={() => setConfirmDelete(null)}
|
||||
onConfirm={handleDelete}
|
||||
|
||||
Reference in New Issue
Block a user