minecodex 0.1.19 → 0.2.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/features/images/codex-feature.json +5 -0
- package/features/images/src/chatgpt-image-source-page.mjs +207 -0
- package/features/images/src/chatgpt-image-source.mjs +42 -0
- package/features/images/src/http-server.mjs +92 -3
- package/features/images/src/image-library.mjs +171 -4
- package/features/images/web/app.js +35 -13
- package/features/model-slider/THIRD_PARTY_NOTICES.md +7 -0
- package/features/model-slider/assets/providers/muse.svg +1 -0
- package/features/model-slider/codex-feature.json +8 -2
- package/package.json +1 -1
- package/packages/cli/src/chatgpt-launch-coordinator.mjs +27 -1
- package/packages/cli/src/platform.mjs +0 -12
- package/packages/cli/src/runtime-manager.mjs +25 -28
- package/packages/runtime-host/src/codex-runtime.mjs +397 -135
- package/packages/runtime-host/src/feature-registry.mjs +56 -10
|
@@ -46,9 +46,10 @@ const SOURCE_ICON_MARKUP = {
|
|
|
46
46
|
].join(""),
|
|
47
47
|
project: '<path fill-rule="evenodd" clip-rule="evenodd" d="M4.75488 2.1416C5.30942 2.14164 5.74594 2.23705 6.11816 2.38965C6.48323 2.53934 6.76728 2.73817 7.00391 2.9043L7.02148 2.91699C7.47057 3.23238 7.8162 3.47463 8.55176 3.47461H11.333C12.7194 3.47484 13.8311 4.61217 13.8311 6L13.875 6.38281H13.8594C14.8729 6.38292 15.5982 7.3629 15.3018 8.33203L14.0068 12.5586C13.7703 13.3297 13.0576 13.8563 12.251 13.8564H3.83984C3.4199 13.8564 3.04144 13.7174 2.73828 13.4883L2.67383 13.4346C1.99907 12.9811 1.55577 12.2065 1.55566 11.3311L0.941406 4.66699C0.941406 3.2792 2.05315 2.1419 3.43945 2.1416H4.75488ZM4.7627 7.42969C4.56039 7.42972 4.3807 7.5625 4.32129 7.75586L3.08594 11.7891C2.96123 12.1965 3.18214 12.6072 3.54883 12.7529C3.63476 12.7768 3.74102 12.7958 3.88184 12.8086H12.251C12.5974 12.8085 12.9033 12.5821 13.0049 12.251L14.2998 8.02539C14.3901 7.72947 14.1688 7.42979 13.8594 7.42969H4.7627ZM3.43945 3.19141C2.64724 3.1917 1.99121 3.84481 1.99121 4.66699L2.49316 10.1201L3.32031 7.44922C3.51452 6.81571 4.10008 6.38284 4.7627 6.38281H12.8252L12.7812 6C12.7812 5.22902 12.2045 4.607 11.4795 4.53223L11.333 4.52441H8.55176C8.05756 4.52442 7.64464 4.44062 7.2666 4.2793C6.91453 4.12896 6.6274 3.92345 6.41797 3.77637L6.40039 3.76367C6.16212 3.59639 5.96404 3.46151 5.71973 3.36133C5.54113 3.28812 5.32754 3.2289 5.05176 3.2041L4.75488 3.19141H3.43945Z" fill="currentColor"></path>',
|
|
48
48
|
chat: '<path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"></path>',
|
|
49
|
+
work: '<path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"></path>',
|
|
49
50
|
};
|
|
50
51
|
|
|
51
|
-
let facets = { total: 0, chats: 0, projects: [] };
|
|
52
|
+
let facets = { total: 0, chats: 0, works: 0, projects: [] };
|
|
52
53
|
let selectedScope = "all";
|
|
53
54
|
let groupByThread = false;
|
|
54
55
|
let viewContext = "browse";
|
|
@@ -155,6 +156,9 @@ function sourcePresentation(image) {
|
|
|
155
156
|
if (image.sourceKind === "project") {
|
|
156
157
|
return { kind: "project", label: image.projectName || "Project" };
|
|
157
158
|
}
|
|
159
|
+
if (image.sourceKind === "work") {
|
|
160
|
+
return { kind: "work", label: image.threadTitle || "Work" };
|
|
161
|
+
}
|
|
158
162
|
return { kind: "chat", label: image.threadTitle || "Untitled chat" };
|
|
159
163
|
}
|
|
160
164
|
|
|
@@ -170,6 +174,7 @@ function createSourceValue(kind, label, className = "") {
|
|
|
170
174
|
function selectedFacet() {
|
|
171
175
|
if (selectedScope === "all") return { label: "All images", count: facets.total, kind: "all" };
|
|
172
176
|
if (selectedScope === "chat") return { label: "Chats", count: facets.chats, kind: "chat" };
|
|
177
|
+
if (selectedScope === "work") return { label: "Work", count: facets.works, kind: "work" };
|
|
173
178
|
const project = facets.projects.find((item) => item.scope === selectedScope);
|
|
174
179
|
return project
|
|
175
180
|
? { label: project.name, count: project.count, kind: "project" }
|
|
@@ -210,6 +215,7 @@ function createSourceOption({ scope, label, count, kind }) {
|
|
|
210
215
|
function renderSourceMenu() {
|
|
211
216
|
const all = createSourceOption({ scope: "all", label: "All images", count: facets.total, kind: "all" });
|
|
212
217
|
const chats = createSourceOption({ scope: "chat", label: "Chats", count: facets.chats, kind: "chat" });
|
|
218
|
+
const work = createSourceOption({ scope: "work", label: "Work", count: facets.works, kind: "work" });
|
|
213
219
|
const divider = document.createElement("div");
|
|
214
220
|
divider.className = "source-menu-divider";
|
|
215
221
|
divider.setAttribute("role", "separator");
|
|
@@ -219,7 +225,7 @@ function renderSourceMenu() {
|
|
|
219
225
|
count: project.count,
|
|
220
226
|
kind: "project",
|
|
221
227
|
}));
|
|
222
|
-
sourceMenu.replaceChildren(all, chats, ...(projects.length ? [divider, ...projects] : []));
|
|
228
|
+
sourceMenu.replaceChildren(all, chats, work, ...(projects.length ? [divider, ...projects] : []));
|
|
223
229
|
updateFilterTrigger();
|
|
224
230
|
}
|
|
225
231
|
|
|
@@ -259,6 +265,7 @@ async function loadFacets() {
|
|
|
259
265
|
if (
|
|
260
266
|
selectedScope !== "all"
|
|
261
267
|
&& selectedScope !== "chat"
|
|
268
|
+
&& selectedScope !== "work"
|
|
262
269
|
&& !facets.projects.some((project) => project.scope === selectedScope)
|
|
263
270
|
) {
|
|
264
271
|
selectedScope = "all";
|
|
@@ -362,8 +369,12 @@ function createThreadBoard(group) {
|
|
|
362
369
|
button.className = "thread-board";
|
|
363
370
|
button.type = "button";
|
|
364
371
|
button.dataset.threadId = group.threadId;
|
|
365
|
-
const sourceLabel = group.sourceKind === "project"
|
|
366
|
-
|
|
372
|
+
const sourceLabel = group.sourceKind === "project"
|
|
373
|
+
? (group.projectName || "Project")
|
|
374
|
+
: group.sourceKind === "work" ? "Work" : "Chat";
|
|
375
|
+
const threadTitle = group.threadTitle || (
|
|
376
|
+
group.sourceKind === "project" ? "Untitled task" : group.sourceKind === "work" ? "Untitled work" : "Untitled chat"
|
|
377
|
+
);
|
|
367
378
|
button.setAttribute("aria-label", `Open ${threadTitle}, ${group.imageCount} images`);
|
|
368
379
|
|
|
369
380
|
const preview = document.createElement("span");
|
|
@@ -392,7 +403,11 @@ function createThreadBoard(group) {
|
|
|
392
403
|
|
|
393
404
|
const caption = document.createElement("span");
|
|
394
405
|
caption.className = "thread-board-caption";
|
|
395
|
-
caption.append(createSourceValue(
|
|
406
|
+
caption.append(createSourceValue(
|
|
407
|
+
group.sourceKind === "project" ? "project" : group.sourceKind === "work" ? "work" : "chat",
|
|
408
|
+
sourceLabel,
|
|
409
|
+
"card-source",
|
|
410
|
+
));
|
|
396
411
|
const title = document.createElement("span");
|
|
397
412
|
title.className = "thread-board-title";
|
|
398
413
|
title.textContent = threadTitle;
|
|
@@ -609,12 +624,18 @@ function resetCopyFeedback() {
|
|
|
609
624
|
|
|
610
625
|
function updateSourceAction(image) {
|
|
611
626
|
const archived = Boolean(image.archived);
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
627
|
+
const unavailable = archived || !image.sourceUrl;
|
|
628
|
+
sourceButton.disabled = unavailable;
|
|
629
|
+
sourceAction.classList.toggle("is-disabled", unavailable);
|
|
630
|
+
sourceTooltip.hidden = !unavailable;
|
|
631
|
+
if (unavailable) {
|
|
616
632
|
sourceButton.setAttribute("aria-describedby", sourceTooltip.id);
|
|
617
|
-
|
|
633
|
+
sourceTooltip.textContent = archived
|
|
634
|
+
? "This Thread has been archived and can’t be opened."
|
|
635
|
+
: "This ChatGPT conversation cannot be opened from Images.";
|
|
636
|
+
sourceButton.setAttribute("aria-label", archived
|
|
637
|
+
? "Open Thread unavailable: this Thread has been archived"
|
|
638
|
+
: "Open conversation unavailable");
|
|
618
639
|
} else {
|
|
619
640
|
sourceButton.removeAttribute("aria-describedby");
|
|
620
641
|
sourceButton.removeAttribute("aria-label");
|
|
@@ -719,10 +740,11 @@ function trapDetailContextFocus(event) {
|
|
|
719
740
|
function showImageDetails(image) {
|
|
720
741
|
activeImage = image;
|
|
721
742
|
const isProject = image.sourceKind === "project";
|
|
743
|
+
const isWork = image.sourceKind === "work";
|
|
722
744
|
drawerProjectRow.hidden = !isProject;
|
|
723
745
|
if (isProject) drawerProject.textContent = image.projectName || "Project";
|
|
724
|
-
drawerTaskLabel.textContent = isProject ? "Thread" : "Chat";
|
|
725
|
-
drawerThread.textContent = image.threadTitle || (isProject ? "Untitled task" : "Untitled chat");
|
|
746
|
+
drawerTaskLabel.textContent = isProject ? "Thread" : isWork ? "Work" : "Chat";
|
|
747
|
+
drawerThread.textContent = image.threadTitle || (isProject ? "Untitled task" : isWork ? "Untitled work" : "Untitled chat");
|
|
726
748
|
drawerImage.src = image.fileUrl;
|
|
727
749
|
drawerImage.alt = image.prompt || "Generated image";
|
|
728
750
|
if (image.width && image.height) {
|
|
@@ -959,7 +981,7 @@ rescan.addEventListener("click", async () => {
|
|
|
959
981
|
|
|
960
982
|
drawer.querySelector(".drawer-close").addEventListener("click", closeDrawer);
|
|
961
983
|
sourceButton.addEventListener("click", () => {
|
|
962
|
-
if (activeImage && !activeImage.archived) openSourceThread(activeImage.threadId);
|
|
984
|
+
if (activeImage && !activeImage.archived && activeImage.sourceUrl) openSourceThread(activeImage.threadId);
|
|
963
985
|
});
|
|
964
986
|
|
|
965
987
|
promptCopyButton.addEventListener("click", async () => {
|
|
@@ -22,6 +22,13 @@ distributed under MineCodex's MIT License.
|
|
|
22
22
|
(`icons/xiaomi.svg`), licensed under CC0 1.0. The Xiaomi brand mark remains
|
|
23
23
|
the property of Xiaomi Corporation.
|
|
24
24
|
|
|
25
|
+
## Meta logo (provider mark)
|
|
26
|
+
|
|
27
|
+
`assets/providers/muse.svg` is the official Meta Platforms logo rendered in the
|
|
28
|
+
same 24×24 provider-mark style (Lobe Icons) as the other provider icons in this
|
|
29
|
+
folder. The Meta mark remains the property of Meta Platforms, Inc.; the SVG
|
|
30
|
+
wrapper is distributed under MineCodex's MIT License.
|
|
31
|
+
|
|
25
32
|
## Lucide
|
|
26
33
|
|
|
27
34
|
Lucide icons distributed by MineCodex include the Model Slider UI icons, Notes action and
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Meta</title><path d="M6.897 4h-.024l-.031 2.615h.022c1.715 0 3.046 1.357 5.94 6.246l.175.297.012.02 1.62-2.438-.012-.019a48.763 48.763 0 00-1.098-1.716 28.01 28.01 0 00-1.175-1.629C10.413 4.932 8.812 4 6.896 4z" fill="url(#lobe-icons-meta-0-_R_0_)"></path><path d="M6.873 4C4.95 4.01 3.247 5.258 2.02 7.17a4.352 4.352 0 00-.01.017l2.254 1.231.011-.017c.718-1.083 1.61-1.774 2.568-1.785h.021L6.896 4h-.023z" fill="url(#lobe-icons-meta-1-_R_0_)"></path><path d="M2.019 7.17l-.011.017C1.2 8.447.598 9.995.274 11.664l-.005.022 2.534.6.004-.022c.27-1.467.786-2.828 1.456-3.845l.011-.017L2.02 7.17z" fill="url(#lobe-icons-meta-2-_R_0_)"></path><path d="M2.807 12.264l-2.533-.6-.005.022c-.177.918-.267 1.851-.269 2.786v.023l2.598.233v-.023a12.591 12.591 0 01.21-2.44z" fill="url(#lobe-icons-meta-3-_R_0_)"></path><path d="M2.677 15.537a5.462 5.462 0 01-.079-.813v-.022L0 14.468v.024a8.89 8.89 0 00.146 1.652l2.535-.585a4.106 4.106 0 01-.004-.022z" fill="url(#lobe-icons-meta-4-_R_0_)"></path><path d="M3.27 16.89c-.284-.31-.484-.756-.589-1.328l-.004-.021-2.535.585.004.021c.192 1.01.568 1.85 1.106 2.487l.014.017 2.018-1.745a2.106 2.106 0 01-.015-.016z" fill="url(#lobe-icons-meta-5-_R_0_)"></path><path d="M10.78 9.654c-1.528 2.35-2.454 3.825-2.454 3.825-2.035 3.2-2.739 3.917-3.871 3.917a1.545 1.545 0 01-1.186-.508l-2.017 1.744.014.017C2.01 19.518 3.058 20 4.356 20c1.963 0 3.374-.928 5.884-5.33l1.766-3.13a41.283 41.283 0 00-1.227-1.886z" fill="#0082FB"></path><path d="M13.502 5.946l-.016.016c-.4.43-.786.908-1.16 1.416.378.483.768 1.024 1.175 1.63.48-.743.928-1.345 1.367-1.807l.016-.016-1.382-1.24z" fill="url(#lobe-icons-meta-6-_R_0_)"></path><path d="M20.918 5.713C19.853 4.633 18.583 4 17.225 4c-1.432 0-2.637.787-3.723 1.944l-.016.016 1.382 1.24.016-.017c.715-.747 1.408-1.12 2.176-1.12.826 0 1.6.39 2.27 1.075l.015.016 1.589-1.425-.016-.016z" fill="#0082FB"></path><path d="M23.998 14.125c-.06-3.467-1.27-6.566-3.064-8.396l-.016-.016-1.588 1.424.015.016c1.35 1.392 2.277 3.98 2.361 6.971v.023h2.292v-.022z" fill="url(#lobe-icons-meta-7-_R_0_)"></path><path d="M23.998 14.15v-.023h-2.292v.022c.004.14.006.282.006.424 0 .815-.121 1.474-.368 1.95l-.011.022 1.708 1.782.013-.02c.62-.96.946-2.293.946-3.91 0-.083 0-.165-.002-.247z" fill="url(#lobe-icons-meta-8-_R_0_)"></path><path d="M21.344 16.52l-.011.02c-.214.402-.519.67-.917.787l.778 2.462a3.493 3.493 0 00.438-.182 3.558 3.558 0 001.366-1.218l.044-.065.012-.02-1.71-1.784z" fill="url(#lobe-icons-meta-9-_R_0_)"></path><path d="M19.92 17.393c-.262 0-.492-.039-.718-.14l-.798 2.522c.449.153.927.222 1.46.222.492 0 .943-.073 1.352-.215l-.78-2.462c-.167.05-.341.075-.517.073z" fill="url(#lobe-icons-meta-10-_R_0_)"></path><path d="M18.323 16.534l-.014-.017-1.836 1.914.016.017c.637.682 1.246 1.105 1.937 1.337l.797-2.52c-.291-.125-.573-.353-.9-.731z" fill="url(#lobe-icons-meta-11-_R_0_)"></path><path d="M18.309 16.515c-.55-.642-1.232-1.712-2.303-3.44l-1.396-2.336-.011-.02-1.62 2.438.012.02.989 1.668c.959 1.61 1.74 2.774 2.493 3.585l.016.016 1.834-1.914a2.353 2.353 0 01-.014-.017z" fill="url(#lobe-icons-meta-12-_R_0_)"></path><defs><linearGradient id="lobe-icons-meta-0-_R_0_" x1="75.897%" x2="26.312%" y1="89.199%" y2="12.194%"><stop offset=".06%" stop-color="#0867DF"></stop><stop offset="45.39%" stop-color="#0668E1"></stop><stop offset="85.91%" stop-color="#0064E0"></stop></linearGradient><linearGradient id="lobe-icons-meta-1-_R_0_" x1="21.67%" x2="97.068%" y1="75.874%" y2="23.985%"><stop offset="13.23%" stop-color="#0064DF"></stop><stop offset="99.88%" stop-color="#0064E0"></stop></linearGradient><linearGradient id="lobe-icons-meta-2-_R_0_" x1="38.263%" x2="60.895%" y1="89.127%" y2="16.131%"><stop offset="1.47%" stop-color="#0072EC"></stop><stop offset="68.81%" stop-color="#0064DF"></stop></linearGradient><linearGradient id="lobe-icons-meta-3-_R_0_" x1="47.032%" x2="52.15%" y1="90.19%" y2="15.745%"><stop offset="7.31%" stop-color="#007CF6"></stop><stop offset="99.43%" stop-color="#0072EC"></stop></linearGradient><linearGradient id="lobe-icons-meta-4-_R_0_" x1="52.155%" x2="47.591%" y1="58.301%" y2="37.004%"><stop offset="7.31%" stop-color="#007FF9"></stop><stop offset="100%" stop-color="#007CF6"></stop></linearGradient><linearGradient id="lobe-icons-meta-5-_R_0_" x1="37.689%" x2="61.961%" y1="12.502%" y2="63.624%"><stop offset="7.31%" stop-color="#007FF9"></stop><stop offset="100%" stop-color="#0082FB"></stop></linearGradient><linearGradient id="lobe-icons-meta-6-_R_0_" x1="34.808%" x2="62.313%" y1="68.859%" y2="23.174%"><stop offset="27.99%" stop-color="#007FF8"></stop><stop offset="91.41%" stop-color="#0082FB"></stop></linearGradient><linearGradient id="lobe-icons-meta-7-_R_0_" x1="43.762%" x2="57.602%" y1="6.235%" y2="98.514%"><stop offset="0%" stop-color="#0082FB"></stop><stop offset="99.95%" stop-color="#0081FA"></stop></linearGradient><linearGradient id="lobe-icons-meta-8-_R_0_" x1="60.055%" x2="39.88%" y1="4.661%" y2="69.077%"><stop offset="6.19%" stop-color="#0081FA"></stop><stop offset="100%" stop-color="#0080F9"></stop></linearGradient><linearGradient id="lobe-icons-meta-9-_R_0_" x1="30.282%" x2="61.081%" y1="59.32%" y2="33.244%"><stop offset="0%" stop-color="#027AF3"></stop><stop offset="100%" stop-color="#0080F9"></stop></linearGradient><linearGradient id="lobe-icons-meta-10-_R_0_" x1="20.433%" x2="82.112%" y1="50.001%" y2="50.001%"><stop offset="0%" stop-color="#0377EF"></stop><stop offset="99.94%" stop-color="#0279F1"></stop></linearGradient><linearGradient id="lobe-icons-meta-11-_R_0_" x1="40.303%" x2="72.394%" y1="35.298%" y2="57.811%"><stop offset=".19%" stop-color="#0471E9"></stop><stop offset="100%" stop-color="#0377EF"></stop></linearGradient><linearGradient id="lobe-icons-meta-12-_R_0_" x1="32.254%" x2="68.003%" y1="19.719%" y2="84.908%"><stop offset="27.65%" stop-color="#0867DF"></stop><stop offset="100%" stop-color="#0471E9"></stop></linearGradient></defs></svg>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"id": "model-slider",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.12",
|
|
5
5
|
"label": {
|
|
6
6
|
"en": "Model Slider",
|
|
7
7
|
"zh-CN": "模型滑条"
|
|
@@ -16,12 +16,17 @@
|
|
|
16
16
|
"modelSelector": {
|
|
17
17
|
"maxVisibleItems": 6,
|
|
18
18
|
"favoriteStorageKey": "codex-model-slider:favorites:v1",
|
|
19
|
+
"reasoningEffortOverrides": {
|
|
20
|
+
"opencode-go/hy3": ["low", "medium", "high"],
|
|
21
|
+
"opencode-go/muse-spark-1.2-contributor": ["low", "medium", "high", "xhigh"]
|
|
22
|
+
},
|
|
19
23
|
"providerAliases": {
|
|
20
24
|
"opencode-go": "OpenCode Go",
|
|
21
25
|
"packyapi": "PackyAPI",
|
|
22
26
|
"xai": "xAI",
|
|
23
27
|
"zhipu": "Zhipu AI",
|
|
24
28
|
"google": "Google",
|
|
29
|
+
"meta": "Meta",
|
|
25
30
|
"anthropic": "Anthropic",
|
|
26
31
|
"openai": "OpenAI"
|
|
27
32
|
},
|
|
@@ -49,7 +54,8 @@
|
|
|
49
54
|
{ "id": "kimi", "keywords": ["kimi", "moonshot"], "icon": { "resource": "assets/providers/kimi.svg" } },
|
|
50
55
|
{ "id": "minimax", "keywords": ["minimax"], "icon": { "resource": "assets/providers/minimax.svg" } },
|
|
51
56
|
{ "id": "qwen", "keywords": ["qwen", "qwq"], "icon": { "resource": "assets/providers/qwen.svg" } },
|
|
52
|
-
{ "id": "hunyuan", "keywords": ["hunyuan"], "icon": { "resource": "assets/providers/hunyuan.svg" } },
|
|
57
|
+
{ "id": "hunyuan", "keywords": ["hunyuan", "opencode-go/hy3"], "icon": { "resource": "assets/providers/hunyuan.svg" } },
|
|
58
|
+
{ "id": "muse", "keywords": ["muse", "muse-spark"], "icon": { "resource": "assets/providers/muse.svg" } },
|
|
53
59
|
{ "id": "zhipu", "keywords": ["zhipu", "chatglm", "glm"], "icon": { "resource": "assets/providers/zhipu.svg" } },
|
|
54
60
|
{ "id": "openai", "keywords": ["openai", "gpt", "codex", "o1", "o3", "o4"], "icon": { "resource": "assets/providers/openai.svg" } }
|
|
55
61
|
]
|
package/package.json
CHANGED
|
@@ -65,7 +65,33 @@ export class ChatGPTLaunchCoordinator {
|
|
|
65
65
|
throw new Error(`MineCodex refused to replace ChatGPT pid ${processInfo.pid} after identity changed.`);
|
|
66
66
|
}
|
|
67
67
|
const launched = await this.platform.openNativeCodex({ cdpPort: this.cdpPort });
|
|
68
|
-
|
|
68
|
+
try {
|
|
69
|
+
return await this.attach(launched, true, { startRuntime, waitForHealthy });
|
|
70
|
+
} catch (error) {
|
|
71
|
+
const rollbackErrors = [];
|
|
72
|
+
try {
|
|
73
|
+
await stopRuntime();
|
|
74
|
+
} catch (rollbackError) {
|
|
75
|
+
rollbackErrors.push(rollbackError);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const rolledBack = await this.platform.terminateNativeCodex(launched?.pid, launched?.command);
|
|
79
|
+
if (rolledBack !== 1) {
|
|
80
|
+
throw new Error(`MineCodex could not roll back LaunchServices ChatGPT pid ${launched?.pid}.`);
|
|
81
|
+
}
|
|
82
|
+
} catch (rollbackError) {
|
|
83
|
+
rollbackErrors.push(rollbackError);
|
|
84
|
+
}
|
|
85
|
+
if (rollbackErrors.length) {
|
|
86
|
+
const rollbackFailure = new Error(
|
|
87
|
+
`${error.message}; ChatGPT launch rollback failed: ${rollbackErrors.map(({ message }) => message).join("; ")}`,
|
|
88
|
+
{ cause: error },
|
|
89
|
+
);
|
|
90
|
+
rollbackFailure.retryProcessPids = Number.isInteger(launched?.pid) ? [launched.pid] : [];
|
|
91
|
+
throw rollbackFailure;
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
69
95
|
}
|
|
70
96
|
|
|
71
97
|
async relaunchOwned({ stopRuntime, terminateOwned, startRuntime, waitForHealthy }) {
|
|
@@ -613,18 +613,6 @@ export class MacPlatformAdapter {
|
|
|
613
613
|
return true;
|
|
614
614
|
}
|
|
615
615
|
|
|
616
|
-
async codexProcessStartedMsAgo(processInfo) {
|
|
617
|
-
if (!processInfo || !Number.isInteger(processInfo.pid) || processInfo.pid <= 1) return null;
|
|
618
|
-
const { stdout } = await this.execFile("/bin/ps", ["-axo", "pid=,lstart="]);
|
|
619
|
-
const line = String(stdout).split("\n").find((row) => {
|
|
620
|
-
const match = row.match(/\s*(\d+)\s+(.*)/);
|
|
621
|
-
return match?.[1] === String(processInfo.pid);
|
|
622
|
-
});
|
|
623
|
-
const startedAt = line ? Date.parse(line.replace(/^\s*\d+\s+/, "")) : Number.NaN;
|
|
624
|
-
if (!line || !Number.isFinite(startedAt)) return null;
|
|
625
|
-
return Math.max(0, Date.now() - startedAt);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
616
|
async terminateNativeCodex(pid, expectedCommand = null) {
|
|
629
617
|
if (!Number.isInteger(pid) || pid <= 1) {
|
|
630
618
|
throw new Error("MineCodex requires an exact ChatGPT PID to terminate.");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, open, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { ChatGPTLaunchCoordinator } from "./chatgpt-launch-coordinator.mjs";
|
|
4
4
|
import { readConfig, FEATURE_IDS } from "./config.mjs";
|
|
5
5
|
|
|
@@ -121,9 +121,9 @@ export class RuntimeManager {
|
|
|
121
121
|
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
122
122
|
stopTimeoutMs = 5_000,
|
|
123
123
|
manualLaunchPollMs = 150,
|
|
124
|
-
manualTakeoverGraceMs = 60_000,
|
|
125
124
|
cdpPort = Number(process.env.CODEX_RUNTIME_CDP_PORT ?? 9231),
|
|
126
125
|
launchCoordinator = null,
|
|
126
|
+
fileSystem = { open, rename, unlink },
|
|
127
127
|
}) {
|
|
128
128
|
this.paths = paths;
|
|
129
129
|
this.platform = platform;
|
|
@@ -133,8 +133,8 @@ export class RuntimeManager {
|
|
|
133
133
|
this.sleep = sleep;
|
|
134
134
|
this.stopTimeoutMs = stopTimeoutMs;
|
|
135
135
|
this.manualLaunchPollMs = manualLaunchPollMs;
|
|
136
|
-
this.manualTakeoverGraceMs = manualTakeoverGraceMs;
|
|
137
136
|
this.cdpPort = cdpPort;
|
|
137
|
+
this.fileSystem = fileSystem;
|
|
138
138
|
this.child = null;
|
|
139
139
|
this.appliedFeatures = [];
|
|
140
140
|
this.plugins = [];
|
|
@@ -265,10 +265,11 @@ export class RuntimeManager {
|
|
|
265
265
|
};
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
-
async syncManualLaunchBaseline() {
|
|
268
|
+
async syncManualLaunchBaseline({ excludePids = [] } = {}) {
|
|
269
269
|
const snapshot = await this.nativeCodexSnapshot();
|
|
270
270
|
if (!snapshot) return;
|
|
271
|
-
|
|
271
|
+
const excluded = new Set(excludePids);
|
|
272
|
+
this.manualLaunchBaselinePids = new Set([...snapshot.pids].filter((pid) => !excluded.has(pid)));
|
|
272
273
|
this.manualLaunchArmed = true;
|
|
273
274
|
}
|
|
274
275
|
|
|
@@ -329,19 +330,6 @@ export class RuntimeManager {
|
|
|
329
330
|
this.manualLaunchArmed = false;
|
|
330
331
|
}
|
|
331
332
|
|
|
332
|
-
async isManualTakeoverAllowed(processInfo) {
|
|
333
|
-
if (typeof this.platform?.codexProcessStartedMsAgo !== "function") return true;
|
|
334
|
-
let startedMsAgo;
|
|
335
|
-
try {
|
|
336
|
-
startedMsAgo = await this.platform.codexProcessStartedMsAgo(processInfo);
|
|
337
|
-
} catch (error) {
|
|
338
|
-
this.logger.warn?.("MineCodex could not read the ChatGPT start time", error.message);
|
|
339
|
-
return true;
|
|
340
|
-
}
|
|
341
|
-
if (!Number.isInteger(startedMsAgo) || startedMsAgo < 0) return true;
|
|
342
|
-
return startedMsAgo < this.manualTakeoverGraceMs;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
333
|
async reconcileManualCodexLaunch() {
|
|
346
334
|
if (this.manualLaunchMonitorStopped || this.manualLaunchSuppressionDepth > 0) return false;
|
|
347
335
|
const snapshot = await this.nativeCodexSnapshot();
|
|
@@ -375,6 +363,7 @@ export class RuntimeManager {
|
|
|
375
363
|
this.enhancementFailure = null;
|
|
376
364
|
this.manualLaunchSuppressionDepth += 1;
|
|
377
365
|
return this.enqueue(async () => {
|
|
366
|
+
const retryProcessPids = new Set();
|
|
378
367
|
try {
|
|
379
368
|
if (this.manualLaunchMonitorStopped) return false;
|
|
380
369
|
const latest = await this.nativeCodexSnapshot();
|
|
@@ -387,14 +376,6 @@ export class RuntimeManager {
|
|
|
387
376
|
return false;
|
|
388
377
|
}
|
|
389
378
|
try {
|
|
390
|
-
if (!(await this.isManualTakeoverAllowed(currentProcess))) {
|
|
391
|
-
this.enhancementFailure = {
|
|
392
|
-
code: "OFFICIAL_CHATGPT_IN_USE",
|
|
393
|
-
message: "MineCodex left the official ChatGPT instance running because it was already in use.",
|
|
394
|
-
phase: "launch",
|
|
395
|
-
};
|
|
396
|
-
return null;
|
|
397
|
-
}
|
|
398
379
|
const result = await this.launchCoordinator.ensureObservedLaunch(currentProcess, {
|
|
399
380
|
stopRuntime: () => this.stopInternal(),
|
|
400
381
|
startRuntime: () => this.startInternal(),
|
|
@@ -406,6 +387,8 @@ export class RuntimeManager {
|
|
|
406
387
|
await this.syncManualLaunchBaseline();
|
|
407
388
|
return result;
|
|
408
389
|
} catch (error) {
|
|
390
|
+
retryProcessPids.add(observedProcess.pid);
|
|
391
|
+
for (const pid of error.retryProcessPids ?? []) retryProcessPids.add(pid);
|
|
409
392
|
this.enhancementFailure = normalizedFailure({
|
|
410
393
|
code: error.code ?? "CHATGPT_ENHANCEMENT_FAILED",
|
|
411
394
|
message: error.message,
|
|
@@ -423,7 +406,7 @@ export class RuntimeManager {
|
|
|
423
406
|
}
|
|
424
407
|
} finally {
|
|
425
408
|
this.manualLaunchArmed = true;
|
|
426
|
-
await this.syncManualLaunchBaseline().catch((error) => {
|
|
409
|
+
await this.syncManualLaunchBaseline({ excludePids: retryProcessPids }).catch((error) => {
|
|
427
410
|
this.logger.warn?.("MineCodex could not refresh the ChatGPT launch baseline", error.message);
|
|
428
411
|
});
|
|
429
412
|
this.manualLaunchSuppressionDepth -= 1;
|
|
@@ -441,7 +424,21 @@ export class RuntimeManager {
|
|
|
441
424
|
if (!Number.isInteger(marker.pid) || marker.pid <= 1 || typeof marker.command !== "string" || !marker.command) {
|
|
442
425
|
throw new Error("MineCodex cannot persist incomplete ChatGPT ownership identity.");
|
|
443
426
|
}
|
|
444
|
-
|
|
427
|
+
const temporaryPath = `${this.paths.codexPidPath}.${process.pid}.${Date.now()}.tmp`;
|
|
428
|
+
let committed = false;
|
|
429
|
+
try {
|
|
430
|
+
const handle = await this.fileSystem.open(temporaryPath, "wx", 0o600);
|
|
431
|
+
try {
|
|
432
|
+
await handle.writeFile(`${JSON.stringify(marker)}\n`);
|
|
433
|
+
await handle.sync?.();
|
|
434
|
+
} finally {
|
|
435
|
+
await handle.close();
|
|
436
|
+
}
|
|
437
|
+
await this.fileSystem.rename(temporaryPath, this.paths.codexPidPath);
|
|
438
|
+
committed = true;
|
|
439
|
+
} finally {
|
|
440
|
+
if (!committed) await this.fileSystem.unlink(temporaryPath).catch(() => {});
|
|
441
|
+
}
|
|
445
442
|
}
|
|
446
443
|
|
|
447
444
|
async waitForHealthyRuntime(initialStatus = null, timeoutMs = 25_000) {
|