minecodex 1.0.82 → 1.1.0

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.
@@ -0,0 +1,153 @@
1
+ // CDP (Chrome DevTools Protocol) 客户端:WebSocket 连接、表达式求值与进程等待。
2
+ // 与注入内容无关;CodexRuntime 通过这里的 connectClient 与 Codex Renderer 通信。
3
+ export async function connect(url) {
4
+ const socket = new WebSocket(url);
5
+ const pending = new Map();
6
+ const eventWaiters = new Map();
7
+ const eventListeners = new Map();
8
+ let nextId = 1;
9
+
10
+ await new Promise((resolve, reject) => {
11
+ socket.addEventListener("open", resolve, { once: true });
12
+ socket.addEventListener("error", reject, { once: true });
13
+ });
14
+ socket.addEventListener("message", (event) => {
15
+ const message = JSON.parse(String(event.data));
16
+ if (!message.id) {
17
+ for (const listener of eventListeners.get(message.method) ?? []) listener(message.params);
18
+ const waiters = eventWaiters.get(message.method) ?? [];
19
+ eventWaiters.delete(message.method);
20
+ for (const waiter of waiters) waiter.resolve(message.params);
21
+ return;
22
+ }
23
+ const request = pending.get(message.id);
24
+ if (!request) return;
25
+ pending.delete(message.id);
26
+ if (message.error) request.reject(new Error(message.error.message));
27
+ else request.resolve(message.result);
28
+ });
29
+ socket.addEventListener("close", () => {
30
+ const error = new Error("CDP WebSocket closed");
31
+ for (const request of pending.values()) request.reject(error);
32
+ pending.clear();
33
+ for (const waiters of eventWaiters.values()) {
34
+ for (const waiter of waiters) waiter.reject(error);
35
+ }
36
+ eventWaiters.clear();
37
+ eventListeners.clear();
38
+ });
39
+
40
+ return {
41
+ socket,
42
+ send(method, params = {}) {
43
+ const id = nextId++;
44
+ return new Promise((resolve, reject) => {
45
+ pending.set(id, { resolve, reject });
46
+ socket.send(JSON.stringify({ id, method, params }));
47
+ });
48
+ },
49
+ on(method, listener) {
50
+ const listeners = eventListeners.get(method) ?? new Set();
51
+ listeners.add(listener);
52
+ eventListeners.set(method, listeners);
53
+ return () => listeners.delete(listener);
54
+ },
55
+ waitFor(method, timeoutMs) {
56
+ return new Promise((resolve, reject) => {
57
+ const waiters = eventWaiters.get(method) ?? [];
58
+ const waiter = {
59
+ resolve(value) {
60
+ clearTimeout(timeout);
61
+ resolve(value);
62
+ },
63
+ reject,
64
+ };
65
+ const timeout = setTimeout(() => {
66
+ const remaining = (eventWaiters.get(method) ?? []).filter((item) => item !== waiter);
67
+ if (remaining.length) eventWaiters.set(method, remaining);
68
+ else eventWaiters.delete(method);
69
+ reject(new Error(`Timed out waiting for CDP event ${method}`));
70
+ }, timeoutMs);
71
+ waiters.push(waiter);
72
+ eventWaiters.set(method, waiters);
73
+ });
74
+ },
75
+ };
76
+ }
77
+
78
+ export async function waitForExpression(client, expression, timeoutMs = 20_000) {
79
+ const deadline = Date.now() + timeoutMs;
80
+ while (Date.now() < deadline) {
81
+ try {
82
+ const result = await client.send("Runtime.evaluate", { expression, returnByValue: true });
83
+ if (result.result?.value) return;
84
+ } catch {
85
+ // Reloading briefly destroys the previous execution context.
86
+ }
87
+ await new Promise((resolve) => setTimeout(resolve, 250));
88
+ }
89
+ throw new Error(`Timed out waiting for renderer state: ${expression}`);
90
+ }
91
+
92
+ export function evaluationValue(result) {
93
+ if (result.exceptionDetails) {
94
+ throw new Error(result.exceptionDetails.exception?.description ?? result.exceptionDetails.text);
95
+ }
96
+ return result.result?.value;
97
+ }
98
+
99
+ export function requireCdpWebSocketUrl(version, expectedCdpPort) {
100
+ const value = typeof version?.webSocketDebuggerUrl === "string"
101
+ ? version.webSocketDebuggerUrl.trim()
102
+ : "";
103
+ if (!value) {
104
+ throw new Error("Codex CDP /json/version is missing webSocketDebuggerUrl");
105
+ }
106
+ let parsed;
107
+ try {
108
+ parsed = new URL(value);
109
+ } catch {
110
+ throw new Error("Codex CDP webSocketDebuggerUrl is invalid");
111
+ }
112
+ if (!new Set(["ws:", "wss:"]).has(parsed.protocol) || !parsed.host) {
113
+ throw new Error("Codex CDP webSocketDebuggerUrl must use ws: or wss:");
114
+ }
115
+ const hostname = parsed.hostname.toLowerCase().replace(/^\[|\]$/g, "");
116
+ if (!new Set(["127.0.0.1", "localhost", "::1"]).has(hostname)) {
117
+ throw new Error("Codex CDP webSocketDebuggerUrl must use a loopback host");
118
+ }
119
+ const configuredPort = Number(expectedCdpPort);
120
+ const effectivePort = parsed.port
121
+ ? Number(parsed.port)
122
+ : parsed.protocol === "wss:" ? 443 : 80;
123
+ if (!Number.isInteger(configuredPort) || effectivePort !== configuredPort) {
124
+ throw new Error(`Codex CDP webSocketDebuggerUrl must use configured CDP port ${expectedCdpPort}`);
125
+ }
126
+ return value;
127
+ }
128
+
129
+ export function processChildHasExited(child) {
130
+ return child && (
131
+ (child.exitCode !== null && child.exitCode !== undefined)
132
+ || (child.signalCode !== null && child.signalCode !== undefined)
133
+ );
134
+ }
135
+
136
+ export function waitForProcessChildExit(child, timeoutMs) {
137
+ if (processChildHasExited(child)) return Promise.resolve(true);
138
+ return new Promise((resolve) => {
139
+ let settled = false;
140
+ let timer;
141
+ const finish = (exited) => {
142
+ if (settled) return;
143
+ settled = true;
144
+ clearTimeout(timer);
145
+ child.removeListener?.("exit", onExit);
146
+ resolve(exited || processChildHasExited(child));
147
+ };
148
+ const onExit = () => finish(true);
149
+ child.once?.("exit", onExit);
150
+ timer = setTimeout(() => finish(false), timeoutMs);
151
+ });
152
+ }
153
+
@@ -0,0 +1,116 @@
1
+ // Codex 原生设计契约:token 名单与派生规则的单一事实来源。
2
+ // 注入到 Codex Renderer 的 install 脚本必须自包含(install.toString() 序列化
3
+ // 后经 CDP 注入),因此 install 内通过 config.themeTokens 拿到本模块的名单;
4
+ // 名单只在这里维护,install 不再持有自己的字面量副本。
5
+
6
+ export const CODEX_THEME_TOKENS = Object.freeze([
7
+ "--codex-base-accent",
8
+ "--codex-base-ink",
9
+ "--codex-base-surface",
10
+ "--codex-corner-radius-scale",
11
+ "--codex-corner-shape",
12
+ "--spacing",
13
+ "--radius-3xl-base",
14
+ "--elevation-prominent",
15
+ "--elevation-stroke",
16
+ "--color-token-main-surface-primary",
17
+ "--color-token-dropdown-background",
18
+ "--color-token-foreground",
19
+ "--color-token-text-secondary",
20
+ "--color-token-text-tertiary",
21
+ "--color-token-border",
22
+ "--color-token-focus-border",
23
+ "--color-token-conversation-summary-trailing",
24
+ "--color-token-bg-primary",
25
+ "--color-token-bg-secondary",
26
+ "--color-token-bg-tertiary",
27
+ "--color-surface-tertiary",
28
+ "--color-surface-elevated-secondary",
29
+ "--color-background-elevated-primary-opaque",
30
+ "--color-background-primary-ghost-hover",
31
+ "--color-chart-blue",
32
+ "--color-chart-purple",
33
+ "--color-background-panel",
34
+ "--color-background-control",
35
+ "--color-background-primary-solid",
36
+ "--color-text-primary-solid",
37
+ "--color-background-button-primary",
38
+ "--color-background-button-primary-hover",
39
+ "--color-background-button-secondary",
40
+ "--color-background-button-secondary-hover",
41
+ "--color-background-button-tertiary-hover",
42
+ "--color-text-button-tertiary",
43
+ "--color-token-button-background",
44
+ "--color-token-button-foreground",
45
+ "--color-text-foreground",
46
+ "--color-text-foreground-secondary",
47
+ "--color-text-foreground-tertiary",
48
+ "--color-text-inverse",
49
+ "--color-text-on-accent",
50
+ "--color-text-button-primary",
51
+ "--color-text-accent",
52
+ "--color-token-text-link-foreground",
53
+ "--color-border",
54
+ "--color-border-subtle",
55
+ "--color-border-heavy",
56
+ "--color-border-focus",
57
+ "--color-token-scrollbar-slider-background",
58
+ "--color-token-scrollbar-slider-hover-background",
59
+ "--color-token-scrollbar-slider-active-background",
60
+ "--color-icon-primary",
61
+ "--color-icon-secondary",
62
+ "--color-icon-tertiary",
63
+ "--color-token-list-hover-background",
64
+ "--color-token-input-background",
65
+ "--color-token-input-border",
66
+ "--color-token-input-foreground",
67
+ "--color-token-input-placeholder-foreground",
68
+ "--font-sans-default",
69
+ "--font-weight-normal",
70
+ "--vscode-font-weight",
71
+ "--default-mono-font-family",
72
+ "--text-heading-lg",
73
+ "--text-heading-md",
74
+ "--text-base",
75
+ "--text-base--line-height",
76
+ "--text-sm",
77
+ "--text-sm--line-height",
78
+ "--text-xs",
79
+ "--spacing-token-button-composer",
80
+ "--radius-sm",
81
+ "--radius-md",
82
+ "--radius-lg",
83
+ "--radius-xl",
84
+ "--radius-2xl",
85
+ "--shadow-md",
86
+ "--shadow-lg",
87
+ "--shadow-xl",
88
+ ]);
89
+
90
+ // images 图片编辑器可用性依赖的宿主 token 子集。
91
+ // 与 features/images/web/app.js 的 editorTokens 保持同一来源(一致性由测试锁定)。
92
+ export const EDITOR_TOKEN_NAMES = Object.freeze([
93
+ "--color-surface-tertiary",
94
+ "--color-surface-elevated-secondary",
95
+ "--color-border-subtle",
96
+ "--color-background-primary-ghost-hover",
97
+ "--color-chart-blue",
98
+ "--color-text-inverse",
99
+ "--color-token-main-surface-primary",
100
+ "--color-token-input-foreground",
101
+ "--color-text-foreground",
102
+ "--color-text-foreground-tertiary",
103
+ "--color-text-on-accent",
104
+ "--color-border",
105
+ "--color-border-focus",
106
+ "--color-token-list-hover-background",
107
+ "--color-text-accent",
108
+ "--font-sans-default",
109
+ "--font-weight-normal",
110
+ "--text-sm",
111
+ "--text-sm--line-height",
112
+ "--radius-md",
113
+ "--radius-xl",
114
+ "--shadow-md",
115
+ "--shadow-xl",
116
+ ]);