@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,74 +1,74 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import sharp from 'sharp';
|
|
4
|
-
import media from '../shared/code-browser.cjs';
|
|
5
|
-
import { validateInlineImage } from './message-images.mjs';
|
|
6
|
-
import { MAX_FILE_BYTES, MAX_FILE_COUNT, MAX_TOTAL_BYTES } from './file-upload-session.mjs';
|
|
7
|
-
|
|
8
|
-
const mimeTypes = { pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv',
|
|
9
|
-
json: 'application/json', zip: 'application/zip', doc: 'application/msword',
|
|
10
|
-
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
11
|
-
xls: 'application/vnd.ms-excel', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
12
|
-
ppt: 'application/vnd.ms-powerpoint', pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' };
|
|
13
|
-
|
|
14
|
-
async function localFile(file, directory) {
|
|
15
|
-
if (typeof file?.path !== 'string' || typeof file.name !== 'string' || !file.name || /[\r\n\0]/.test(file.name)) {
|
|
16
|
-
throw new Error('附件信息无效,请重新选择。');
|
|
17
|
-
}
|
|
18
|
-
const root = await fs.realpath(directory), resolved = await fs.realpath(file.path);
|
|
19
|
-
if (path.relative(root, path.dirname(resolved)) !== '') throw new Error('附件必须来自本次工作站上传目录。');
|
|
20
|
-
const handle = await fs.open(resolved, 'r');
|
|
21
|
-
try {
|
|
22
|
-
const stat = await handle.stat();
|
|
23
|
-
if (!stat.isFile() || stat.size < 1 || stat.size > MAX_FILE_BYTES) throw new Error('附件为空或超过 20 MiB。');
|
|
24
|
-
const bytes = await handle.readFile();
|
|
25
|
-
if (bytes.length !== stat.size) throw new Error('附件在读取时发生变化,请重新上传。');
|
|
26
|
-
const mimeType = media.mediaMime(file) || mimeTypes[path.extname(file.name).slice(1).toLowerCase()] || 'application/octet-stream';
|
|
27
|
-
if (!/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/i.test(mimeType)) throw new Error('附件类型无效。');
|
|
28
|
-
return { name: file.name, mimeType, bytes };
|
|
29
|
-
} finally { await handle.close(); }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function readMessageUploads(body, uploadsRoot) {
|
|
33
|
-
const images = body.images ?? [], originals = body.originalImages ?? [], files = body.files ?? [];
|
|
34
|
-
if (![images, originals, files].every(Array.isArray) || images.length + originals.length > 4 || files.length + originals.length > MAX_FILE_COUNT) {
|
|
35
|
-
throw new Error('每条消息最多 4 张图片、4 个文件。');
|
|
36
|
-
}
|
|
37
|
-
const uploads = [];
|
|
38
|
-
let imageBytes = 0, fileBytes = 0;
|
|
39
|
-
for (const image of images) {
|
|
40
|
-
let file;
|
|
41
|
-
if (typeof image === 'string' && image.startsWith('data:')) {
|
|
42
|
-
validateInlineImage(image);
|
|
43
|
-
const mimeType = image.slice(5, image.indexOf(';'));
|
|
44
|
-
file = { name: `image.${mimeType.split('/')[1]}`, mimeType, bytes: Buffer.from(image.slice(image.indexOf(',') + 1), 'base64') };
|
|
45
|
-
} else {
|
|
46
|
-
if (typeof image !== 'string' || !/^[a-f0-9]{64}$/.test(image)) throw new Error('图片标识无效。');
|
|
47
|
-
let extension = 'webp';
|
|
48
|
-
try { await fs.access(path.join(uploadsRoot, `${image}.webp`)); }
|
|
49
|
-
catch (error) { if (error.code !== 'ENOENT') throw error; extension = 'png'; }
|
|
50
|
-
file = await localFile({ path: path.join(uploadsRoot, `${image}.${extension}`), name: `image.${extension}` }, uploadsRoot);
|
|
51
|
-
}
|
|
52
|
-
imageBytes += file.bytes.length;
|
|
53
|
-
if (imageBytes > 8 * 1024 * 1024) throw new Error('压缩图片合计不能超过 8 MiB。');
|
|
54
|
-
uploads.push(file);
|
|
55
|
-
}
|
|
56
|
-
for (const original of originals) {
|
|
57
|
-
if (typeof original !== 'string' || !/\.(png|jpe?g|webp|gif)$/i.test(original)) throw new Error('原图路径无效。');
|
|
58
|
-
}
|
|
59
|
-
for (const file of [...originals.map(value => ({ path: value, name: path.basename(value) })), ...files]) {
|
|
60
|
-
const uploaded = await localFile(file, path.join(uploadsRoot, 'incoming'));
|
|
61
|
-
fileBytes += uploaded.bytes.length;
|
|
62
|
-
if (fileBytes > MAX_TOTAL_BYTES) throw new Error('文件合计不能超过 40 MiB。');
|
|
63
|
-
uploads.push(uploaded);
|
|
64
|
-
}
|
|
65
|
-
for (const file of uploads) {
|
|
66
|
-
if (/^image\/(?:png|jpeg|webp|gif)$/.test(file.mimeType)) {
|
|
67
|
-
const metadata = await sharp(file.bytes).metadata();
|
|
68
|
-
file.width = metadata.width; file.height = metadata.height;
|
|
69
|
-
if (!file.width || !file.height) throw new Error('无法读取 图片尺寸。');
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (uploads.filter(file => file.width).length > 4) throw new Error('每条消息最多 4 张图片。');
|
|
73
|
-
return uploads;
|
|
74
|
-
}
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
import media from '../shared/code-browser.cjs';
|
|
5
|
+
import { validateInlineImage } from './message-images.mjs';
|
|
6
|
+
import { MAX_FILE_BYTES, MAX_FILE_COUNT, MAX_TOTAL_BYTES } from './file-upload-session.mjs';
|
|
7
|
+
|
|
8
|
+
const mimeTypes = { pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv',
|
|
9
|
+
json: 'application/json', zip: 'application/zip', doc: 'application/msword',
|
|
10
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
11
|
+
xls: 'application/vnd.ms-excel', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
12
|
+
ppt: 'application/vnd.ms-powerpoint', pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' };
|
|
13
|
+
|
|
14
|
+
async function localFile(file, directory) {
|
|
15
|
+
if (typeof file?.path !== 'string' || typeof file.name !== 'string' || !file.name || /[\r\n\0]/.test(file.name)) {
|
|
16
|
+
throw new Error('附件信息无效,请重新选择。');
|
|
17
|
+
}
|
|
18
|
+
const root = await fs.realpath(directory), resolved = await fs.realpath(file.path);
|
|
19
|
+
if (path.relative(root, path.dirname(resolved)) !== '') throw new Error('附件必须来自本次工作站上传目录。');
|
|
20
|
+
const handle = await fs.open(resolved, 'r');
|
|
21
|
+
try {
|
|
22
|
+
const stat = await handle.stat();
|
|
23
|
+
if (!stat.isFile() || stat.size < 1 || stat.size > MAX_FILE_BYTES) throw new Error('附件为空或超过 20 MiB。');
|
|
24
|
+
const bytes = await handle.readFile();
|
|
25
|
+
if (bytes.length !== stat.size) throw new Error('附件在读取时发生变化,请重新上传。');
|
|
26
|
+
const mimeType = media.mediaMime(file) || mimeTypes[path.extname(file.name).slice(1).toLowerCase()] || 'application/octet-stream';
|
|
27
|
+
if (!/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/i.test(mimeType)) throw new Error('附件类型无效。');
|
|
28
|
+
return { name: file.name, mimeType, bytes };
|
|
29
|
+
} finally { await handle.close(); }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function readMessageUploads(body, uploadsRoot) {
|
|
33
|
+
const images = body.images ?? [], originals = body.originalImages ?? [], files = body.files ?? [];
|
|
34
|
+
if (![images, originals, files].every(Array.isArray) || images.length + originals.length > 4 || files.length + originals.length > MAX_FILE_COUNT) {
|
|
35
|
+
throw new Error('每条消息最多 4 张图片、4 个文件。');
|
|
36
|
+
}
|
|
37
|
+
const uploads = [];
|
|
38
|
+
let imageBytes = 0, fileBytes = 0;
|
|
39
|
+
for (const image of images) {
|
|
40
|
+
let file;
|
|
41
|
+
if (typeof image === 'string' && image.startsWith('data:')) {
|
|
42
|
+
validateInlineImage(image);
|
|
43
|
+
const mimeType = image.slice(5, image.indexOf(';'));
|
|
44
|
+
file = { name: `image.${mimeType.split('/')[1]}`, mimeType, bytes: Buffer.from(image.slice(image.indexOf(',') + 1), 'base64') };
|
|
45
|
+
} else {
|
|
46
|
+
if (typeof image !== 'string' || !/^[a-f0-9]{64}$/.test(image)) throw new Error('图片标识无效。');
|
|
47
|
+
let extension = 'webp';
|
|
48
|
+
try { await fs.access(path.join(uploadsRoot, `${image}.webp`)); }
|
|
49
|
+
catch (error) { if (error.code !== 'ENOENT') throw error; extension = 'png'; }
|
|
50
|
+
file = await localFile({ path: path.join(uploadsRoot, `${image}.${extension}`), name: `image.${extension}` }, uploadsRoot);
|
|
51
|
+
}
|
|
52
|
+
imageBytes += file.bytes.length;
|
|
53
|
+
if (imageBytes > 8 * 1024 * 1024) throw new Error('压缩图片合计不能超过 8 MiB。');
|
|
54
|
+
uploads.push(file);
|
|
55
|
+
}
|
|
56
|
+
for (const original of originals) {
|
|
57
|
+
if (typeof original !== 'string' || !/\.(png|jpe?g|webp|gif)$/i.test(original)) throw new Error('原图路径无效。');
|
|
58
|
+
}
|
|
59
|
+
for (const file of [...originals.map(value => ({ path: value, name: path.basename(value) })), ...files]) {
|
|
60
|
+
const uploaded = await localFile(file, path.join(uploadsRoot, 'incoming'));
|
|
61
|
+
fileBytes += uploaded.bytes.length;
|
|
62
|
+
if (fileBytes > MAX_TOTAL_BYTES) throw new Error('文件合计不能超过 40 MiB。');
|
|
63
|
+
uploads.push(uploaded);
|
|
64
|
+
}
|
|
65
|
+
for (const file of uploads) {
|
|
66
|
+
if (/^image\/(?:png|jpeg|webp|gif)$/.test(file.mimeType)) {
|
|
67
|
+
const metadata = await sharp(file.bytes).metadata();
|
|
68
|
+
file.width = metadata.width; file.height = metadata.height;
|
|
69
|
+
if (!file.width || !file.height) throw new Error('无法读取 图片尺寸。');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (uploads.filter(file => file.width).length > 4) throw new Error('每条消息最多 4 张图片。');
|
|
73
|
+
return uploads;
|
|
74
|
+
}
|
package/dist/src/onboarding.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export function phonePairingSteps(_transportMode, paired = false) {
|
|
2
|
-
return paired ? ['已配对设备可直接连接。添加设备:relax pair']
|
|
3
|
-
: ['生成配对二维码:relax pair', '用 Code Relax App 扫码添加工作站。'];
|
|
4
|
-
}
|
|
5
|
-
export const desktopSetupSteps = [
|
|
6
|
-
'工作站服务和 Codex 内核已就绪,已启用登录自启。',
|
|
7
|
-
'生成配对二维码:relax pair',
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
export function pairingDisplayText(nextSteps) {
|
|
11
|
-
return ['Code Relax 工作站已启动。', ...nextSteps].join('\n');
|
|
12
|
-
}
|
|
1
|
+
export function phonePairingSteps(_transportMode, paired = false) {
|
|
2
|
+
return paired ? ['已配对设备可直接连接。添加设备:relax pair']
|
|
3
|
+
: ['生成配对二维码:relax pair', '用 Code Relax App 扫码添加工作站。'];
|
|
4
|
+
}
|
|
5
|
+
export const desktopSetupSteps = [
|
|
6
|
+
'工作站服务和 Codex 内核已就绪,已启用登录自启。',
|
|
7
|
+
'生成配对二维码:relax pair',
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
export function pairingDisplayText(nextSteps) {
|
|
11
|
+
return ['Code Relax 工作站已启动。', ...nextSteps].join('\n');
|
|
12
|
+
}
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import http2 from 'node:http2';
|
|
2
|
-
import pricing from '../shared/plan-pricing.cjs';
|
|
3
|
-
|
|
4
|
-
export async function readPlanPricing(client) {
|
|
5
|
-
await client.connect();
|
|
6
|
-
const token = async () => (await client.request('getAuthStatus', { includeToken: true, refreshToken: false })).authToken;
|
|
7
|
-
const authToken = await token();
|
|
8
|
-
if (!authToken) return null;
|
|
9
|
-
const identity = value => {
|
|
10
|
-
const auth = JSON.parse(Buffer.from(value.split('.')[1], 'base64url'))['https://api.openai.com/auth'];
|
|
11
|
-
return JSON.stringify([auth?.chatgpt_account_id, auth?.chatgpt_user_id]);
|
|
12
|
-
};
|
|
13
|
-
const initialIdentity = identity(authToken), accountId = JSON.parse(initialIdentity)[0];
|
|
14
|
-
if (!accountId) return null;
|
|
15
|
-
const session = http2.connect('https://chatgpt.com');
|
|
16
|
-
session.on('error', () => {}); // Each stream reports its own request error.
|
|
17
|
-
try {
|
|
18
|
-
const get = route => new Promise((resolve, reject) => {
|
|
19
|
-
const request = session.request({ ':method': 'GET', ':path': '/backend-api' + route,
|
|
20
|
-
authorization: `Bearer ${authToken}`, 'chatgpt-account-id': accountId,
|
|
21
|
-
'user-agent': 'Code-Relax/0.1.0', accept: 'application/json' });
|
|
22
|
-
let body = '', status, size = 0, ended = false;
|
|
23
|
-
const timer = setTimeout(() => request.destroy(new Error('套餐费用查询超时。')), 15000);
|
|
24
|
-
request.setEncoding('utf8');
|
|
25
|
-
request.on('response', headers => { status = headers[':status']; });
|
|
26
|
-
request.on('data', chunk => {
|
|
27
|
-
size += Buffer.byteLength(chunk);
|
|
28
|
-
if (size > 1024 * 1024) request.destroy(new Error('套餐费用响应过大。'));
|
|
29
|
-
else body += chunk;
|
|
30
|
-
});
|
|
31
|
-
request.on('error', reject);
|
|
32
|
-
request.on('close', () => { clearTimeout(timer); if (!ended) reject(new Error('套餐费用连接已断开。')); });
|
|
33
|
-
request.on('end', () => {
|
|
34
|
-
ended = true;
|
|
35
|
-
try {
|
|
36
|
-
if (status !== 200) throw new Error(`套餐费用查询失败(HTTP ${status})。`);
|
|
37
|
-
resolve(JSON.parse(body));
|
|
38
|
-
} catch (error) { reject(error); }
|
|
39
|
-
});
|
|
40
|
-
request.end();
|
|
41
|
-
});
|
|
42
|
-
const value = await pricing.read(accountId, get);
|
|
43
|
-
const current = await token();
|
|
44
|
-
if (!current || identity(current) !== initialIdentity) throw new Error('套餐查询期间账号已变化。');
|
|
45
|
-
return value;
|
|
46
|
-
} finally { session.destroy(); }
|
|
47
|
-
}
|
|
1
|
+
import http2 from 'node:http2';
|
|
2
|
+
import pricing from '../shared/plan-pricing.cjs';
|
|
3
|
+
|
|
4
|
+
export async function readPlanPricing(client) {
|
|
5
|
+
await client.connect();
|
|
6
|
+
const token = async () => (await client.request('getAuthStatus', { includeToken: true, refreshToken: false })).authToken;
|
|
7
|
+
const authToken = await token();
|
|
8
|
+
if (!authToken) return null;
|
|
9
|
+
const identity = value => {
|
|
10
|
+
const auth = JSON.parse(Buffer.from(value.split('.')[1], 'base64url'))['https://api.openai.com/auth'];
|
|
11
|
+
return JSON.stringify([auth?.chatgpt_account_id, auth?.chatgpt_user_id]);
|
|
12
|
+
};
|
|
13
|
+
const initialIdentity = identity(authToken), accountId = JSON.parse(initialIdentity)[0];
|
|
14
|
+
if (!accountId) return null;
|
|
15
|
+
const session = http2.connect('https://chatgpt.com');
|
|
16
|
+
session.on('error', () => {}); // Each stream reports its own request error.
|
|
17
|
+
try {
|
|
18
|
+
const get = route => new Promise((resolve, reject) => {
|
|
19
|
+
const request = session.request({ ':method': 'GET', ':path': '/backend-api' + route,
|
|
20
|
+
authorization: `Bearer ${authToken}`, 'chatgpt-account-id': accountId,
|
|
21
|
+
'user-agent': 'Code-Relax/0.1.0', accept: 'application/json' });
|
|
22
|
+
let body = '', status, size = 0, ended = false;
|
|
23
|
+
const timer = setTimeout(() => request.destroy(new Error('套餐费用查询超时。')), 15000);
|
|
24
|
+
request.setEncoding('utf8');
|
|
25
|
+
request.on('response', headers => { status = headers[':status']; });
|
|
26
|
+
request.on('data', chunk => {
|
|
27
|
+
size += Buffer.byteLength(chunk);
|
|
28
|
+
if (size > 1024 * 1024) request.destroy(new Error('套餐费用响应过大。'));
|
|
29
|
+
else body += chunk;
|
|
30
|
+
});
|
|
31
|
+
request.on('error', reject);
|
|
32
|
+
request.on('close', () => { clearTimeout(timer); if (!ended) reject(new Error('套餐费用连接已断开。')); });
|
|
33
|
+
request.on('end', () => {
|
|
34
|
+
ended = true;
|
|
35
|
+
try {
|
|
36
|
+
if (status !== 200) throw new Error(`套餐费用查询失败(HTTP ${status})。`);
|
|
37
|
+
resolve(JSON.parse(body));
|
|
38
|
+
} catch (error) { reject(error); }
|
|
39
|
+
});
|
|
40
|
+
request.end();
|
|
41
|
+
});
|
|
42
|
+
const value = await pricing.read(accountId, get);
|
|
43
|
+
const current = await token();
|
|
44
|
+
if (!current || identity(current) !== initialIdentity) throw new Error('套餐查询期间账号已变化。');
|
|
45
|
+
return value;
|
|
46
|
+
} finally { session.destroy(); }
|
|
47
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# 被控主机平台适配
|
|
2
|
-
|
|
3
|
-
当前只实现 Windows Codex:
|
|
4
|
-
|
|
5
|
-
- `windows/runtime-paths.mjs`:固定服务、用户状态、Codex 数据、附件和长期凭据路径。
|
|
6
|
-
- `windows/service-host.mjs`:网卡、Bridge 进程环境和旧状态迁移。
|
|
7
|
-
- `windows/desktop-host.mjs`、PowerShell 脚本:普通/共享 Desktop、快捷方式和隐藏控制台启动。
|
|
8
|
-
- `windows/credential-store.mjs/.ps1`:私有中继电脑凭据的 DPAPI 与 ACL。
|
|
9
|
-
|
|
10
|
-
Windows 专有脚本兼容系统自带 PowerShell 5.1。通用生命周期使用 Node 接口;系统检测和凭据保护不扩散到业务代码。
|
|
11
|
-
|
|
12
|
-
macOS、Linux 目录仅保留规划说明,尚无路径、IPC、进程或安装实现。接入排在控制端扩展之后,并需分别验证 Codex Desktop/app-server 的可用连接方式;只有独立 app-server 时不能宣称接管 Desktop 当前任务。
|
|
1
|
+
# 被控主机平台适配
|
|
2
|
+
|
|
3
|
+
当前只实现 Windows Codex:
|
|
4
|
+
|
|
5
|
+
- `windows/runtime-paths.mjs`:固定服务、用户状态、Codex 数据、附件和长期凭据路径。
|
|
6
|
+
- `windows/service-host.mjs`:网卡、Bridge 进程环境和旧状态迁移。
|
|
7
|
+
- `windows/desktop-host.mjs`、PowerShell 脚本:普通/共享 Desktop、快捷方式和隐藏控制台启动。
|
|
8
|
+
- `windows/credential-store.mjs/.ps1`:私有中继电脑凭据的 DPAPI 与 ACL。
|
|
9
|
+
|
|
10
|
+
Windows 专有脚本兼容系统自带 PowerShell 5.1。通用生命周期使用 Node 接口;系统检测和凭据保护不扩散到业务代码。
|
|
11
|
+
|
|
12
|
+
macOS、Linux 目录仅保留规划说明,尚无路径、IPC、进程或安装实现。接入排在控制端扩展之后,并需分别验证 Codex Desktop/app-server 的可用连接方式;只有独立 app-server 时不能宣称接管 Desktop 当前任务。
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { execFile } from 'node:child_process';
|
|
4
|
-
import { promisify } from 'node:util';
|
|
5
|
-
|
|
6
|
-
const exec = promisify(execFile);
|
|
7
|
-
const requiredMethods = ['thread/start', 'thread/resume', 'thread/list', 'thread/turns/list',
|
|
8
|
-
'thread/items/list', 'turn/start', 'turn/steer', 'turn/interrupt', 'model/list', 'project/list',
|
|
9
|
-
'project/create', 'project/update', 'project/move',
|
|
10
|
-
'fs/getMetadata', 'fs/readDirectory', 'fs/createDirectory', 'command/exec',
|
|
11
|
-
'thread/settings/update', 'configRequirements/read', 'thread/queue/list', 'thread/queue/add',
|
|
12
|
-
'thread/queue/delete', 'thread/queue/start', 'thread/read', 'thread/unsubscribe', 'config/read', 'account/rateLimits/read'];
|
|
13
|
-
|
|
14
|
-
export function compareVersions(a, b) {
|
|
15
|
-
const parse = v => v.split(/[.-]/).map(part => /^\d+$/.test(part) ? Number(part) : part);
|
|
16
|
-
const aa = parse(a), bb = parse(b);
|
|
17
|
-
for (let i = 0; i < Math.max(aa.length, bb.length); i++) {
|
|
18
|
-
if (aa[i] === bb[i]) continue;
|
|
19
|
-
if (aa[i] === undefined) return 1;
|
|
20
|
-
if (bb[i] === undefined) return -1;
|
|
21
|
-
return typeof aa[i] === 'number' && typeof bb[i] === 'number' ? aa[i] - bb[i] : String(aa[i]).localeCompare(String(bb[i]));
|
|
22
|
-
}
|
|
23
|
-
return 0;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function verifyProtocol(executable, stateRoot) {
|
|
27
|
-
const directory = fs.mkdtempSync(path.join(stateRoot, 'cli-schema-'));
|
|
28
|
-
try {
|
|
29
|
-
await exec(executable, ['app-server', 'generate-json-schema', '--experimental', '--out', directory],
|
|
30
|
-
{ windowsHide: true, timeout: 30000, maxBuffer: 4 * 1024 * 1024 });
|
|
31
|
-
const request = fs.readFileSync(path.join(directory, 'ClientRequest.json'), 'utf8');
|
|
32
|
-
const missing = requiredMethods.filter(method => !request.includes(`"${method}"`));
|
|
33
|
-
const turns = fs.readFileSync(path.join(directory, 'v2/ThreadTurnsListParams.json'), 'utf8');
|
|
34
|
-
if (!turns.includes('"itemsView"') || !turns.includes('"summary"')) missing.push('turn summary pagination');
|
|
35
|
-
if (missing.length) throw new Error(`缺少协议能力:${missing.join(', ')}`);
|
|
36
|
-
} finally {
|
|
37
|
-
const remove = dir => {
|
|
38
|
-
for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
39
|
-
const file = path.join(dir, item.name);
|
|
40
|
-
if (item.isDirectory()) remove(file); else fs.unlinkSync(file);
|
|
41
|
-
}
|
|
42
|
-
fs.rmdirSync(dir);
|
|
43
|
-
};
|
|
44
|
-
remove(directory);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
|
|
6
|
+
const exec = promisify(execFile);
|
|
7
|
+
const requiredMethods = ['thread/start', 'thread/resume', 'thread/list', 'thread/turns/list',
|
|
8
|
+
'thread/items/list', 'turn/start', 'turn/steer', 'turn/interrupt', 'model/list', 'project/list',
|
|
9
|
+
'project/create', 'project/update', 'project/move',
|
|
10
|
+
'fs/getMetadata', 'fs/readDirectory', 'fs/createDirectory', 'command/exec',
|
|
11
|
+
'thread/settings/update', 'configRequirements/read', 'thread/queue/list', 'thread/queue/add',
|
|
12
|
+
'thread/queue/delete', 'thread/queue/start', 'thread/read', 'thread/unsubscribe', 'config/read', 'account/rateLimits/read'];
|
|
13
|
+
|
|
14
|
+
export function compareVersions(a, b) {
|
|
15
|
+
const parse = v => v.split(/[.-]/).map(part => /^\d+$/.test(part) ? Number(part) : part);
|
|
16
|
+
const aa = parse(a), bb = parse(b);
|
|
17
|
+
for (let i = 0; i < Math.max(aa.length, bb.length); i++) {
|
|
18
|
+
if (aa[i] === bb[i]) continue;
|
|
19
|
+
if (aa[i] === undefined) return 1;
|
|
20
|
+
if (bb[i] === undefined) return -1;
|
|
21
|
+
return typeof aa[i] === 'number' && typeof bb[i] === 'number' ? aa[i] - bb[i] : String(aa[i]).localeCompare(String(bb[i]));
|
|
22
|
+
}
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function verifyProtocol(executable, stateRoot) {
|
|
27
|
+
const directory = fs.mkdtempSync(path.join(stateRoot, 'cli-schema-'));
|
|
28
|
+
try {
|
|
29
|
+
await exec(executable, ['app-server', 'generate-json-schema', '--experimental', '--out', directory],
|
|
30
|
+
{ windowsHide: true, timeout: 30000, maxBuffer: 4 * 1024 * 1024 });
|
|
31
|
+
const request = fs.readFileSync(path.join(directory, 'ClientRequest.json'), 'utf8');
|
|
32
|
+
const missing = requiredMethods.filter(method => !request.includes(`"${method}"`));
|
|
33
|
+
const turns = fs.readFileSync(path.join(directory, 'v2/ThreadTurnsListParams.json'), 'utf8');
|
|
34
|
+
if (!turns.includes('"itemsView"') || !turns.includes('"summary"')) missing.push('turn summary pagination');
|
|
35
|
+
if (missing.length) throw new Error(`缺少协议能力:${missing.join(', ')}`);
|
|
36
|
+
} finally {
|
|
37
|
+
const remove = dir => {
|
|
38
|
+
for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
39
|
+
const file = path.join(dir, item.name);
|
|
40
|
+
if (item.isDirectory()) remove(file); else fs.unlinkSync(file);
|
|
41
|
+
}
|
|
42
|
+
fs.rmdirSync(dir);
|
|
43
|
+
};
|
|
44
|
+
remove(directory);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { platformModule } from './select.mjs';
|
|
2
|
-
const adapter = await import(platformModule('codex-runtime'));
|
|
3
|
-
export const { discoverCodex, resolveCodexRuntime, runtimeFilesAvailable, codeModeHostPath, runtimeFingerprint, pruneRetainedDesktopRuntimes } = adapter;
|
|
1
|
+
import { platformModule } from './select.mjs';
|
|
2
|
+
const adapter = await import(platformModule('codex-runtime'));
|
|
3
|
+
export const { discoverCodex, resolveCodexRuntime, runtimeFilesAvailable, codeModeHostPath, runtimeFingerprint, pruneRetainedDesktopRuntimes } = adapter;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { platformModule } from './select.mjs';
|
|
2
|
-
const adapter = await import(platformModule('credential-store'));
|
|
3
|
-
export const { credentialStore } = adapter;
|
|
1
|
+
import { platformModule } from './select.mjs';
|
|
2
|
+
const adapter = await import(platformModule('credential-store'));
|
|
3
|
+
export const { credentialStore } = adapter;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { platformModule } from './select.mjs';
|
|
2
|
-
const adapter = await import(platformModule('desktop-host'));
|
|
3
|
-
export const { desktopHost, resolveStateRoot, removeDesktopShortcut, residentStartup } = adapter;
|
|
1
|
+
import { platformModule } from './select.mjs';
|
|
2
|
+
const adapter = await import(platformModule('desktop-host'));
|
|
3
|
+
export const { desktopHost, resolveStateRoot, removeDesktopShortcut, residentStartup } = adapter;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { platformModule } from './select.mjs';
|
|
2
|
-
const adapter = await import(platformModule('desktop-project-registration'));
|
|
3
|
-
export const { withDesktopProjectRegistration } = adapter;
|
|
1
|
+
import { platformModule } from './select.mjs';
|
|
2
|
+
const adapter = await import(platformModule('desktop-project-registration'));
|
|
3
|
+
export const { withDesktopProjectRegistration } = adapter;
|