@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.
- package/README.md +95 -89
- package/dist/bin/cleanup-accounts.mjs +63 -63
- package/dist/bin/codex-remote.mjs +374 -367
- package/dist/bin/manage-relay-admin.mjs +20 -20
- package/dist/bin/migrate-relay-db.mjs +109 -109
- package/dist/bin/publish-android-release.mjs +62 -58
- package/dist/bin/self-relay-admin.mjs +8 -8
- package/dist/bin/self-relay-demo.mjs +33 -33
- package/dist/bin/self-relay-server.mjs +66 -66
- package/dist/build-platform.json +1 -1
- package/dist/get-lan-networks.ps1 +40 -40
- package/dist/get-shared-service-url.ps1 +6 -6
- package/dist/shared/app-server-events.cjs +422 -412
- package/dist/shared/code-browser.cjs +159 -159
- package/dist/shared/file-changes.cjs +31 -31
- package/dist/shared/file-citations.cjs +117 -117
- package/dist/shared/file-mentions.cjs +92 -92
- package/dist/shared/message-delivery.cjs +54 -54
- package/dist/shared/p2p-data-channel.cjs +72 -72
- package/dist/shared/p2p-diagnostics.cjs +28 -28
- package/dist/shared/p2p-file-channel.cjs +74 -74
- package/dist/shared/plan-pricing.cjs +32 -32
- package/dist/shared/question-attention.cjs +40 -0
- package/dist/shared/server-requests.cjs +219 -219
- package/dist/shared/timeline-sync.cjs +173 -173
- package/dist/skills/code-relax/SKILL.md +22 -22
- package/dist/src/archived-threads.mjs +22 -0
- package/dist/src/background-process.mjs +18 -18
- package/dist/src/bounded-process-log.mjs +63 -63
- package/dist/src/celsius-client.mjs +107 -107
- package/dist/src/code-browser.mjs +100 -100
- package/dist/src/desktop-launcher.mjs +46 -46
- package/dist/src/dots-approval-timeline.mjs +99 -99
- package/dist/src/dots-avatar.mjs +42 -42
- package/dist/src/dots-chat-projection.mjs +50 -50
- package/dist/src/dots-chat-server.mjs +461 -461
- package/dist/src/dots-message-images.mjs +319 -319
- package/dist/src/dots-profile.mjs +77 -77
- package/dist/src/dots-room-activity.mjs +94 -94
- package/dist/src/dots-room-client.mjs +135 -135
- package/dist/src/dots-room-projection.mjs +94 -94
- package/dist/src/dots-schedules.mjs +66 -66
- package/dist/src/dots-summary-tasks.mjs +32 -32
- package/dist/src/dots-task-catalog.mjs +430 -430
- package/dist/src/dots-task-files.mjs +52 -52
- package/dist/src/execution-health.mjs +26 -26
- package/dist/src/file-transfer-range.mjs +22 -22
- package/dist/src/file-upload-session.mjs +130 -130
- package/dist/src/hosted-history-app-server.mjs +103 -103
- package/dist/src/hosted-history-cache.mjs +113 -113
- package/dist/src/lan-socket-server.mjs +265 -263
- package/dist/src/managed-app-server.mjs +244 -244
- package/dist/src/message-images.mjs +9 -9
- package/dist/src/message-upload-files.mjs +74 -74
- package/dist/src/onboarding.mjs +12 -12
- package/dist/src/plan-pricing.mjs +47 -47
- package/dist/src/platform/README.md +12 -12
- package/dist/src/platform/codex-protocol.mjs +46 -46
- package/dist/src/platform/codex-runtime.mjs +3 -3
- package/dist/src/platform/credential-store.mjs +3 -3
- package/dist/src/platform/desktop-host.mjs +3 -3
- package/dist/src/platform/desktop-project-registration.mjs +3 -3
- package/dist/src/platform/desktop-project-store.mjs +208 -149
- package/dist/src/platform/file-opener.mjs +9 -0
- package/dist/src/platform/linux/README.md +3 -3
- package/dist/src/platform/linux/codex-runtime.mjs +56 -0
- package/dist/src/platform/linux/credential-store.mjs +28 -0
- package/dist/src/platform/linux/desktop-host.mjs +83 -0
- package/dist/src/platform/linux/desktop-installation.mjs +36 -0
- package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
- package/dist/src/platform/linux/network-info.mjs +11 -0
- package/dist/src/platform/linux/process.mjs +42 -0
- package/dist/src/platform/linux/project-locations.mjs +17 -0
- package/dist/src/platform/linux/resident-startup.mjs +52 -0
- package/dist/src/platform/linux/retained-runtime.mjs +76 -0
- package/dist/src/platform/linux/service-host.mjs +18 -0
- package/dist/src/platform/linux/system.mjs +49 -0
- package/dist/src/platform/linux/user-environment.mjs +7 -0
- package/dist/src/platform/macos/README.md +3 -3
- package/dist/src/platform/macos/codex-runtime.mjs +125 -125
- package/dist/src/platform/macos/credential-store.mjs +10 -10
- package/dist/src/platform/macos/desktop-host.mjs +82 -82
- package/dist/src/platform/macos/desktop-project-registration.mjs +11 -10
- package/dist/src/platform/macos/network-info.mjs +8 -28
- package/dist/src/platform/macos/project-locations.mjs +8 -8
- package/dist/src/platform/macos/resident-startup.mjs +84 -84
- package/dist/src/platform/macos/retained-runtime.mjs +127 -127
- package/dist/src/platform/macos/service-host.mjs +18 -18
- package/dist/src/platform/macos/system.mjs +29 -29
- package/dist/src/platform/macos/user-environment.mjs +7 -7
- package/dist/src/platform/native-package.mjs +58 -46
- package/dist/src/platform/network-info.mjs +3 -3
- package/dist/src/platform/path-identity.mjs +5 -5
- package/dist/src/platform/posix/network-candidates.mjs +23 -0
- package/dist/src/platform/posix/runtime-paths.mjs +25 -25
- package/dist/src/platform/project-locations.mjs +3 -3
- package/dist/src/platform/runtime-paths.mjs +8 -8
- package/dist/src/platform/select.mjs +5 -5
- package/dist/src/platform/service-host.mjs +3 -3
- package/dist/src/platform/user-environment.mjs +3 -3
- package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
- package/dist/src/platform/windows/activate-desktop.ps1 +48 -48
- package/dist/src/platform/windows/background-process.mjs +30 -30
- package/dist/src/platform/windows/codex-runtime.mjs +215 -215
- package/dist/src/platform/windows/credential-store.mjs +27 -27
- package/dist/src/platform/windows/credential-store.ps1 +49 -49
- package/dist/src/platform/windows/desktop-helper.mjs +179 -179
- package/dist/src/platform/windows/desktop-host.mjs +58 -58
- package/dist/src/platform/windows/desktop-info.ps1 +14 -14
- package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
- package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
- package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -21
- package/dist/src/platform/windows/dots-profile.mjs +49 -49
- package/dist/src/platform/windows/launch-worker.mjs +41 -41
- package/dist/src/platform/windows/network-info.mjs +34 -34
- package/dist/src/platform/windows/project-locations.mjs +16 -16
- package/dist/src/platform/windows/resident-start.js +20 -20
- package/dist/src/platform/windows/resident-start.ps1 +7 -7
- package/dist/src/platform/windows/resident-startup.ps1 +83 -83
- package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
- package/dist/src/platform/windows/runtime-paths.mjs +20 -20
- package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
- package/dist/src/platform/windows/service-host.mjs +31 -31
- package/dist/src/platform/windows/start-hidden-console.ps1 +49 -49
- package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
- package/dist/src/platform/windows/user-environment.mjs +50 -50
- package/dist/src/platform/windows/user-environment.ps1 +41 -41
- package/dist/src/project-archive.mjs +30 -0
- package/dist/src/project-create.mjs +121 -119
- package/dist/src/project-management.mjs +59 -69
- package/dist/src/project-remove.mjs +55 -0
- package/dist/src/project-update.mjs +56 -0
- package/dist/src/runtime-discovery.mjs +47 -47
- package/dist/src/self-relay/account-cleanup.mjs +30 -30
- package/dist/src/self-relay/account-connections.mjs +52 -52
- package/dist/src/self-relay/admin-cloud.mjs +101 -101
- package/dist/src/self-relay/admin-page.mjs +80 -80
- package/dist/src/self-relay/admin-server.mjs +74 -74
- package/dist/src/self-relay/admin-state.mjs +127 -127
- package/dist/src/self-relay/admin-style.mjs +14 -14
- package/dist/src/self-relay/android-releases.mjs +61 -61
- package/dist/src/self-relay/client.mjs +61 -61
- package/dist/src/self-relay/connector.mjs +439 -438
- package/dist/src/self-relay/debug-allowlist.mjs +33 -33
- package/dist/src/self-relay/demo.mjs +37 -37
- package/dist/src/self-relay/device-info.mjs +27 -0
- package/dist/src/self-relay/device-store.mjs +92 -84
- package/dist/src/self-relay/diagnostics.mjs +54 -54
- package/dist/src/self-relay/email-accounts.mjs +183 -183
- package/dist/src/self-relay/file-wire.mjs +13 -13
- package/dist/src/self-relay/lan-authorizations.mjs +71 -42
- package/dist/src/self-relay/lifecycle.mjs +264 -250
- package/dist/src/self-relay/long-pairing.mjs +40 -40
- package/dist/src/self-relay/mysql-state.mjs +118 -105
- package/dist/src/self-relay/p2p-probe.mjs +159 -159
- package/dist/src/self-relay/privacy-page.mjs +26 -26
- package/dist/src/self-relay/public-releases.mjs +34 -0
- package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
- package/dist/src/self-relay/server.mjs +1006 -932
- package/dist/src/self-relay/usage-daily.mjs +55 -55
- package/dist/src/self-relay/usage-meter.mjs +141 -141
- package/dist/src/self-relay/windows-route-helper.mjs +75 -75
- package/dist/src/self-relay/wire.mjs +36 -36
- package/dist/src/server.mjs +1861 -1778
- package/dist/src/service-control.mjs +82 -82
- package/dist/src/service-doctor.mjs +110 -110
- package/dist/src/service-health.mjs +33 -33
- package/dist/src/service-install.mjs +91 -91
- package/dist/src/service-lifecycle.mjs +138 -138
- package/dist/src/service-restart.mjs +81 -81
- package/dist/src/shared-app-server.mjs +615 -611
- package/dist/src/shared-catalog.mjs +132 -132
- package/dist/src/shared-recovery.mjs +146 -146
- package/dist/src/shared-thread-stream.mjs +164 -164
- package/dist/src/skill-install.mjs +51 -51
- package/dist/src/thread-title-generation.mjs +150 -150
- package/dist/src/turn-model-history.mjs +47 -47
- package/dist/src/usage-reset.mjs +17 -17
- package/dist/src/workstation-downloads.mjs +120 -120
- package/dist/tools/rtc-route-helper.ps1 +99 -99
- package/dist/tools/start-rtc-route-helper.ps1 +15 -15
- package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
- package/dist/web/vendor/markdown-it.umd.min.js +13 -13
- package/package.json +7 -6
- 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
|
+
});
|