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

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 (187) 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 +63 -58
  7. package/dist/bin/publish-harmony-release.mjs +46 -0
  8. package/dist/bin/release-upload-server.mjs +174 -0
  9. package/dist/bin/self-relay-admin.mjs +8 -8
  10. package/dist/bin/self-relay-demo.mjs +33 -33
  11. package/dist/bin/self-relay-server.mjs +66 -66
  12. package/dist/build-platform.json +1 -1
  13. package/dist/get-lan-networks.ps1 +40 -40
  14. package/dist/get-shared-service-url.ps1 +6 -6
  15. package/dist/shared/app-server-events.cjs +422 -412
  16. package/dist/shared/code-browser.cjs +159 -159
  17. package/dist/shared/file-changes.cjs +31 -31
  18. package/dist/shared/file-citations.cjs +117 -117
  19. package/dist/shared/file-mentions.cjs +92 -92
  20. package/dist/shared/message-delivery.cjs +54 -54
  21. package/dist/shared/p2p-data-channel.cjs +72 -72
  22. package/dist/shared/p2p-diagnostics.cjs +28 -28
  23. package/dist/shared/p2p-file-channel.cjs +78 -74
  24. package/dist/shared/plan-pricing.cjs +32 -32
  25. package/dist/shared/question-attention.cjs +69 -0
  26. package/dist/shared/server-requests.cjs +219 -219
  27. package/dist/shared/timeline-sync.cjs +173 -173
  28. package/dist/skills/code-relax/SKILL.md +22 -22
  29. package/dist/src/archived-threads.mjs +22 -0
  30. package/dist/src/background-process.mjs +18 -18
  31. package/dist/src/bounded-process-log.mjs +63 -63
  32. package/dist/src/celsius-client.mjs +107 -107
  33. package/dist/src/code-browser.mjs +100 -100
  34. package/dist/src/desktop-launcher.mjs +46 -46
  35. package/dist/src/dots-approval-timeline.mjs +99 -99
  36. package/dist/src/dots-avatar.mjs +42 -42
  37. package/dist/src/dots-chat-projection.mjs +50 -50
  38. package/dist/src/dots-chat-server.mjs +461 -461
  39. package/dist/src/dots-message-images.mjs +319 -319
  40. package/dist/src/dots-profile.mjs +77 -77
  41. package/dist/src/dots-room-activity.mjs +94 -94
  42. package/dist/src/dots-room-client.mjs +135 -135
  43. package/dist/src/dots-room-projection.mjs +94 -94
  44. package/dist/src/dots-schedules.mjs +66 -66
  45. package/dist/src/dots-summary-tasks.mjs +32 -32
  46. package/dist/src/dots-task-catalog.mjs +430 -430
  47. package/dist/src/dots-task-files.mjs +52 -52
  48. package/dist/src/execution-health.mjs +26 -26
  49. package/dist/src/file-transfer-range.mjs +22 -22
  50. package/dist/src/file-upload-session.mjs +130 -130
  51. package/dist/src/hosted-history-app-server.mjs +103 -103
  52. package/dist/src/hosted-history-cache.mjs +113 -113
  53. package/dist/src/lan-socket-server.mjs +265 -263
  54. package/dist/src/managed-app-server.mjs +244 -244
  55. package/dist/src/message-images.mjs +9 -9
  56. package/dist/src/message-upload-files.mjs +74 -74
  57. package/dist/src/onboarding.mjs +12 -12
  58. package/dist/src/plan-pricing.mjs +47 -47
  59. package/dist/src/platform/README.md +12 -12
  60. package/dist/src/platform/codex-protocol.mjs +46 -46
  61. package/dist/src/platform/codex-runtime.mjs +3 -3
  62. package/dist/src/platform/credential-store.mjs +3 -3
  63. package/dist/src/platform/desktop-host.mjs +3 -3
  64. package/dist/src/platform/desktop-project-registration.mjs +3 -3
  65. package/dist/src/platform/desktop-project-store.mjs +208 -149
  66. package/dist/src/platform/file-opener.mjs +9 -0
  67. package/dist/src/platform/linux/README.md +3 -3
  68. package/dist/src/platform/linux/codex-runtime.mjs +56 -0
  69. package/dist/src/platform/linux/credential-store.mjs +28 -0
  70. package/dist/src/platform/linux/desktop-host.mjs +83 -0
  71. package/dist/src/platform/linux/desktop-installation.mjs +36 -0
  72. package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
  73. package/dist/src/platform/linux/network-info.mjs +11 -0
  74. package/dist/src/platform/linux/process.mjs +42 -0
  75. package/dist/src/platform/linux/project-locations.mjs +17 -0
  76. package/dist/src/platform/linux/resident-startup.mjs +52 -0
  77. package/dist/src/platform/linux/retained-runtime.mjs +76 -0
  78. package/dist/src/platform/linux/service-host.mjs +18 -0
  79. package/dist/src/platform/linux/system.mjs +49 -0
  80. package/dist/src/platform/linux/user-environment.mjs +7 -0
  81. package/dist/src/platform/macos/README.md +3 -3
  82. package/dist/src/platform/macos/codex-runtime.mjs +125 -125
  83. package/dist/src/platform/macos/credential-store.mjs +10 -10
  84. package/dist/src/platform/macos/desktop-host.mjs +82 -82
  85. package/dist/src/platform/macos/desktop-project-registration.mjs +11 -10
  86. package/dist/src/platform/macos/network-info.mjs +8 -28
  87. package/dist/src/platform/macos/project-locations.mjs +8 -8
  88. package/dist/src/platform/macos/resident-startup.mjs +84 -84
  89. package/dist/src/platform/macos/retained-runtime.mjs +127 -127
  90. package/dist/src/platform/macos/service-host.mjs +18 -18
  91. package/dist/src/platform/macos/system.mjs +29 -29
  92. package/dist/src/platform/macos/user-environment.mjs +7 -7
  93. package/dist/src/platform/native-package.mjs +58 -46
  94. package/dist/src/platform/network-info.mjs +3 -3
  95. package/dist/src/platform/path-identity.mjs +5 -5
  96. package/dist/src/platform/posix/network-candidates.mjs +23 -0
  97. package/dist/src/platform/posix/runtime-paths.mjs +25 -25
  98. package/dist/src/platform/project-locations.mjs +3 -3
  99. package/dist/src/platform/runtime-paths.mjs +8 -8
  100. package/dist/src/platform/select.mjs +5 -5
  101. package/dist/src/platform/service-host.mjs +3 -3
  102. package/dist/src/platform/user-environment.mjs +3 -3
  103. package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
  104. package/dist/src/platform/windows/activate-desktop.ps1 +48 -48
  105. package/dist/src/platform/windows/background-process.mjs +30 -30
  106. package/dist/src/platform/windows/codex-runtime.mjs +215 -215
  107. package/dist/src/platform/windows/credential-store.mjs +27 -27
  108. package/dist/src/platform/windows/credential-store.ps1 +49 -49
  109. package/dist/src/platform/windows/desktop-helper.mjs +179 -179
  110. package/dist/src/platform/windows/desktop-host.mjs +58 -58
  111. package/dist/src/platform/windows/desktop-info.ps1 +14 -14
  112. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
  113. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
  114. package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -21
  115. package/dist/src/platform/windows/dots-profile.mjs +49 -49
  116. package/dist/src/platform/windows/launch-worker.mjs +41 -41
  117. package/dist/src/platform/windows/network-info.mjs +34 -34
  118. package/dist/src/platform/windows/project-locations.mjs +16 -16
  119. package/dist/src/platform/windows/resident-start.js +20 -20
  120. package/dist/src/platform/windows/resident-start.ps1 +7 -7
  121. package/dist/src/platform/windows/resident-startup.ps1 +83 -83
  122. package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
  123. package/dist/src/platform/windows/runtime-paths.mjs +20 -20
  124. package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
  125. package/dist/src/platform/windows/service-host.mjs +31 -31
  126. package/dist/src/platform/windows/start-hidden-console.ps1 +49 -49
  127. package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
  128. package/dist/src/platform/windows/user-environment.mjs +50 -50
  129. package/dist/src/platform/windows/user-environment.ps1 +41 -41
  130. package/dist/src/project-archive.mjs +30 -0
  131. package/dist/src/project-create.mjs +121 -119
  132. package/dist/src/project-management.mjs +59 -69
  133. package/dist/src/project-remove.mjs +55 -0
  134. package/dist/src/project-update.mjs +56 -0
  135. package/dist/src/runtime-discovery.mjs +47 -47
  136. package/dist/src/self-relay/account-cleanup.mjs +30 -30
  137. package/dist/src/self-relay/account-connections.mjs +52 -52
  138. package/dist/src/self-relay/admin-cloud.mjs +101 -101
  139. package/dist/src/self-relay/admin-page.mjs +80 -80
  140. package/dist/src/self-relay/admin-server.mjs +74 -74
  141. package/dist/src/self-relay/admin-state.mjs +127 -127
  142. package/dist/src/self-relay/admin-style.mjs +14 -14
  143. package/dist/src/self-relay/android-releases.mjs +61 -61
  144. package/dist/src/self-relay/client.mjs +61 -61
  145. package/dist/src/self-relay/connector.mjs +439 -438
  146. package/dist/src/self-relay/debug-allowlist.mjs +33 -33
  147. package/dist/src/self-relay/demo.mjs +37 -37
  148. package/dist/src/self-relay/device-info.mjs +27 -0
  149. package/dist/src/self-relay/device-store.mjs +92 -84
  150. package/dist/src/self-relay/diagnostics.mjs +54 -54
  151. package/dist/src/self-relay/email-accounts.mjs +183 -183
  152. package/dist/src/self-relay/file-wire.mjs +13 -13
  153. package/dist/src/self-relay/lan-authorizations.mjs +71 -42
  154. package/dist/src/self-relay/lifecycle.mjs +264 -250
  155. package/dist/src/self-relay/long-pairing.mjs +40 -40
  156. package/dist/src/self-relay/mysql-state.mjs +118 -105
  157. package/dist/src/self-relay/p2p-probe.mjs +159 -159
  158. package/dist/src/self-relay/privacy-page.mjs +26 -26
  159. package/dist/src/self-relay/public-releases.mjs +52 -0
  160. package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
  161. package/dist/src/self-relay/server.mjs +1006 -932
  162. package/dist/src/self-relay/usage-daily.mjs +55 -55
  163. package/dist/src/self-relay/usage-meter.mjs +141 -141
  164. package/dist/src/self-relay/windows-route-helper.mjs +75 -75
  165. package/dist/src/self-relay/wire.mjs +36 -36
  166. package/dist/src/server.mjs +1861 -1778
  167. package/dist/src/service-control.mjs +82 -82
  168. package/dist/src/service-doctor.mjs +110 -110
  169. package/dist/src/service-health.mjs +33 -33
  170. package/dist/src/service-install.mjs +91 -91
  171. package/dist/src/service-lifecycle.mjs +138 -138
  172. package/dist/src/service-restart.mjs +81 -81
  173. package/dist/src/shared-app-server.mjs +615 -611
  174. package/dist/src/shared-catalog.mjs +132 -132
  175. package/dist/src/shared-recovery.mjs +146 -146
  176. package/dist/src/shared-thread-stream.mjs +164 -164
  177. package/dist/src/skill-install.mjs +51 -51
  178. package/dist/src/thread-title-generation.mjs +150 -150
  179. package/dist/src/turn-model-history.mjs +47 -47
  180. package/dist/src/usage-reset.mjs +17 -17
  181. package/dist/src/workstation-downloads.mjs +120 -120
  182. package/dist/tools/rtc-route-helper.ps1 +99 -99
  183. package/dist/tools/start-rtc-route-helper.ps1 +15 -15
  184. package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
  185. package/dist/web/vendor/markdown-it.umd.min.js +13 -13
  186. package/package.json +7 -6
  187. 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
+ });