@walkhi/code-relax 0.1.0-beta.30 → 0.1.0-beta.31

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.
Files changed (185) hide show
  1. package/README.md +95 -89
  2. package/dist/bin/cleanup-accounts.mjs +63 -63
  3. package/dist/bin/codex-remote.mjs +374 -367
  4. package/dist/bin/manage-relay-admin.mjs +20 -20
  5. package/dist/bin/migrate-relay-db.mjs +109 -109
  6. package/dist/bin/publish-android-release.mjs +62 -58
  7. package/dist/bin/self-relay-admin.mjs +8 -8
  8. package/dist/bin/self-relay-demo.mjs +33 -33
  9. package/dist/bin/self-relay-server.mjs +66 -66
  10. package/dist/build-platform.json +1 -1
  11. package/dist/get-lan-networks.ps1 +40 -40
  12. package/dist/get-shared-service-url.ps1 +6 -6
  13. package/dist/shared/app-server-events.cjs +422 -412
  14. package/dist/shared/code-browser.cjs +159 -159
  15. package/dist/shared/file-changes.cjs +31 -31
  16. package/dist/shared/file-citations.cjs +117 -117
  17. package/dist/shared/file-mentions.cjs +92 -92
  18. package/dist/shared/message-delivery.cjs +54 -54
  19. package/dist/shared/p2p-data-channel.cjs +72 -72
  20. package/dist/shared/p2p-diagnostics.cjs +28 -28
  21. package/dist/shared/p2p-file-channel.cjs +74 -74
  22. package/dist/shared/plan-pricing.cjs +32 -32
  23. package/dist/shared/question-attention.cjs +40 -0
  24. package/dist/shared/server-requests.cjs +219 -219
  25. package/dist/shared/timeline-sync.cjs +173 -173
  26. package/dist/skills/code-relax/SKILL.md +22 -22
  27. package/dist/src/archived-threads.mjs +22 -0
  28. package/dist/src/background-process.mjs +18 -18
  29. package/dist/src/bounded-process-log.mjs +63 -63
  30. package/dist/src/celsius-client.mjs +107 -107
  31. package/dist/src/code-browser.mjs +100 -100
  32. package/dist/src/desktop-launcher.mjs +46 -46
  33. package/dist/src/dots-approval-timeline.mjs +99 -99
  34. package/dist/src/dots-avatar.mjs +42 -42
  35. package/dist/src/dots-chat-projection.mjs +50 -50
  36. package/dist/src/dots-chat-server.mjs +461 -461
  37. package/dist/src/dots-message-images.mjs +319 -319
  38. package/dist/src/dots-profile.mjs +77 -77
  39. package/dist/src/dots-room-activity.mjs +94 -94
  40. package/dist/src/dots-room-client.mjs +135 -135
  41. package/dist/src/dots-room-projection.mjs +94 -94
  42. package/dist/src/dots-schedules.mjs +66 -66
  43. package/dist/src/dots-summary-tasks.mjs +32 -32
  44. package/dist/src/dots-task-catalog.mjs +430 -430
  45. package/dist/src/dots-task-files.mjs +52 -52
  46. package/dist/src/execution-health.mjs +26 -26
  47. package/dist/src/file-transfer-range.mjs +22 -22
  48. package/dist/src/file-upload-session.mjs +130 -130
  49. package/dist/src/hosted-history-app-server.mjs +103 -103
  50. package/dist/src/hosted-history-cache.mjs +113 -113
  51. package/dist/src/lan-socket-server.mjs +265 -263
  52. package/dist/src/managed-app-server.mjs +244 -244
  53. package/dist/src/message-images.mjs +9 -9
  54. package/dist/src/message-upload-files.mjs +74 -74
  55. package/dist/src/onboarding.mjs +12 -12
  56. package/dist/src/plan-pricing.mjs +47 -47
  57. package/dist/src/platform/README.md +12 -12
  58. package/dist/src/platform/codex-protocol.mjs +46 -46
  59. package/dist/src/platform/codex-runtime.mjs +3 -3
  60. package/dist/src/platform/credential-store.mjs +3 -3
  61. package/dist/src/platform/desktop-host.mjs +3 -3
  62. package/dist/src/platform/desktop-project-registration.mjs +3 -3
  63. package/dist/src/platform/desktop-project-store.mjs +208 -149
  64. package/dist/src/platform/file-opener.mjs +9 -0
  65. package/dist/src/platform/linux/README.md +3 -3
  66. package/dist/src/platform/linux/codex-runtime.mjs +56 -0
  67. package/dist/src/platform/linux/credential-store.mjs +28 -0
  68. package/dist/src/platform/linux/desktop-host.mjs +83 -0
  69. package/dist/src/platform/linux/desktop-installation.mjs +36 -0
  70. package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
  71. package/dist/src/platform/linux/network-info.mjs +11 -0
  72. package/dist/src/platform/linux/process.mjs +42 -0
  73. package/dist/src/platform/linux/project-locations.mjs +17 -0
  74. package/dist/src/platform/linux/resident-startup.mjs +52 -0
  75. package/dist/src/platform/linux/retained-runtime.mjs +76 -0
  76. package/dist/src/platform/linux/service-host.mjs +18 -0
  77. package/dist/src/platform/linux/system.mjs +49 -0
  78. package/dist/src/platform/linux/user-environment.mjs +7 -0
  79. package/dist/src/platform/macos/README.md +3 -3
  80. package/dist/src/platform/macos/codex-runtime.mjs +125 -125
  81. package/dist/src/platform/macos/credential-store.mjs +10 -10
  82. package/dist/src/platform/macos/desktop-host.mjs +82 -82
  83. package/dist/src/platform/macos/desktop-project-registration.mjs +11 -10
  84. package/dist/src/platform/macos/network-info.mjs +8 -28
  85. package/dist/src/platform/macos/project-locations.mjs +8 -8
  86. package/dist/src/platform/macos/resident-startup.mjs +84 -84
  87. package/dist/src/platform/macos/retained-runtime.mjs +127 -127
  88. package/dist/src/platform/macos/service-host.mjs +18 -18
  89. package/dist/src/platform/macos/system.mjs +29 -29
  90. package/dist/src/platform/macos/user-environment.mjs +7 -7
  91. package/dist/src/platform/native-package.mjs +58 -46
  92. package/dist/src/platform/network-info.mjs +3 -3
  93. package/dist/src/platform/path-identity.mjs +5 -5
  94. package/dist/src/platform/posix/network-candidates.mjs +23 -0
  95. package/dist/src/platform/posix/runtime-paths.mjs +25 -25
  96. package/dist/src/platform/project-locations.mjs +3 -3
  97. package/dist/src/platform/runtime-paths.mjs +8 -8
  98. package/dist/src/platform/select.mjs +5 -5
  99. package/dist/src/platform/service-host.mjs +3 -3
  100. package/dist/src/platform/user-environment.mjs +3 -3
  101. package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
  102. package/dist/src/platform/windows/activate-desktop.ps1 +48 -48
  103. package/dist/src/platform/windows/background-process.mjs +30 -30
  104. package/dist/src/platform/windows/codex-runtime.mjs +215 -215
  105. package/dist/src/platform/windows/credential-store.mjs +27 -27
  106. package/dist/src/platform/windows/credential-store.ps1 +49 -49
  107. package/dist/src/platform/windows/desktop-helper.mjs +179 -179
  108. package/dist/src/platform/windows/desktop-host.mjs +58 -58
  109. package/dist/src/platform/windows/desktop-info.ps1 +14 -14
  110. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
  111. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
  112. package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -21
  113. package/dist/src/platform/windows/dots-profile.mjs +49 -49
  114. package/dist/src/platform/windows/launch-worker.mjs +41 -41
  115. package/dist/src/platform/windows/network-info.mjs +34 -34
  116. package/dist/src/platform/windows/project-locations.mjs +16 -16
  117. package/dist/src/platform/windows/resident-start.js +20 -20
  118. package/dist/src/platform/windows/resident-start.ps1 +7 -7
  119. package/dist/src/platform/windows/resident-startup.ps1 +83 -83
  120. package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
  121. package/dist/src/platform/windows/runtime-paths.mjs +20 -20
  122. package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
  123. package/dist/src/platform/windows/service-host.mjs +31 -31
  124. package/dist/src/platform/windows/start-hidden-console.ps1 +49 -49
  125. package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
  126. package/dist/src/platform/windows/user-environment.mjs +50 -50
  127. package/dist/src/platform/windows/user-environment.ps1 +41 -41
  128. package/dist/src/project-archive.mjs +30 -0
  129. package/dist/src/project-create.mjs +121 -119
  130. package/dist/src/project-management.mjs +59 -69
  131. package/dist/src/project-remove.mjs +55 -0
  132. package/dist/src/project-update.mjs +56 -0
  133. package/dist/src/runtime-discovery.mjs +47 -47
  134. package/dist/src/self-relay/account-cleanup.mjs +30 -30
  135. package/dist/src/self-relay/account-connections.mjs +52 -52
  136. package/dist/src/self-relay/admin-cloud.mjs +101 -101
  137. package/dist/src/self-relay/admin-page.mjs +80 -80
  138. package/dist/src/self-relay/admin-server.mjs +74 -74
  139. package/dist/src/self-relay/admin-state.mjs +127 -127
  140. package/dist/src/self-relay/admin-style.mjs +14 -14
  141. package/dist/src/self-relay/android-releases.mjs +61 -61
  142. package/dist/src/self-relay/client.mjs +61 -61
  143. package/dist/src/self-relay/connector.mjs +439 -438
  144. package/dist/src/self-relay/debug-allowlist.mjs +33 -33
  145. package/dist/src/self-relay/demo.mjs +37 -37
  146. package/dist/src/self-relay/device-info.mjs +27 -0
  147. package/dist/src/self-relay/device-store.mjs +92 -84
  148. package/dist/src/self-relay/diagnostics.mjs +54 -54
  149. package/dist/src/self-relay/email-accounts.mjs +183 -183
  150. package/dist/src/self-relay/file-wire.mjs +13 -13
  151. package/dist/src/self-relay/lan-authorizations.mjs +71 -42
  152. package/dist/src/self-relay/lifecycle.mjs +264 -250
  153. package/dist/src/self-relay/long-pairing.mjs +40 -40
  154. package/dist/src/self-relay/mysql-state.mjs +118 -105
  155. package/dist/src/self-relay/p2p-probe.mjs +159 -159
  156. package/dist/src/self-relay/privacy-page.mjs +26 -26
  157. package/dist/src/self-relay/public-releases.mjs +34 -0
  158. package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
  159. package/dist/src/self-relay/server.mjs +1006 -932
  160. package/dist/src/self-relay/usage-daily.mjs +55 -55
  161. package/dist/src/self-relay/usage-meter.mjs +141 -141
  162. package/dist/src/self-relay/windows-route-helper.mjs +75 -75
  163. package/dist/src/self-relay/wire.mjs +36 -36
  164. package/dist/src/server.mjs +1861 -1778
  165. package/dist/src/service-control.mjs +82 -82
  166. package/dist/src/service-doctor.mjs +110 -110
  167. package/dist/src/service-health.mjs +33 -33
  168. package/dist/src/service-install.mjs +91 -91
  169. package/dist/src/service-lifecycle.mjs +138 -138
  170. package/dist/src/service-restart.mjs +81 -81
  171. package/dist/src/shared-app-server.mjs +615 -611
  172. package/dist/src/shared-catalog.mjs +132 -132
  173. package/dist/src/shared-recovery.mjs +146 -146
  174. package/dist/src/shared-thread-stream.mjs +164 -164
  175. package/dist/src/skill-install.mjs +51 -51
  176. package/dist/src/thread-title-generation.mjs +150 -150
  177. package/dist/src/turn-model-history.mjs +47 -47
  178. package/dist/src/usage-reset.mjs +17 -17
  179. package/dist/src/workstation-downloads.mjs +120 -120
  180. package/dist/tools/rtc-route-helper.ps1 +99 -99
  181. package/dist/tools/start-rtc-route-helper.ps1 +15 -15
  182. package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
  183. package/dist/web/vendor/markdown-it.umd.min.js +13 -13
  184. package/package.json +7 -6
  185. package/tools/postinstall.mjs +127 -127
@@ -1,219 +1,219 @@
1
- (function (root, factory) {
2
- if (typeof module === 'object' && module.exports) module.exports = factory();
3
- else root.CodexServerRequests = factory();
4
- })(globalThis, function () {
5
- const command = 'item/commandExecution/requestApproval';
6
- const file = 'item/fileChange/requestApproval';
7
- const input = 'item/tool/requestUserInput';
8
- const elicitation = 'mcpServer/elicitation/request';
9
- const equal = (a, b) => JSON.stringify(a) === JSON.stringify(b);
10
- const approvalId = request => 'approval:' + (request.params.turnId || '') + ':' + typeof request.id + ':' + request.id;
11
- function confirmation(request) {
12
- const p = request.params || {}, value = p._meta?.['openai/confirmation'], schema = p.requestedSchema;
13
- if (request.method !== elicitation || p.mode !== 'form' || value?.version !== 1
14
- || typeof value.field !== 'string' || schema?.type !== 'object'
15
- || Object.keys(schema.properties || {}).length !== 1 || schema.properties[value.field]?.type !== 'boolean'
16
- || schema.required?.length !== 1 || schema.required[0] !== value.field) return null;
17
- return value;
18
- }
19
- function confirmationResult(request, decision) {
20
- return { action: decision, content: decision === 'accept' ? { [confirmation(request).field]: true } : null, _meta: null };
21
- }
22
- function browserAccess(request) {
23
- const p = request.params || {}, meta = p._meta || {};
24
- if (request.method !== elicitation || p.mode !== 'form' || meta.tool_name !== 'access_browser_origin'
25
- || meta.codex_approval_kind !== 'mcp_tool_call' || meta.codex_request_type !== 'approval_request'
26
- || meta.codex_sensitive_action !== true || p.requestedSchema?.type !== 'object'
27
- || Object.keys(p.requestedSchema.properties || {}).length) return null;
28
- try {
29
- const origin = new URL(meta.origin);
30
- if (!['http:', 'https:'].includes(origin.protocol) || origin.origin !== meta.origin) return null;
31
- return { origin: meta.origin, persistent: meta.persist === 'always' };
32
- } catch { return null; }
33
- }
34
- function browserAction(request) {
35
- const p = request.params || {}, meta = p._meta || {};
36
- if (browserAccess(request) || request.method !== elicitation || p.mode !== 'form'
37
- || p.requestedSchema?.type !== 'object' || Object.keys(p.requestedSchema.properties || {}).length
38
- || p.requestedSchema.required?.length || typeof p.message !== 'string' || !p.message.trim()) return null;
39
- const tool = meta.codex_approval_kind === 'mcp_tool_call' && meta.connector_id === 'browser-use';
40
- if (!tool && !['browser', 'browser-use'].includes(p.serverName)) return null;
41
- if (meta.codex_approval_kind && meta.codex_approval_kind !== 'mcp_tool_call') return null;
42
- const modes = meta.persist == null ? [] : Array.isArray(meta.persist) ? meta.persist : [meta.persist];
43
- if (modes.some(mode => !['session', 'always'].includes(mode))) return null;
44
- if (!tool && !modes.length) return null;
45
- try {
46
- const origin = new URL(meta.origin);
47
- if (!['http:', 'https:'].includes(origin.protocol) || origin.origin !== meta.origin) return null;
48
- return { origin: meta.origin, session: modes.includes('session'), persistent: modes.includes('always') };
49
- } catch { return null; }
50
- }
51
- function browserActionResult(decision) {
52
- return { action: decision === 'cancel' ? 'cancel' : 'accept', content: decision === 'cancel' ? null : {},
53
- _meta: decision === 'allowAlways' ? { persist: 'always' }
54
- : decision === 'allowSession' ? { persist: 'session' } : null };
55
- }
56
- function computerAppAccess(request) {
57
- const p = request.params || {}, meta = p._meta || {};
58
- if (request.method !== elicitation || p.mode !== 'form' || meta.codex_approval_kind !== 'mcp_tool_call'
59
- || (meta.codex_request_type != null && meta.codex_request_type !== 'approval_request')
60
- || typeof meta.connector_id !== 'string' || p.requestedSchema?.type !== 'object'
61
- || Object.keys(p.requestedSchema.properties || {}).length || p.requestedSchema.required?.length) return null;
62
- const connector = meta.connector_id.trim().toLowerCase().split(/[^a-z0-9]+/g).filter(Boolean).join('-');
63
- if (connector !== 'computer-use' && !connector.startsWith('computer-use-')) return null;
64
- const modes = Array.isArray(meta.persist) ? meta.persist : [meta.persist];
65
- if (!modes.length || modes.some(mode => !['session', 'always'].includes(mode))) return null;
66
- const display = Array.isArray(meta.tool_params_display) ? meta.tool_params_display.find(entry => entry?.name === 'app')?.value : null;
67
- const app = typeof display === 'string' && display.trim() ? display : meta.tool_params?.app;
68
- if (typeof app !== 'string' || !app.trim()) return null;
69
- return { app: app.trim(), session: modes.includes('session'), persistent: modes.includes('always'),
70
- subtitle: typeof meta.subtitle === 'string' ? meta.subtitle : '', highRisk: meta.riskLevel === 'high' };
71
- }
72
- function computerAppResult(request, decision) {
73
- const app = computerAppAccess(request);
74
- return { action: decision === 'decline' ? 'decline' : 'accept', content: decision === 'decline' ? null : {},
75
- _meta: decision === 'allowAlways' ? { persist: 'always' }
76
- : decision === 'allowConversation' && app?.session ? { persist: 'session' } : null };
77
- }
78
- function mcpToolApproval(request) {
79
- const p = request.params || {}, meta = p._meta || {};
80
- if (request.method !== elicitation || p.mode !== 'form' || meta.codex_approval_kind !== 'mcp_tool_call'
81
- || (meta.codex_request_type != null && meta.codex_request_type !== 'approval_request')
82
- || p.requestedSchema?.type !== 'object' || Object.keys(p.requestedSchema.properties || {}).length
83
- || p.requestedSchema.required?.length || typeof p.message !== 'string' || !p.message.trim()
84
- || typeof meta.connector_id !== 'string' || !meta.connector_id.trim()
85
- || !meta.tool_params || typeof meta.tool_params !== 'object' || Array.isArray(meta.tool_params)) return null;
86
- const connector = meta.connector_id.trim().toLowerCase().split(/[^a-z0-9]+/g).filter(Boolean).join('-');
87
- // These requests have dedicated approval semantics in Desktop.
88
- if (connector === 'browser-use' || connector === 'computer-use' || connector.startsWith('computer-use-')
89
- || meta.tool_name === 'access_browser_origin' || meta.tool_name === 'access_browser_origin_with_raw_cdp'
90
- || meta.connector_id === 'connector_20205bf7d4e99a89d7154bb849718324') return null;
91
- const modes = meta.persist === undefined ? [] : Array.isArray(meta.persist) ? meta.persist : [meta.persist];
92
- if ((Array.isArray(meta.persist) && (!modes.length || modes.length > 2))
93
- || modes.some(mode => !['session', 'always'].includes(mode))) return null;
94
- return { session: modes.includes('session'), persistent: modes.includes('always') };
95
- }
96
- function mcpToolResult(decision) {
97
- return { action: decision === 'decline' ? 'decline' : 'accept', content: decision === 'decline' ? null : {},
98
- _meta: decision === 'allowAlways' ? { persist: 'always' }
99
- : decision === 'allowSession' ? { persist: 'session' } : null };
100
- }
101
- function decisions(request) {
102
- if (confirmation(request)) return ['decline', 'accept'];
103
- if (request.method === command) return request.params.availableDecisions ?? ['accept', 'acceptForSession', 'decline', 'cancel'];
104
- if (request.method === file) return ['accept', 'acceptForSession', 'decline', 'cancel'];
105
- const action = browserAction(request);
106
- if (action) return ['allowOnce', ...(action.session ? ['allowSession'] : []), ...(action.persistent ? ['allowAlways'] : []), 'cancel'];
107
- const app = computerAppAccess(request);
108
- if (app) return ['decline', 'allowConversation', ...(app.persistent ? ['allowAlways'] : [])];
109
- const browser = browserAccess(request);
110
- if (browser) return browser.persistent ? ['allowOnce', 'allowSite', 'allowAllSites', 'decline'] : ['allowOnce', 'decline'];
111
- const tool = mcpToolApproval(request);
112
- if (tool) return ['decline', 'allowOnce', ...(tool.session ? ['allowSession'] : []), ...(tool.persistent ? ['allowAlways'] : [])];
113
- return [];
114
- }
115
- function validate(request, result) {
116
- if (confirmation(request)) {
117
- if (!decisions(request).some(decision => equal(confirmationResult(request, decision), result))) throw new Error('确认结果不属于当前请求');
118
- return;
119
- }
120
- if ([command, file].includes(request.method)) {
121
- if (!result || Object.keys(result).length !== 1 || !decisions(request).some(value => equal(value, result.decision))) throw new Error('审批选项不属于当前请求');
122
- return;
123
- }
124
- if (request.method === input) {
125
- const questions = request.params.questions || [];
126
- if (!result?.answers || Object.keys(result).length !== 1 || Object.keys(result.answers).length !== questions.length
127
- || questions.some(q => !Array.isArray(result.answers[q.id]?.answers) || result.answers[q.id].answers.some(a => typeof a !== 'string'))) throw new Error('回答格式无效');
128
- return;
129
- }
130
- if (browserAction(request)) {
131
- if (!decisions(request).some(decision => equal(browserActionResult(decision), result))) throw new Error('浏览器操作决定不属于当前请求');
132
- return;
133
- }
134
- if (computerAppAccess(request)) {
135
- if (!decisions(request).some(decision => equal(computerAppResult(request, decision), result))) throw new Error('应用访问决定不属于当前请求');
136
- return;
137
- }
138
- const browser = browserAccess(request);
139
- if (browser) {
140
- const allowed = [
141
- { action: 'accept', content: {}, _meta: null },
142
- { action: 'decline', content: null, _meta: null },
143
- ];
144
- if (browser.persistent) allowed.push(
145
- { action: 'accept', content: {}, _meta: { persist: 'always' } },
146
- { action: 'accept', content: {}, _meta: { persist: 'always', browser_use_persistent_approval_scope: 'all-sites' } },
147
- );
148
- if (!allowed.some(choice => equal(choice, result))) throw new Error('网站访问决定不属于当前请求');
149
- return;
150
- }
151
- if (mcpToolApproval(request)) {
152
- if (!decisions(request).some(decision => equal(mcpToolResult(decision), result))) throw new Error('工具授权决定不属于当前请求');
153
- return;
154
- }
155
- throw new Error('此类请求需在工作站处理');
156
- }
157
- class Store {
158
- constructor(epoch, changed = () => {}) {
159
- this.epoch = epoch; this.changed = changed; this.requests = new Map(); this.approvalHistory = new Map();
160
- }
161
- list(threadId) { return [...this.requests.values()].filter(r => r.params.threadId === threadId).map(r => ({ ...r })); }
162
- history(threadId) { return [...this.approvalHistory.values()].filter(r => r.params.threadId === threadId).map(r => ({ ...r })); }
163
- complete(request, status) {
164
- const approval = this.approvalHistory.get(approvalId(request));
165
- if (approval) { approval.status = status; approval.completedAtMs = Date.now(); }
166
- }
167
- receive(message) {
168
- if (!['string', 'number'].includes(typeof message.id) || !message.params?.threadId || this.requests.has(message.id)) return;
169
- const request = { id: message.id, method: message.method, params: message.params, epoch: this.epoch, status: 'pending' };
170
- if (request.method !== input) this.approvalHistory.set(approvalId(request), {
171
- type: 'approval', id: approvalId(request), requestId: request.id, method: request.method,
172
- params: request.params, turnId: request.params.turnId, status: 'pending', receivedAtMs: Date.now(),
173
- });
174
- this.requests.set(message.id, request); this.changed(request.params.threadId);
175
- }
176
- notification(method, params) {
177
- if (method === 'serverRequest/resolved') {
178
- const request = this.requests.get(params.requestId);
179
- if (request?.params.threadId === params.threadId) {
180
- this.complete(request, 'resolved'); this.requests.delete(params.requestId); this.changed(params.threadId);
181
- }
182
- } else if (method === 'turn/completed' || method === 'thread/closed') {
183
- for (const [id, r] of this.requests) if (r.params.threadId === params.threadId && (method === 'thread/closed' || r.params.turnId === params.turn?.id)) {
184
- this.complete(r, 'expired'); this.requests.delete(id);
185
- }
186
- this.changed(params.threadId);
187
- }
188
- }
189
- forget(threadId) {
190
- for (const [id, request] of this.requests) if (request.params.threadId === threadId) this.requests.delete(id);
191
- for (const [id, approval] of this.approvalHistory) if (approval.params.threadId === threadId) this.approvalHistory.delete(id);
192
- this.changed(threadId);
193
- }
194
- reset(epoch) {
195
- const threads = new Set([...this.requests.values()].map(r => r.params.threadId));
196
- for (const request of this.requests.values()) this.complete(request, 'expired');
197
- this.requests.clear(); this.epoch = epoch; for (const id of threads) this.changed(id);
198
- this.approvalHistory.clear();
199
- }
200
- async respond(threadId, { id, epoch, result }, send) {
201
- const request = this.requests.get(id);
202
- if (!request || epoch !== this.epoch || request.params.threadId !== threadId) throw new Error('审批已处理或连接已变化,请等待同步');
203
- if (request.status !== 'pending') throw new Error('决定已提交,正在等待工作站确认,请勿重复提交');
204
- validate(request, result);
205
- const approval = this.approvalHistory.get(approvalId(request));
206
- if (approval) { approval.status = 'submitted'; approval.result = result; }
207
- request.status = 'submitted'; this.changed(threadId);
208
- try { await send({ id: request.id, result }); }
209
- catch (error) {
210
- if (this.requests.get(id) === request) {
211
- request.status = 'unknown'; if (approval) approval.status = 'unknown'; this.changed(threadId);
212
- }
213
- throw new Error('审批发送结果未确认,请检查工作站;不会自动重发。' + error.message);
214
- }
215
- return { submitted: true };
216
- }
217
- }
218
- return { Store, decisions, validate, command, file, input, elicitation, browserAccess, browserAction, browserActionResult, computerAppAccess, computerAppResult, mcpToolApproval, mcpToolResult, confirmation, confirmationResult };
219
- });
1
+ (function (root, factory) {
2
+ if (typeof module === 'object' && module.exports) module.exports = factory();
3
+ else root.CodexServerRequests = factory();
4
+ })(globalThis, function () {
5
+ const command = 'item/commandExecution/requestApproval';
6
+ const file = 'item/fileChange/requestApproval';
7
+ const input = 'item/tool/requestUserInput';
8
+ const elicitation = 'mcpServer/elicitation/request';
9
+ const equal = (a, b) => JSON.stringify(a) === JSON.stringify(b);
10
+ const approvalId = request => 'approval:' + (request.params.turnId || '') + ':' + typeof request.id + ':' + request.id;
11
+ function confirmation(request) {
12
+ const p = request.params || {}, value = p._meta?.['openai/confirmation'], schema = p.requestedSchema;
13
+ if (request.method !== elicitation || p.mode !== 'form' || value?.version !== 1
14
+ || typeof value.field !== 'string' || schema?.type !== 'object'
15
+ || Object.keys(schema.properties || {}).length !== 1 || schema.properties[value.field]?.type !== 'boolean'
16
+ || schema.required?.length !== 1 || schema.required[0] !== value.field) return null;
17
+ return value;
18
+ }
19
+ function confirmationResult(request, decision) {
20
+ return { action: decision, content: decision === 'accept' ? { [confirmation(request).field]: true } : null, _meta: null };
21
+ }
22
+ function browserAccess(request) {
23
+ const p = request.params || {}, meta = p._meta || {};
24
+ if (request.method !== elicitation || p.mode !== 'form' || meta.tool_name !== 'access_browser_origin'
25
+ || meta.codex_approval_kind !== 'mcp_tool_call' || meta.codex_request_type !== 'approval_request'
26
+ || meta.codex_sensitive_action !== true || p.requestedSchema?.type !== 'object'
27
+ || Object.keys(p.requestedSchema.properties || {}).length) return null;
28
+ try {
29
+ const origin = new URL(meta.origin);
30
+ if (!['http:', 'https:'].includes(origin.protocol) || origin.origin !== meta.origin) return null;
31
+ return { origin: meta.origin, persistent: meta.persist === 'always' };
32
+ } catch { return null; }
33
+ }
34
+ function browserAction(request) {
35
+ const p = request.params || {}, meta = p._meta || {};
36
+ if (browserAccess(request) || request.method !== elicitation || p.mode !== 'form'
37
+ || p.requestedSchema?.type !== 'object' || Object.keys(p.requestedSchema.properties || {}).length
38
+ || p.requestedSchema.required?.length || typeof p.message !== 'string' || !p.message.trim()) return null;
39
+ const tool = meta.codex_approval_kind === 'mcp_tool_call' && meta.connector_id === 'browser-use';
40
+ if (!tool && !['browser', 'browser-use'].includes(p.serverName)) return null;
41
+ if (meta.codex_approval_kind && meta.codex_approval_kind !== 'mcp_tool_call') return null;
42
+ const modes = meta.persist == null ? [] : Array.isArray(meta.persist) ? meta.persist : [meta.persist];
43
+ if (modes.some(mode => !['session', 'always'].includes(mode))) return null;
44
+ if (!tool && !modes.length) return null;
45
+ try {
46
+ const origin = new URL(meta.origin);
47
+ if (!['http:', 'https:'].includes(origin.protocol) || origin.origin !== meta.origin) return null;
48
+ return { origin: meta.origin, session: modes.includes('session'), persistent: modes.includes('always') };
49
+ } catch { return null; }
50
+ }
51
+ function browserActionResult(decision) {
52
+ return { action: decision === 'cancel' ? 'cancel' : 'accept', content: decision === 'cancel' ? null : {},
53
+ _meta: decision === 'allowAlways' ? { persist: 'always' }
54
+ : decision === 'allowSession' ? { persist: 'session' } : null };
55
+ }
56
+ function computerAppAccess(request) {
57
+ const p = request.params || {}, meta = p._meta || {};
58
+ if (request.method !== elicitation || p.mode !== 'form' || meta.codex_approval_kind !== 'mcp_tool_call'
59
+ || (meta.codex_request_type != null && meta.codex_request_type !== 'approval_request')
60
+ || typeof meta.connector_id !== 'string' || p.requestedSchema?.type !== 'object'
61
+ || Object.keys(p.requestedSchema.properties || {}).length || p.requestedSchema.required?.length) return null;
62
+ const connector = meta.connector_id.trim().toLowerCase().split(/[^a-z0-9]+/g).filter(Boolean).join('-');
63
+ if (connector !== 'computer-use' && !connector.startsWith('computer-use-')) return null;
64
+ const modes = Array.isArray(meta.persist) ? meta.persist : [meta.persist];
65
+ if (!modes.length || modes.some(mode => !['session', 'always'].includes(mode))) return null;
66
+ const display = Array.isArray(meta.tool_params_display) ? meta.tool_params_display.find(entry => entry?.name === 'app')?.value : null;
67
+ const app = typeof display === 'string' && display.trim() ? display : meta.tool_params?.app;
68
+ if (typeof app !== 'string' || !app.trim()) return null;
69
+ return { app: app.trim(), session: modes.includes('session'), persistent: modes.includes('always'),
70
+ subtitle: typeof meta.subtitle === 'string' ? meta.subtitle : '', highRisk: meta.riskLevel === 'high' };
71
+ }
72
+ function computerAppResult(request, decision) {
73
+ const app = computerAppAccess(request);
74
+ return { action: decision === 'decline' ? 'decline' : 'accept', content: decision === 'decline' ? null : {},
75
+ _meta: decision === 'allowAlways' ? { persist: 'always' }
76
+ : decision === 'allowConversation' && app?.session ? { persist: 'session' } : null };
77
+ }
78
+ function mcpToolApproval(request) {
79
+ const p = request.params || {}, meta = p._meta || {};
80
+ if (request.method !== elicitation || p.mode !== 'form' || meta.codex_approval_kind !== 'mcp_tool_call'
81
+ || (meta.codex_request_type != null && meta.codex_request_type !== 'approval_request')
82
+ || p.requestedSchema?.type !== 'object' || Object.keys(p.requestedSchema.properties || {}).length
83
+ || p.requestedSchema.required?.length || typeof p.message !== 'string' || !p.message.trim()
84
+ || typeof meta.connector_id !== 'string' || !meta.connector_id.trim()
85
+ || !meta.tool_params || typeof meta.tool_params !== 'object' || Array.isArray(meta.tool_params)) return null;
86
+ const connector = meta.connector_id.trim().toLowerCase().split(/[^a-z0-9]+/g).filter(Boolean).join('-');
87
+ // These requests have dedicated approval semantics in Desktop.
88
+ if (connector === 'browser-use' || connector === 'computer-use' || connector.startsWith('computer-use-')
89
+ || meta.tool_name === 'access_browser_origin' || meta.tool_name === 'access_browser_origin_with_raw_cdp'
90
+ || meta.connector_id === 'connector_20205bf7d4e99a89d7154bb849718324') return null;
91
+ const modes = meta.persist === undefined ? [] : Array.isArray(meta.persist) ? meta.persist : [meta.persist];
92
+ if ((Array.isArray(meta.persist) && (!modes.length || modes.length > 2))
93
+ || modes.some(mode => !['session', 'always'].includes(mode))) return null;
94
+ return { session: modes.includes('session'), persistent: modes.includes('always') };
95
+ }
96
+ function mcpToolResult(decision) {
97
+ return { action: decision === 'decline' ? 'decline' : 'accept', content: decision === 'decline' ? null : {},
98
+ _meta: decision === 'allowAlways' ? { persist: 'always' }
99
+ : decision === 'allowSession' ? { persist: 'session' } : null };
100
+ }
101
+ function decisions(request) {
102
+ if (confirmation(request)) return ['decline', 'accept'];
103
+ if (request.method === command) return request.params.availableDecisions ?? ['accept', 'acceptForSession', 'decline', 'cancel'];
104
+ if (request.method === file) return ['accept', 'acceptForSession', 'decline', 'cancel'];
105
+ const action = browserAction(request);
106
+ if (action) return ['allowOnce', ...(action.session ? ['allowSession'] : []), ...(action.persistent ? ['allowAlways'] : []), 'cancel'];
107
+ const app = computerAppAccess(request);
108
+ if (app) return ['decline', 'allowConversation', ...(app.persistent ? ['allowAlways'] : [])];
109
+ const browser = browserAccess(request);
110
+ if (browser) return browser.persistent ? ['allowOnce', 'allowSite', 'allowAllSites', 'decline'] : ['allowOnce', 'decline'];
111
+ const tool = mcpToolApproval(request);
112
+ if (tool) return ['decline', 'allowOnce', ...(tool.session ? ['allowSession'] : []), ...(tool.persistent ? ['allowAlways'] : [])];
113
+ return [];
114
+ }
115
+ function validate(request, result) {
116
+ if (confirmation(request)) {
117
+ if (!decisions(request).some(decision => equal(confirmationResult(request, decision), result))) throw new Error('确认结果不属于当前请求');
118
+ return;
119
+ }
120
+ if ([command, file].includes(request.method)) {
121
+ if (!result || Object.keys(result).length !== 1 || !decisions(request).some(value => equal(value, result.decision))) throw new Error('审批选项不属于当前请求');
122
+ return;
123
+ }
124
+ if (request.method === input) {
125
+ const questions = request.params.questions || [];
126
+ if (!result?.answers || Object.keys(result).length !== 1 || Object.keys(result.answers).length !== questions.length
127
+ || questions.some(q => !Array.isArray(result.answers[q.id]?.answers) || result.answers[q.id].answers.some(a => typeof a !== 'string'))) throw new Error('回答格式无效');
128
+ return;
129
+ }
130
+ if (browserAction(request)) {
131
+ if (!decisions(request).some(decision => equal(browserActionResult(decision), result))) throw new Error('浏览器操作决定不属于当前请求');
132
+ return;
133
+ }
134
+ if (computerAppAccess(request)) {
135
+ if (!decisions(request).some(decision => equal(computerAppResult(request, decision), result))) throw new Error('应用访问决定不属于当前请求');
136
+ return;
137
+ }
138
+ const browser = browserAccess(request);
139
+ if (browser) {
140
+ const allowed = [
141
+ { action: 'accept', content: {}, _meta: null },
142
+ { action: 'decline', content: null, _meta: null },
143
+ ];
144
+ if (browser.persistent) allowed.push(
145
+ { action: 'accept', content: {}, _meta: { persist: 'always' } },
146
+ { action: 'accept', content: {}, _meta: { persist: 'always', browser_use_persistent_approval_scope: 'all-sites' } },
147
+ );
148
+ if (!allowed.some(choice => equal(choice, result))) throw new Error('网站访问决定不属于当前请求');
149
+ return;
150
+ }
151
+ if (mcpToolApproval(request)) {
152
+ if (!decisions(request).some(decision => equal(mcpToolResult(decision), result))) throw new Error('工具授权决定不属于当前请求');
153
+ return;
154
+ }
155
+ throw new Error('此类请求需在工作站处理');
156
+ }
157
+ class Store {
158
+ constructor(epoch, changed = () => {}) {
159
+ this.epoch = epoch; this.changed = changed; this.requests = new Map(); this.approvalHistory = new Map();
160
+ }
161
+ list(threadId) { return [...this.requests.values()].filter(r => r.params.threadId === threadId).map(r => ({ ...r })); }
162
+ history(threadId) { return [...this.approvalHistory.values()].filter(r => r.params.threadId === threadId).map(r => ({ ...r })); }
163
+ complete(request, status) {
164
+ const approval = this.approvalHistory.get(approvalId(request));
165
+ if (approval) { approval.status = status; approval.completedAtMs = Date.now(); }
166
+ }
167
+ receive(message) {
168
+ if (!['string', 'number'].includes(typeof message.id) || !message.params?.threadId || this.requests.has(message.id)) return;
169
+ const request = { id: message.id, method: message.method, params: message.params, epoch: this.epoch, status: 'pending' };
170
+ if (request.method !== input) this.approvalHistory.set(approvalId(request), {
171
+ type: 'approval', id: approvalId(request), requestId: request.id, method: request.method,
172
+ params: request.params, turnId: request.params.turnId, status: 'pending', receivedAtMs: Date.now(),
173
+ });
174
+ this.requests.set(message.id, request); this.changed(request.params.threadId);
175
+ }
176
+ notification(method, params) {
177
+ if (method === 'serverRequest/resolved') {
178
+ const request = this.requests.get(params.requestId);
179
+ if (request?.params.threadId === params.threadId) {
180
+ this.complete(request, 'resolved'); this.requests.delete(params.requestId); this.changed(params.threadId);
181
+ }
182
+ } else if (method === 'turn/completed' || method === 'thread/closed') {
183
+ for (const [id, r] of this.requests) if (r.params.threadId === params.threadId && (method === 'thread/closed' || r.params.turnId === params.turn?.id)) {
184
+ this.complete(r, 'expired'); this.requests.delete(id);
185
+ }
186
+ this.changed(params.threadId);
187
+ }
188
+ }
189
+ forget(threadId) {
190
+ for (const [id, request] of this.requests) if (request.params.threadId === threadId) this.requests.delete(id);
191
+ for (const [id, approval] of this.approvalHistory) if (approval.params.threadId === threadId) this.approvalHistory.delete(id);
192
+ this.changed(threadId);
193
+ }
194
+ reset(epoch) {
195
+ const threads = new Set([...this.requests.values()].map(r => r.params.threadId));
196
+ for (const request of this.requests.values()) this.complete(request, 'expired');
197
+ this.requests.clear(); this.epoch = epoch; for (const id of threads) this.changed(id);
198
+ this.approvalHistory.clear();
199
+ }
200
+ async respond(threadId, { id, epoch, result }, send) {
201
+ const request = this.requests.get(id);
202
+ if (!request || epoch !== this.epoch || request.params.threadId !== threadId) throw new Error('审批已处理或连接已变化,请等待同步');
203
+ if (request.status !== 'pending') throw new Error('决定已提交,正在等待工作站确认,请勿重复提交');
204
+ validate(request, result);
205
+ const approval = this.approvalHistory.get(approvalId(request));
206
+ if (approval) { approval.status = 'submitted'; approval.result = result; }
207
+ request.status = 'submitted'; this.changed(threadId);
208
+ try { await send({ id: request.id, result }); }
209
+ catch (error) {
210
+ if (this.requests.get(id) === request) {
211
+ request.status = 'unknown'; if (approval) approval.status = 'unknown'; this.changed(threadId);
212
+ }
213
+ throw new Error('审批发送结果未确认,请检查工作站;不会自动重发。' + error.message);
214
+ }
215
+ return { submitted: true };
216
+ }
217
+ }
218
+ return { Store, decisions, validate, command, file, input, elicitation, browserAccess, browserAction, browserActionResult, computerAppAccess, computerAppResult, mcpToolApproval, mcpToolResult, confirmation, confirmationResult };
219
+ });