minecodex 0.1.1 → 0.1.3
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/README.md +5 -3
- package/features/images/README.md +1 -1
- package/features/images/codex-feature.json +1 -1
- package/features/model-slider/codex-feature.json +1 -1
- package/features/notes/codex-feature.json +1 -1
- package/package.json +1 -1
- package/packages/cli/src/commands.mjs +23 -27
- package/packages/cli/src/runtime-manager.mjs +4 -2
- package/packages/runtime-host/README.md +3 -0
- package/packages/runtime-host/src/codex-runtime.mjs +47 -87
- package/packages/runtime-host/src/main.mjs +2 -1
package/README.md
CHANGED
|
@@ -17,9 +17,11 @@ npm install -g minecodex
|
|
|
17
17
|
mcx install
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
`mcx install`
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
`mcx install` 不会关闭或启动 Codex,也不会改变当前窗口。安装完成后,终端会提示
|
|
21
|
+
MineCodex 将在下次重启 Codex 时启用,并询问是否现在重启;默认答案是 **No**。
|
|
22
|
+
只有用户明确确认,或之后主动运行 `mcx restart`,MineCodex 才会关闭并重新打开
|
|
23
|
+
Codex。安装后,macOS 的“登录项与扩展”中会显示名为 **MineCodex** 的后台项目;
|
|
24
|
+
这个后台项目负责插件与本地控制台,不会在用户退出 Codex 后自动将它重新打开。
|
|
23
25
|
|
|
24
26
|
安装后三个插件默认开启。打开本地控制台:
|
|
25
27
|
|
|
@@ -87,7 +87,7 @@ HTTP + Images UI
|
|
|
87
87
|
- Images 服务需要保持运行,才会持续监听新图片;侧边栏注册由 `CodexRuntimeHost` 统一维持。
|
|
88
88
|
- 每次服务启动都会先开放 health,再在后台补扫停机期间新增的图片并刷新来源名称;初始补扫完成后,运行期间的新图片由文件监听自动加入。手动 Refresh 会再次补扫来源和 Archive 状态,但不会重置筛选、图板模式、详情、滚动位置或列数。刷新 ring 至少显示 1 秒。
|
|
89
89
|
- 侧边栏使用 Codex 当前未公开的 CDP / DOM 能力,Codex UI 更新后只需调整统一宿主;独立 Web UI 和路径索引不受影响。
|
|
90
|
-
- 当前 Codex 的 CSP 会屏蔽普通本地 iframe
|
|
90
|
+
- 当前 Codex 的 CSP 会屏蔽普通本地 iframe。统一宿主会先启用 CSP bypass 并注册 document-start 脚本;尚未完成该初始化的首次或旧版 document 会受控 reload 一次。完成初始化后会留下 document 标记,后续 RuntimeHost 重启只替换旧的 MineCodex runtime,不再刷新 Codex 页面(Issue #1)。
|
|
91
91
|
- 详情打开时会从本机 Session JSONL 按需读取该 ImageGen item 的真实 `revised_prompt` 并缓存到索引;历史记录缺少该事件时明确显示 Prompt 不可用,不从附近文本猜测。
|
|
92
92
|
- 当前没有发现 Codex 会因运行时间增长而自动清理 `generated_images` 的证据。如果未来观察到真实清理,再决定是否增加可选备份。
|
|
93
93
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access, readFile, rm } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { createInterface } from "node:readline/promises";
|
|
3
4
|
import { assertNodeSupported, MacPlatformAdapter } from "./platform.mjs";
|
|
4
5
|
import { ensureConfig, ensureControlToken, readConfig } from "./config.mjs";
|
|
5
6
|
import { createControlServer } from "./control-server.mjs";
|
|
@@ -98,37 +99,32 @@ async function restoreService(platform, snapshot, { cliPath, paths }) {
|
|
|
98
99
|
return platform.restoreServiceState(snapshot, { cliPath, paths });
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
async function nativeSnapshot(platform) {
|
|
102
|
-
if (platform.snapshotNativeCodexState) return platform.snapshotNativeCodexState();
|
|
103
|
-
return { count: 0 };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function restoreNative(platform, snapshot) {
|
|
107
|
-
if (!snapshot?.count) return;
|
|
108
|
-
if (platform.restoreNativeCodexState) {
|
|
109
|
-
await platform.restoreNativeCodexState(snapshot);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
await platform.openNativeCodex?.();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
102
|
function appendRollbackError(error, rollbackError) {
|
|
116
103
|
if (!rollbackError) return error;
|
|
117
104
|
return new Error(`${error.message} Automatic rollback also failed: ${rollbackError.message}`, { cause: error });
|
|
118
105
|
}
|
|
119
106
|
|
|
120
|
-
async function
|
|
107
|
+
export async function confirmRestartNow({ input = process.stdin, terminalOutput = process.stdout } = {}) {
|
|
108
|
+
if (!input.isTTY || !terminalOutput.isTTY) return false;
|
|
109
|
+
const prompt = createInterface({ input, output: terminalOutput });
|
|
110
|
+
try {
|
|
111
|
+
const answer = await prompt.question("Restart Codex now? [y/N] ");
|
|
112
|
+
return /^(y|yes)$/i.test(answer.trim());
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
} finally {
|
|
116
|
+
prompt.close();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function installCommand({ paths, platform, cliPath, output, serviceProbe, fetchImpl, confirmRestart, nodeVersion = process.versions.node }) {
|
|
121
121
|
assertNodeSupported(nodeVersion);
|
|
122
122
|
platform.assertInstallSupported();
|
|
123
123
|
await platform.assertCodexInstalled();
|
|
124
124
|
const previousService = await serviceSnapshot(platform, paths);
|
|
125
|
-
const previousNative = await nativeSnapshot(platform);
|
|
126
125
|
await ensureConfig(paths.configPath);
|
|
127
126
|
await ensureControlToken(paths.tokenPath);
|
|
128
|
-
let nativeCount = 0;
|
|
129
127
|
try {
|
|
130
|
-
nativeCount = await platform.terminateNativeCodex();
|
|
131
|
-
if (nativeCount) output(`Restarting ${nativeCount} running Codex instance${nativeCount === 1 ? "" : "s"} with MineCodex enabled…`);
|
|
132
128
|
await platform.installService({ cliPath, paths, start: true });
|
|
133
129
|
const waitForServiceImpl = serviceProbe?.waitForService ?? waitForService;
|
|
134
130
|
const waitForManagedReadyImpl = serviceProbe?.waitForManagedReady ?? waitForManagedReady;
|
|
@@ -143,15 +139,14 @@ async function installCommand({ paths, platform, cliPath, output, serviceProbe,
|
|
|
143
139
|
} catch (failure) {
|
|
144
140
|
rollbackError = failure;
|
|
145
141
|
}
|
|
146
|
-
try {
|
|
147
|
-
await restoreNative(platform, previousNative?.count ? previousNative : { count: nativeCount });
|
|
148
|
-
} catch (failure) {
|
|
149
|
-
rollbackError = rollbackError ?? failure;
|
|
150
|
-
}
|
|
151
142
|
throw appendRollbackError(error, rollbackError);
|
|
152
143
|
}
|
|
153
|
-
output("MineCodex installed.
|
|
154
|
-
|
|
144
|
+
output("MineCodex installed. It will be enabled the next time Codex restarts.");
|
|
145
|
+
if (await confirmRestart()) {
|
|
146
|
+
await restartCommand({ paths, output, fetchImpl });
|
|
147
|
+
} else {
|
|
148
|
+
output("Run `mcx restart` when you are ready.");
|
|
149
|
+
}
|
|
155
150
|
}
|
|
156
151
|
|
|
157
152
|
async function openCommand({ paths, platform, serviceProbe, fetchImpl }) {
|
|
@@ -377,12 +372,13 @@ export async function runCli(argv, {
|
|
|
377
372
|
npm = createNpmAdapter(),
|
|
378
373
|
serviceProbe,
|
|
379
374
|
fetchImpl = fetch,
|
|
375
|
+
confirmRestart = confirmRestartNow,
|
|
380
376
|
nodeVersion = process.versions.node,
|
|
381
377
|
} = {}) {
|
|
382
378
|
const [command = "--help", ...args] = argv;
|
|
383
379
|
if (["--help", "-h", "help"].includes(command)) return printHelp(output);
|
|
384
380
|
if (["--version", "-v"].includes(command)) return output(packageMetadata.version);
|
|
385
|
-
if (command === "install") return installCommand({ paths, platform, cliPath, output, serviceProbe, fetchImpl, nodeVersion });
|
|
381
|
+
if (command === "install") return installCommand({ paths, platform, cliPath, output, serviceProbe, fetchImpl, confirmRestart, nodeVersion });
|
|
386
382
|
if (command === "open" || command === "gui") return openCommand({ paths, platform, serviceProbe, fetchImpl });
|
|
387
383
|
if (command === "status") return statusCommand({ paths, platform, jsonOutput: args.includes("--json"), output, fetchImpl });
|
|
388
384
|
if (command === "restart") return restartCommand({ paths, output, fetchImpl });
|
|
@@ -142,7 +142,7 @@ export class RuntimeManager {
|
|
|
142
142
|
return next;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
async startInternal() {
|
|
145
|
+
async startInternal({ launchCodex = false } = {}) {
|
|
146
146
|
if (this.child && !hasExited(this.child)) return this.status();
|
|
147
147
|
this.clearChildState();
|
|
148
148
|
this.stopping = false;
|
|
@@ -169,6 +169,7 @@ export class RuntimeManager {
|
|
|
169
169
|
MINECODEX_ENABLED_FEATURES: features.join(","),
|
|
170
170
|
MINECODEX_CODEX_PID_FILE: this.paths.codexPidPath,
|
|
171
171
|
MINECODEX_RUNTIME_READY_FILE: this.paths.runtimeReadyPath,
|
|
172
|
+
MINECODEX_LAUNCH_CODEX: launchCodex ? "1" : "0",
|
|
172
173
|
...(codexExecutable ? { MINECODEX_CODEX_EXECUTABLE: codexExecutable } : {}),
|
|
173
174
|
},
|
|
174
175
|
stdio: "inherit",
|
|
@@ -307,7 +308,8 @@ export class RuntimeManager {
|
|
|
307
308
|
const operation = this.enqueue(async () => {
|
|
308
309
|
await this.stopInternal();
|
|
309
310
|
await this.platform.terminateOwnedCodex(this.paths);
|
|
310
|
-
|
|
311
|
+
await this.platform.terminateNativeCodex?.();
|
|
312
|
+
return this.startInternal({ launchCodex: true });
|
|
311
313
|
});
|
|
312
314
|
this.restartInFlight = operation;
|
|
313
315
|
const shared = this.restartInFlight.finally(() => {
|
|
@@ -141,6 +141,9 @@ Host 会再次发送当前状态。功能页面可据此暂停隐藏状态下的
|
|
|
141
141
|
|
|
142
142
|
- 只在 top frame 安装 Runtime,绝不向业务 iframe 注入 binding token。
|
|
143
143
|
- Watch / refresh 串行;已连接 Renderer 不重复连接,关闭 target 会清理状态。
|
|
144
|
+
- 本地 Surface 注入前先启用 CSP bypass 并注册 document-start 脚本;缺少初始化标记的
|
|
145
|
+
首次或旧版 document 只受控 reload 一次。标记存在时,新的 RuntimeHost 会话直接
|
|
146
|
+
替换旧 Runtime,不刷新当前 Codex 页面。
|
|
144
147
|
- 入口由 Renderer 内的 MutationObserver 幂等挂载,React 重绘不会产生重复入口。
|
|
145
148
|
- Summary 根据对话主区域宽度连续派生 `overlay / shift / gutter`:小于 1096px
|
|
146
149
|
使用临时 Popover;1096–1535px 预留 316px 并把对话内容左移 158px;更宽时
|
|
@@ -5,6 +5,8 @@ import path from "node:path";
|
|
|
5
5
|
|
|
6
6
|
const RUNTIME_VERSION = 99;
|
|
7
7
|
const HOST_BINDING_NAME = "__codexPersonalHostAction";
|
|
8
|
+
const CSP_BOOTSTRAP_VERSION = 1;
|
|
9
|
+
const CSP_BOOTSTRAP_KEY = "__mineCodexCspBootstrapVersion";
|
|
8
10
|
|
|
9
11
|
export const RESPONSIVE_SUMMARY_LAYOUT = Object.freeze({
|
|
10
12
|
contentBaseWidth: 736,
|
|
@@ -3564,6 +3566,14 @@ export function createRuntimeReadyContract({
|
|
|
3564
3566
|
};
|
|
3565
3567
|
}
|
|
3566
3568
|
|
|
3569
|
+
function createDocumentBootstrapSource(source) {
|
|
3570
|
+
return `window[${JSON.stringify(CSP_BOOTSTRAP_KEY)}] = ${CSP_BOOTSTRAP_VERSION};\n${source}`;
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3573
|
+
function documentBootstrapExpression() {
|
|
3574
|
+
return `window[${JSON.stringify(CSP_BOOTSTRAP_KEY)}] === ${CSP_BOOTSTRAP_VERSION}`;
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3567
3577
|
async function connect(url) {
|
|
3568
3578
|
const socket = new WebSocket(url);
|
|
3569
3579
|
const pending = new Map();
|
|
@@ -3728,8 +3738,6 @@ export class CodexRuntime {
|
|
|
3728
3738
|
sleep = (timeoutMs) => new Promise((resolve) => setTimeout(resolve, timeoutMs)),
|
|
3729
3739
|
availabilityTimeoutMs = 20_000,
|
|
3730
3740
|
availabilityPollMs = 500,
|
|
3731
|
-
relaunchBaseDelayMs = 250,
|
|
3732
|
-
relaunchMaxDelayMs = 2_000,
|
|
3733
3741
|
monitorIntervalMs = 2_000,
|
|
3734
3742
|
runtimeSessionId = randomBytes(16).toString("hex"),
|
|
3735
3743
|
onStatusChange = null,
|
|
@@ -3746,8 +3754,6 @@ export class CodexRuntime {
|
|
|
3746
3754
|
this.sleep = sleep;
|
|
3747
3755
|
this.availabilityTimeoutMs = availabilityTimeoutMs;
|
|
3748
3756
|
this.availabilityPollMs = availabilityPollMs;
|
|
3749
|
-
this.relaunchBaseDelayMs = relaunchBaseDelayMs;
|
|
3750
|
-
this.relaunchMaxDelayMs = relaunchMaxDelayMs;
|
|
3751
3757
|
this.monitorIntervalMs = monitorIntervalMs;
|
|
3752
3758
|
this.runtimeSessionId = runtimeSessionId;
|
|
3753
3759
|
this.onStatusChange = onStatusChange;
|
|
@@ -3764,7 +3770,6 @@ export class CodexRuntime {
|
|
|
3764
3770
|
this.targetRefreshQueued = false;
|
|
3765
3771
|
this.appPid = null;
|
|
3766
3772
|
this.managedCodexChild = null;
|
|
3767
|
-
this.recoveryPromise = null;
|
|
3768
3773
|
this.monitorTimer = null;
|
|
3769
3774
|
this.monitorWake = null;
|
|
3770
3775
|
this.stopPromise = null;
|
|
@@ -3772,20 +3777,35 @@ export class CodexRuntime {
|
|
|
3772
3777
|
this.pidChangeQueue = null;
|
|
3773
3778
|
this.rendererDiscovered = false;
|
|
3774
3779
|
this.lastRendererStatus = null;
|
|
3775
|
-
this.codexLifecycleManaged = false;
|
|
3776
3780
|
}
|
|
3777
3781
|
|
|
3778
|
-
async start() {
|
|
3782
|
+
async start({ launchCodex = false } = {}) {
|
|
3779
3783
|
this.stopping = false;
|
|
3780
3784
|
await mkdir(this.profileDir, { recursive: true });
|
|
3781
|
-
|
|
3782
|
-
if (!
|
|
3783
|
-
this.launchManagedCodex();
|
|
3784
|
-
|
|
3785
|
+
const available = await this.isAvailable();
|
|
3786
|
+
if (!available && launchCodex) {
|
|
3787
|
+
const child = this.launchManagedCodex();
|
|
3788
|
+
try {
|
|
3789
|
+
await this.waitUntilAvailable();
|
|
3790
|
+
} catch (error) {
|
|
3791
|
+
if (this.managedCodexChild === child && !processChildHasExited(child)) {
|
|
3792
|
+
child.kill?.("SIGTERM");
|
|
3793
|
+
const exited = await waitForProcessChildExit(child, 500);
|
|
3794
|
+
if (!exited && !processChildHasExited(child)) {
|
|
3795
|
+
child.kill?.("SIGKILL");
|
|
3796
|
+
await waitForProcessChildExit(child, 500);
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
throw error;
|
|
3800
|
+
}
|
|
3785
3801
|
}
|
|
3786
3802
|
|
|
3787
|
-
|
|
3788
|
-
|
|
3803
|
+
if (available || launchCodex) {
|
|
3804
|
+
await this.startTargetDiscovery();
|
|
3805
|
+
await this.refresh();
|
|
3806
|
+
} else {
|
|
3807
|
+
this.notifyStatusChange();
|
|
3808
|
+
}
|
|
3789
3809
|
await this.flushManagedCodexPidChange();
|
|
3790
3810
|
this.monitorPromise = this.monitor();
|
|
3791
3811
|
}
|
|
@@ -3859,14 +3879,10 @@ export class CodexRuntime {
|
|
|
3859
3879
|
|
|
3860
3880
|
markCodexUnavailable(error) {
|
|
3861
3881
|
this.clearRendererState();
|
|
3862
|
-
if (this.codexLifecycleManaged && !this.stopping) {
|
|
3863
|
-
this.scheduleCodexRecovery();
|
|
3864
|
-
}
|
|
3865
3882
|
return error;
|
|
3866
3883
|
}
|
|
3867
3884
|
|
|
3868
3885
|
launchManagedCodex() {
|
|
3869
|
-
this.codexLifecycleManaged = true;
|
|
3870
3886
|
const child = this.spawnProcess(
|
|
3871
3887
|
this.appPath,
|
|
3872
3888
|
[
|
|
@@ -3891,7 +3907,6 @@ export class CodexRuntime {
|
|
|
3891
3907
|
this.notifyManagedCodexPidChange(null);
|
|
3892
3908
|
if (this.stopping) return;
|
|
3893
3909
|
this.clearRendererState();
|
|
3894
|
-
this.scheduleCodexRecovery();
|
|
3895
3910
|
};
|
|
3896
3911
|
child.once?.("exit", onExit);
|
|
3897
3912
|
child.once?.("error", (error) => {
|
|
@@ -3902,73 +3917,6 @@ export class CodexRuntime {
|
|
|
3902
3917
|
return child;
|
|
3903
3918
|
}
|
|
3904
3919
|
|
|
3905
|
-
scheduleCodexRecovery() {
|
|
3906
|
-
if (this.stopping || this.recoveryPromise) return this.recoveryPromise;
|
|
3907
|
-
const recovery = this.recoverManagedCodex();
|
|
3908
|
-
let trackedRecovery;
|
|
3909
|
-
trackedRecovery = recovery.finally(() => {
|
|
3910
|
-
if (this.recoveryPromise === trackedRecovery) this.recoveryPromise = null;
|
|
3911
|
-
});
|
|
3912
|
-
this.recoveryPromise = trackedRecovery;
|
|
3913
|
-
return trackedRecovery;
|
|
3914
|
-
}
|
|
3915
|
-
|
|
3916
|
-
async recoverManagedCodex() {
|
|
3917
|
-
let attempt = 0;
|
|
3918
|
-
while (!this.stopping) {
|
|
3919
|
-
if (this.managedCodexChild && !processChildHasExited(this.managedCodexChild)) {
|
|
3920
|
-
await this.sleep(Math.max(1, Math.min(this.relaunchMaxDelayMs, this.relaunchBaseDelayMs || 1)));
|
|
3921
|
-
continue;
|
|
3922
|
-
}
|
|
3923
|
-
if (await this.isAvailable()) {
|
|
3924
|
-
try {
|
|
3925
|
-
await this.startTargetDiscovery();
|
|
3926
|
-
await this.refresh();
|
|
3927
|
-
await this.flushManagedCodexPidChange();
|
|
3928
|
-
return;
|
|
3929
|
-
} catch (error) {
|
|
3930
|
-
if (this.stopping) return;
|
|
3931
|
-
if (await this.isAvailable()) {
|
|
3932
|
-
this.logger.warn?.("Managed Codex CDP is still available; retrying discovery", error.message);
|
|
3933
|
-
await this.sleep(Math.max(1, Math.min(this.relaunchMaxDelayMs, this.relaunchBaseDelayMs || 1)));
|
|
3934
|
-
continue;
|
|
3935
|
-
}
|
|
3936
|
-
}
|
|
3937
|
-
}
|
|
3938
|
-
const delay = Math.min(
|
|
3939
|
-
this.relaunchBaseDelayMs * (2 ** attempt),
|
|
3940
|
-
this.relaunchMaxDelayMs,
|
|
3941
|
-
);
|
|
3942
|
-
if (delay > 0) await this.sleep(delay);
|
|
3943
|
-
if (this.stopping) return;
|
|
3944
|
-
let child;
|
|
3945
|
-
try {
|
|
3946
|
-
child = this.launchManagedCodex();
|
|
3947
|
-
await this.waitUntilAvailable();
|
|
3948
|
-
await this.startTargetDiscovery();
|
|
3949
|
-
await this.refresh();
|
|
3950
|
-
await this.flushManagedCodexPidChange();
|
|
3951
|
-
return;
|
|
3952
|
-
} catch (error) {
|
|
3953
|
-
this.logger.warn?.("Managed Codex recovery retry failed", error.message);
|
|
3954
|
-
if (
|
|
3955
|
-
child
|
|
3956
|
-
&& this.managedCodexChild === child
|
|
3957
|
-
&& child.exitCode === null
|
|
3958
|
-
&& child.signalCode === null
|
|
3959
|
-
) {
|
|
3960
|
-
child.kill?.("SIGTERM");
|
|
3961
|
-
const exited = await waitForProcessChildExit(child, 500);
|
|
3962
|
-
if (!exited && !processChildHasExited(child)) {
|
|
3963
|
-
child.kill?.("SIGKILL");
|
|
3964
|
-
await waitForProcessChildExit(child, 500);
|
|
3965
|
-
}
|
|
3966
|
-
}
|
|
3967
|
-
attempt += 1;
|
|
3968
|
-
}
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
3971
|
-
|
|
3972
3920
|
queueTargetRefresh() {
|
|
3973
3921
|
if (this.targetRefreshQueued || this.stopping) return;
|
|
3974
3922
|
this.targetRefreshQueued = true;
|
|
@@ -4250,7 +4198,20 @@ export class CodexRuntime {
|
|
|
4250
4198
|
bindingToken,
|
|
4251
4199
|
runtimeSessionId: this.runtimeSessionId,
|
|
4252
4200
|
});
|
|
4253
|
-
const
|
|
4201
|
+
const bootstrapExpression = documentBootstrapExpression();
|
|
4202
|
+
const script = await client.send("Page.addScriptToEvaluateOnNewDocument", {
|
|
4203
|
+
source: createDocumentBootstrapSource(source),
|
|
4204
|
+
});
|
|
4205
|
+
const documentWasBootstrapped = Boolean(evaluationValue(await client.send("Runtime.evaluate", {
|
|
4206
|
+
expression: bootstrapExpression,
|
|
4207
|
+
returnByValue: true,
|
|
4208
|
+
})));
|
|
4209
|
+
if (!documentWasBootstrapped) {
|
|
4210
|
+
const pageLoaded = client.waitFor("Page.loadEventFired", 20_000);
|
|
4211
|
+
await client.send("Page.reload");
|
|
4212
|
+
await pageLoaded;
|
|
4213
|
+
await waitForExpression(client, bootstrapExpression);
|
|
4214
|
+
}
|
|
4254
4215
|
await waitForExpression(
|
|
4255
4216
|
client,
|
|
4256
4217
|
`document.readyState === "interactive" || document.readyState === "complete"`,
|
|
@@ -4280,7 +4241,6 @@ export class CodexRuntime {
|
|
|
4280
4241
|
this.stopping = true;
|
|
4281
4242
|
this.targetRefreshQueued = false;
|
|
4282
4243
|
this.monitorWake?.();
|
|
4283
|
-
await this.recoveryPromise?.catch(() => {});
|
|
4284
4244
|
await this.monitorPromise?.catch(() => {});
|
|
4285
4245
|
await Promise.all(Array.from(this.clients, async ([targetId, client]) => {
|
|
4286
4246
|
const identifier = this.scriptIds.get(targetId);
|
|
@@ -17,6 +17,7 @@ const featuresRoot = process.env.CODEX_FEATURES_ROOT ?? path.dirname(runtimeRoot
|
|
|
17
17
|
const legacyDataDir = process.env.CODEX_IMAGE_HOST_DATA_DIR ?? path.join(os.homedir(), ".codex-image-host");
|
|
18
18
|
const profileDir = process.env.CODEX_RUNTIME_PROFILE_DIR ?? path.join(legacyDataDir, "codex-profile");
|
|
19
19
|
const cdpPort = Number(process.env.CODEX_RUNTIME_CDP_PORT ?? 9231);
|
|
20
|
+
const launchCodex = process.env.MINECODEX_LAUNCH_CODEX === "1";
|
|
20
21
|
|
|
21
22
|
const discoveredFeatures = await discoverFeatures(featuresRoot);
|
|
22
23
|
if (!discoveredFeatures.length) throw new Error(`No codex-feature.json files found under ${featuresRoot}`);
|
|
@@ -70,7 +71,7 @@ runtime = new CodexRuntime({
|
|
|
70
71
|
});
|
|
71
72
|
|
|
72
73
|
try {
|
|
73
|
-
await runtime.start();
|
|
74
|
+
await runtime.start({ launchCodex });
|
|
74
75
|
runtimeReady = true;
|
|
75
76
|
await writeReadyFile();
|
|
76
77
|
} catch (error) {
|