@bitkyc08/opencodex 2.7.7 → 2.7.8
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.
|
@@ -37,4 +37,4 @@ v2: 多线程代理(spawn_agent)。所有模型使用 v2 协作界面。
|
|
|
37
37
|
-d '{
|
|
38
38
|
"model": "gpt-5.4",
|
|
39
39
|
"input": "Hello, world!"
|
|
40
|
-
}'`})]})]})}var zt=`opencodex-api-token`,Bt=!1,Vt=null;function Ht(e){try{let t=e instanceof Request?e.url:String(e);return new URL(t,window.location.href).pathname}catch{return null}}function Ut(e){let t=Ht(e);return!!t&&(t.startsWith(`/api/`)||t.startsWith(`/v1/`))}function Wt(){try{return sessionStorage.getItem(zt)?.trim()||null}catch{return null}}function Gt(e){try{sessionStorage.setItem(zt,e)}catch{}}function Kt(){try{sessionStorage.removeItem(zt)}catch{}}function qt(e,t,n){let r=new Headers(t?.headers??(e instanceof Request?e.headers:void 0));return r.set(`X-OpenCodex-API-Key`,n),e instanceof Request?[new Request(e,{headers:r}),t?{...t,headers:r}:void 0]:[e,{...t,headers:r}]}async function Jt(){return Vt||(Vt=Promise.resolve().then(()=>window.prompt(`OpenCodex API token`)?.trim()||null).finally(()=>{Vt=null}),Vt)}function Yt(){if(Bt)return;Bt=!0;let e=window.fetch.bind(window);window.fetch=async(t,n)=>{if(!Ut(t))return e(t,n);let r=Wt(),[i,a]=r?qt(t,n,r):[t,n],o=await e(i,a);if(o.status!==401)return o;r&&Kt();let s=await Jt();if(!s)return o;Gt(s);let[c,l]=qt(t,n,s),u=await e(c,l);return u.status===401&&Kt(),u}}Yt();var Xt=new Set([`dashboard`,`providers`,`models`,`subagents`,`logs`,`debug`,`usage`,`codex-auth`,`api`]);function Zt(){let e=location.hash.replace(/^#\/?/,``);return Xt.has(e)?e:`dashboard`}var Qt=``,$t=`ocx-theme`,en=[{id:`dashboard`,tkey:`nav.dashboard`,Icon:w},{id:`providers`,tkey:`nav.providers`,Icon:T},{id:`models`,tkey:`nav.models`,Icon:E},{id:`subagents`,tkey:`nav.subagents`,Icon:ee},{id:`logs`,tkey:`nav.logs`,Icon:D},{id:`debug`,tkey:`nav.debug`,Icon:te},{id:`usage`,tkey:`nav.usage`,Icon:ne},{id:`codex-auth`,tkey:`nav.codexAuth`,Icon:ce},{id:`api`,tkey:`nav.api`,Icon:he}],tn={light:fe,dark:pe,system:me},nn={light:`theme.light`,dark:`theme.dark`,system:`theme.system`};function rn(e){if(!e||typeof e!=`object`||!(`version`in e))return null;let t=e.version;return typeof t==`string`&&t.length>0?t:null}function an(){let e=localStorage.getItem($t);return e===`light`||e===`dark`?e:`system`}function on(){let[e,t]=(0,_.useState)(Zt),n=e=>{location.hash=e,t(e)},[r,i]=(0,_.useState)(an),[a,o]=(0,_.useState)(null),{locale:s,setLocale:c}=we(),l=Te();(0,_.useEffect)(()=>{let e=()=>t(Zt());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,_.useEffect)(()=>{let e=document.documentElement;r===`system`?(e.removeAttribute(`data-theme`),localStorage.removeItem($t)):(e.setAttribute(`data-theme`,r),localStorage.setItem($t,r))},[r]),(0,_.useEffect)(()=>{let e=!1,t=async()=>{try{let t=await fetch(`${Qt}/healthz`);if(!t.ok)return;let n=rn(await t.json());!e&&n&&o(n)}catch{}};t();let n=setInterval(t,3e4);return()=>{e=!0,clearInterval(n)}},[]);let u=()=>i(e=>e===`light`?`dark`:e===`dark`?`system`:`light`),d=tn[r],f=a??`2.7.
|
|
40
|
+
}'`})]})]})}var zt=`opencodex-api-token`,Bt=!1,Vt=null;function Ht(e){try{let t=e instanceof Request?e.url:String(e);return new URL(t,window.location.href).pathname}catch{return null}}function Ut(e){let t=Ht(e);return!!t&&(t.startsWith(`/api/`)||t.startsWith(`/v1/`))}function Wt(){try{return sessionStorage.getItem(zt)?.trim()||null}catch{return null}}function Gt(e){try{sessionStorage.setItem(zt,e)}catch{}}function Kt(){try{sessionStorage.removeItem(zt)}catch{}}function qt(e,t,n){let r=new Headers(t?.headers??(e instanceof Request?e.headers:void 0));return r.set(`X-OpenCodex-API-Key`,n),e instanceof Request?[new Request(e,{headers:r}),t?{...t,headers:r}:void 0]:[e,{...t,headers:r}]}async function Jt(){return Vt||(Vt=Promise.resolve().then(()=>window.prompt(`OpenCodex API token`)?.trim()||null).finally(()=>{Vt=null}),Vt)}function Yt(){if(Bt)return;Bt=!0;let e=window.fetch.bind(window);window.fetch=async(t,n)=>{if(!Ut(t))return e(t,n);let r=Wt(),[i,a]=r?qt(t,n,r):[t,n],o=await e(i,a);if(o.status!==401)return o;r&&Kt();let s=await Jt();if(!s)return o;Gt(s);let[c,l]=qt(t,n,s),u=await e(c,l);return u.status===401&&Kt(),u}}Yt();var Xt=new Set([`dashboard`,`providers`,`models`,`subagents`,`logs`,`debug`,`usage`,`codex-auth`,`api`]);function Zt(){let e=location.hash.replace(/^#\/?/,``);return Xt.has(e)?e:`dashboard`}var Qt=``,$t=`ocx-theme`,en=[{id:`dashboard`,tkey:`nav.dashboard`,Icon:w},{id:`providers`,tkey:`nav.providers`,Icon:T},{id:`models`,tkey:`nav.models`,Icon:E},{id:`subagents`,tkey:`nav.subagents`,Icon:ee},{id:`logs`,tkey:`nav.logs`,Icon:D},{id:`debug`,tkey:`nav.debug`,Icon:te},{id:`usage`,tkey:`nav.usage`,Icon:ne},{id:`codex-auth`,tkey:`nav.codexAuth`,Icon:ce},{id:`api`,tkey:`nav.api`,Icon:he}],tn={light:fe,dark:pe,system:me},nn={light:`theme.light`,dark:`theme.dark`,system:`theme.system`};function rn(e){if(!e||typeof e!=`object`||!(`version`in e))return null;let t=e.version;return typeof t==`string`&&t.length>0?t:null}function an(){let e=localStorage.getItem($t);return e===`light`||e===`dark`?e:`system`}function on(){let[e,t]=(0,_.useState)(Zt),n=e=>{location.hash=e,t(e)},[r,i]=(0,_.useState)(an),[a,o]=(0,_.useState)(null),{locale:s,setLocale:c}=we(),l=Te();(0,_.useEffect)(()=>{let e=()=>t(Zt());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,_.useEffect)(()=>{let e=document.documentElement;r===`system`?(e.removeAttribute(`data-theme`),localStorage.removeItem($t)):(e.setAttribute(`data-theme`,r),localStorage.setItem($t,r))},[r]),(0,_.useEffect)(()=>{let e=!1,t=async()=>{try{let t=await fetch(`${Qt}/healthz`);if(!t.ok)return;let n=rn(await t.json());!e&&n&&o(n)}catch{}};t();let n=setInterval(t,3e4);return()=>{e=!0,clearInterval(n)}},[]);let u=()=>i(e=>e===`light`?`dark`:e===`dark`?`system`:`light`),d=tn[r],f=a??`2.7.8`,[p,m]=(0,_.useState)(!1);return(0,S.jsxs)(`div`,{className:`app`,children:[(0,S.jsxs)(`aside`,{className:`sidebar`,children:[(0,S.jsxs)(`div`,{className:`brand`,children:[(0,S.jsx)(`span`,{className:`brand-logo`,role:`img`,"aria-label":`opencodex logo`}),(0,S.jsx)(`span`,{className:`name`,children:`opencodex`}),(0,S.jsxs)(`span`,{className:`ver`,children:[`v`,f]})]}),(0,S.jsx)(`nav`,{children:en.map(({id:t,tkey:r,Icon:i})=>(0,S.jsxs)(`button`,{className:`nav-item${e===t?` active`:``}`,"data-page":t,onClick:()=>n(t),"aria-current":e===t?`page`:void 0,children:[(0,S.jsx)(i,{}),` `,l(r)]},t))}),(0,S.jsxs)(`div`,{className:`sidebar-foot`,children:[(0,S.jsxs)(`div`,{className:`lang-toggle`,children:[(0,S.jsx)(he,{"aria-hidden":!0}),(0,S.jsx)(Me,{value:s,options:ve.map(e=>({value:e.code,label:e.name})),onChange:e=>c(e),label:l(`lang.label`),placement:`right`,style:{flex:1,minWidth:0,width:`100%`}})]}),(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle`,onClick:u,"aria-label":`${l(`theme.label`)}: ${l(nn[r])}`,title:`${l(`theme.label`)}: ${l(nn[r])}`,children:[(0,S.jsx)(d,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:l(nn[r])})]}),(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle stop-toggle`,onClick:async()=>{if(confirm(l(`dash.stopConfirm`))){m(!0);try{await fetch(`${Qt}/api/stop`,{method:`POST`})}catch{}}},disabled:p,"aria-label":l(`dash.stop`),title:l(`dash.stop`),children:[(0,S.jsx)(I,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:l(p?`dash.stopping`:`dash.stop`)})]}),(0,S.jsxs)(`a`,{className:`sidebar-link`,href:`https://github.com/lidge-jun/opencodex`,target:`_blank`,rel:`noreferrer`,children:[(0,S.jsx)(F,{}),` `,l(`common.github`)]})]})]}),(0,S.jsx)(`main`,{className:`main`,children:(0,S.jsxs)(`div`,{className:`main-inner`,children:[e===`dashboard`&&(0,S.jsx)(qe,{apiBase:Qt}),e===`providers`&&(0,S.jsx)(st,{apiBase:Qt}),e===`models`&&(0,S.jsx)(ft,{apiBase:Qt}),e===`subagents`&&(0,S.jsx)(pt,{apiBase:Qt}),e===`logs`&&(0,S.jsx)(Ct,{apiBase:Qt}),e===`debug`&&(0,S.jsx)(wt,{apiBase:Qt}),e===`usage`&&(0,S.jsx)(jt,{apiBase:Qt}),e===`codex-auth`&&(0,S.jsx)(Nt,{apiBase:Qt}),e===`api`&&(0,S.jsx)(Rt,{apiBase:Qt})]})})]})}v.createRoot(document.getElementById(`root`)).render((0,S.jsx)(_.StrictMode,{children:(0,S.jsx)(Ce,{children:(0,S.jsx)(on,{})})}));
|
package/gui/dist/index.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-Bp8dDrs5.js"></script>
|
|
20
20
|
<link rel="stylesheet" crossorigin href="/assets/index-C0xVu72_.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
package/package.json
CHANGED
package/src/responses/parser.ts
CHANGED
|
@@ -278,6 +278,34 @@ export function parseRequest(body: unknown): OcxParsedRequest {
|
|
|
278
278
|
continue;
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
if (effectiveType === "agent_message") {
|
|
282
|
+
const agentMessage = item as {
|
|
283
|
+
author?: string;
|
|
284
|
+
recipient?: string;
|
|
285
|
+
content?: unknown;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const content = inputContentParts(
|
|
289
|
+
agentMessage.content as unknown[] | string | undefined,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
const hasContent =
|
|
293
|
+
typeof content === "string"
|
|
294
|
+
? content.trim().length > 0
|
|
295
|
+
: content.length > 0;
|
|
296
|
+
|
|
297
|
+
// An agent_message is external input delivered to the parent agent.
|
|
298
|
+
// Preserve it as a user-role turn so signed Anthropic thinking blocks
|
|
299
|
+
// on either side are never merged into one modified assistant response.
|
|
300
|
+
messages.push({
|
|
301
|
+
role: "user",
|
|
302
|
+
content: hasContent ? content : "(sub-agent message received)",
|
|
303
|
+
timestamp: now,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
|
|
281
309
|
if (effectiveType === "message") {
|
|
282
310
|
const msg = item as { role?: string; content?: unknown };
|
|
283
311
|
switch (msg.role) {
|
package/src/server/responses.ts
CHANGED
|
@@ -298,10 +298,19 @@ function encryptedSlotParts(payload: string): Array<Record<string, string>> {
|
|
|
298
298
|
return parts.length > 0 ? parts : [{ type: "input_text", text: payload }];
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
function hasEncryptedContentPart(content: unknown): boolean {
|
|
302
|
+
return Array.isArray(content) && content.some(part => (
|
|
303
|
+
part && typeof part === "object"
|
|
304
|
+
&& (part as { type?: unknown }).type === "encrypted_content"
|
|
305
|
+
));
|
|
306
|
+
}
|
|
307
|
+
|
|
301
308
|
/**
|
|
302
309
|
* Rewrite non-ciphertext `{type:"encrypted_content"}` parts into `{type:"input_text"}`
|
|
303
|
-
* throughout a
|
|
304
|
-
*
|
|
310
|
+
* throughout a request's input items (message content and function_call_output content
|
|
311
|
+
* arrays share the part shape, codex-rs protocol/models.rs). An `agent_message` whose
|
|
312
|
+
* payload becomes entirely plaintext is normalized to a user message so routed parsers
|
|
313
|
+
* that do not understand the internal item type still deliver the spawn task.
|
|
305
314
|
* Genuine backend blobs are left byte-identical so replay/cache semantics survive, and
|
|
306
315
|
* MIXED slots (plaintext preamble + embedded Fernet task body) are split so the backend
|
|
307
316
|
* decrypts the blob while the prose passes as text. Returns the number of parts rewritten.
|
|
@@ -309,7 +318,8 @@ function encryptedSlotParts(payload: string): Array<Record<string, string>> {
|
|
|
309
318
|
export function sanitizeEncryptedContentInPlace(input: unknown): number {
|
|
310
319
|
if (!Array.isArray(input)) return 0;
|
|
311
320
|
let rewritten = 0;
|
|
312
|
-
const visit = (node: unknown):
|
|
321
|
+
const visit = (node: unknown): number => {
|
|
322
|
+
const before = rewritten;
|
|
313
323
|
if (Array.isArray(node)) {
|
|
314
324
|
for (let i = 0; i < node.length; i += 1) {
|
|
315
325
|
const child = node[i] as unknown;
|
|
@@ -327,13 +337,26 @@ export function sanitizeEncryptedContentInPlace(input: unknown): number {
|
|
|
327
337
|
continue;
|
|
328
338
|
}
|
|
329
339
|
}
|
|
330
|
-
visit(child);
|
|
340
|
+
const childRewrites = visit(child);
|
|
341
|
+
if (
|
|
342
|
+
childRewrites > 0
|
|
343
|
+
&& child && typeof child === "object"
|
|
344
|
+
&& (child as { type?: unknown }).type === "agent_message"
|
|
345
|
+
&& !hasEncryptedContentPart((child as { content?: unknown }).content)
|
|
346
|
+
) {
|
|
347
|
+
const message = child as { type: string; role?: string; author?: unknown; recipient?: unknown };
|
|
348
|
+
message.type = "message";
|
|
349
|
+
message.role = "user";
|
|
350
|
+
delete message.author;
|
|
351
|
+
delete message.recipient;
|
|
352
|
+
}
|
|
331
353
|
}
|
|
332
|
-
return;
|
|
354
|
+
return rewritten - before;
|
|
333
355
|
}
|
|
334
356
|
if (node && typeof node === "object") {
|
|
335
357
|
for (const value of Object.values(node)) visit(value);
|
|
336
358
|
}
|
|
359
|
+
return rewritten - before;
|
|
337
360
|
};
|
|
338
361
|
visit(input);
|
|
339
362
|
return rewritten;
|
|
@@ -410,6 +433,21 @@ export async function handleResponses(
|
|
|
410
433
|
body = expandPreviousResponseInput(body);
|
|
411
434
|
const previousResponseInputExpanded = body !== originalBody;
|
|
412
435
|
|
|
436
|
+
// Spawn-message compatibility (both directions): agent_message task payloads ride in
|
|
437
|
+
// encrypted_content slots as plaintext. Rewrite them to input_text on the RAW body BEFORE
|
|
438
|
+
// parsing so every consumer sees the payload: parseRequest (routed/translated providers read
|
|
439
|
+
// the parsed messages) and the native passthrough (_rawBody is this same object, serialized
|
|
440
|
+
// verbatim). Genuine backend ciphertext is left byte-identical (looksLikeBackendCiphertext).
|
|
441
|
+
{
|
|
442
|
+
const rewritten = sanitizeEncryptedContentInPlace(
|
|
443
|
+
(body as { input?: unknown } | undefined)?.input,
|
|
444
|
+
);
|
|
445
|
+
if (rewritten > 0)
|
|
446
|
+
console.warn(
|
|
447
|
+
`[opencodex] rewrote ${rewritten} plaintext encrypted_content part(s) to input_text (spawn-message compatibility)`,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
413
451
|
let parsed;
|
|
414
452
|
try {
|
|
415
453
|
parsed = parseRequest(body);
|
|
@@ -452,15 +490,6 @@ export async function handleResponses(
|
|
|
452
490
|
// Runs BEFORE the mock-max clamp below so the synthetic top tier (ultra arrives
|
|
453
491
|
// as max on the codex wire) is still visible. Both request shapes are rewritten.
|
|
454
492
|
{
|
|
455
|
-
const requestedModelId = logCtx.requestedModel ?? route.modelId;
|
|
456
|
-
// Cross-provider spawn poison fix: native-bound requests may carry plaintext parked in
|
|
457
|
-
// encrypted_content slots (spawn messages minted under a routed parent). Rewrite them
|
|
458
|
-
// to input_text before the passthrough serializes _rawBody verbatim.
|
|
459
|
-
if (!requestedModelId.includes("/")) {
|
|
460
|
-
const raw = parsed._rawBody as { input?: unknown } | undefined;
|
|
461
|
-
const rewritten = sanitizeEncryptedContentInPlace(raw?.input);
|
|
462
|
-
if (rewritten > 0) console.warn(`[opencodex] ${route.modelId}: rewrote ${rewritten} plaintext encrypted_content part(s) to input_text (routed-parent spawn compatibility)`);
|
|
463
|
-
}
|
|
464
493
|
const guidance = await multiAgentGuidanceText(parsed, config.injectionModel, config.injectionEffort, config.subagentModels, config.injectionPrompt);
|
|
465
494
|
if (guidance) {
|
|
466
495
|
injectDeveloperMessage(parsed, guidance);
|