@walkhi/code-relax 0.1.0-beta.50 → 0.1.0-beta.51
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 +105 -105
- package/dist/bin/cleanup-accounts.mjs +63 -63
- package/dist/bin/codex-remote.mjs +403 -403
- package/dist/bin/publish-android-release.mjs +63 -63
- package/dist/bin/release-upload-server.mjs +190 -190
- package/dist/build-platform.json +1 -1
- package/dist/get-shared-service-url.ps1 +14 -14
- 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 +115 -115
- package/dist/shared/p2p-file-channel.cjs +87 -87
- package/dist/shared/server-requests.cjs +311 -311
- package/dist/shared/timeline-sync.cjs +173 -173
- package/dist/skills/code-relax/SKILL.md +22 -22
- package/dist/src/bounded-process-log.mjs +63 -63
- package/dist/src/celsius-client.mjs +107 -107
- 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 +483 -483
- package/dist/src/dots-message-images.mjs +319 -319
- package/dist/src/dots-profile.mjs +77 -77
- package/dist/src/dots-room-activity.mjs +172 -172
- 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/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 +266 -266
- package/dist/src/managed-app-server.mjs +373 -373
- package/dist/src/managed-endpoint.mjs +25 -25
- package/dist/src/message-upload-files.mjs +109 -109
- package/dist/src/onboarding.mjs +12 -12
- package/dist/src/platform/README.md +1 -1
- package/dist/src/platform/codex-protocol.mjs +46 -46
- package/dist/src/platform/desktop-project-store.mjs +208 -208
- package/dist/src/platform/linux/desktop-host.mjs +87 -87
- package/dist/src/platform/linux/process.mjs +53 -53
- package/dist/src/platform/linux/system.mjs +50 -50
- package/dist/src/platform/linux/user-environment.mjs +9 -9
- package/dist/src/platform/macos/codex-runtime.mjs +125 -125
- package/dist/src/platform/macos/desktop-host.mjs +86 -86
- package/dist/src/platform/macos/user-environment.mjs +9 -9
- package/dist/src/platform/native-package.mjs +58 -58
- package/dist/src/platform/windows/activate-desktop.ps1 +49 -49
- 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-host.mjs +65 -65
- package/dist/src/platform/windows/desktop-info.ps1 +38 -38
- package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
- package/dist/src/platform/windows/desktop-shortcuts.ps1 +14 -14
- 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/project-locations.mjs +16 -16
- package/dist/src/platform/windows/service-host.mjs +35 -35
- package/dist/src/platform/windows/user-environment.mjs +52 -52
- package/dist/src/project-management.mjs +58 -58
- package/dist/src/runtime-discovery.mjs +47 -47
- package/dist/src/self-relay/account-cleanup.mjs +30 -30
- package/dist/src/self-relay/admin-page.mjs +80 -80
- package/dist/src/self-relay/admin-style.mjs +14 -14
- package/dist/src/self-relay/connector.mjs +439 -439
- package/dist/src/self-relay/demo.mjs +31 -31
- 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/p2p-probe.mjs +168 -168
- package/dist/src/self-relay/privacy-page.mjs +26 -26
- package/dist/src/self-relay/public-releases.mjs +67 -67
- package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
- package/dist/src/self-relay/usage-meter.mjs +141 -141
- package/dist/src/server.mjs +5 -4
- package/dist/src/service-doctor.mjs +148 -148
- package/dist/src/service-install.mjs +97 -97
- package/dist/src/service-lifecycle.mjs +139 -139
- package/dist/src/shared-app-server.mjs +666 -666
- package/dist/src/shared-catalog.mjs +132 -132
- package/dist/src/shared-thread-stream.mjs +165 -165
- package/dist/src/usage-reset.mjs +17 -17
- package/dist/src/workstation-downloads.mjs +120 -120
- package/package.json +4 -4
- package/tools/postinstall.mjs +186 -186
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
|
-
|
|
5
|
-
const threadBudget = 8 * 1024 * 1024;
|
|
6
|
-
const totalBudget = 128 * 1024 * 1024;
|
|
7
|
-
const digest = value => createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
8
|
-
|
|
9
|
-
// Store confirmed ranges, not connection tokens, execution context or image bytes.
|
|
10
|
-
export function historyCacheScope(identity, source, threadId) {
|
|
11
|
-
return identity?.accountId && threadId ? digest([identity.accountId, identity.userId || '', source, threadId]) : null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function completedHistory(turns) {
|
|
15
|
-
if (!turns?.length || turns.some(turn => !['completed', 'failed', 'interrupted'].includes(turn.status)
|
|
16
|
-
|| turn.cacheStub || turn.items?.some(item => item.dotsStreaming
|
|
17
|
-
|| item.type === 'imageGeneration' && !item.savedPath && item.result))) return false;
|
|
18
|
-
// Signed media addresses and embedded bytes must be reread/refreshed, not replayed from disk.
|
|
19
|
-
const media = value => value && typeof value === 'object' && Object.entries(value).some(([key, part]) =>
|
|
20
|
-
typeof part === 'string' && (/^data:/.test(part) || /[?&](?:X-Amz-|X-Goog-|signature=|sig=|token=)/i.test(part))
|
|
21
|
-
|| key === 'imageBytes' || media(part));
|
|
22
|
-
return !media(turns);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function turnMetadata(turn) {
|
|
26
|
-
return digest(Object.entries(turn || {}).filter(([key]) => !['items', 'itemPage', 'cacheStub'].includes(key))
|
|
27
|
-
.sort(([a], [b]) => a.localeCompare(b)));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export class HostedHistoryCache {
|
|
31
|
-
constructor(stateRoot, { report = error => console.error(`托管历史缓存失败:${error.message}`), maxThreadBytes = threadBudget, maxTotalBytes = totalBudget } = {}) {
|
|
32
|
-
this.directory = path.join(stateRoot, 'hosted-history');
|
|
33
|
-
this.report = report; this.maxThreadBytes = maxThreadBytes; this.maxTotalBytes = maxTotalBytes;
|
|
34
|
-
this.tail = Promise.resolve();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async record(scope) {
|
|
38
|
-
try {
|
|
39
|
-
const record = JSON.parse(await fs.readFile(path.join(this.directory, `${scope}.json`), 'utf8'));
|
|
40
|
-
if (record.version !== 1 || !record.entries || Array.isArray(record.entries) || typeof record.entries !== 'object') {
|
|
41
|
-
throw new Error('历史缓存结构无效,需重新读取云端历史。');
|
|
42
|
-
}
|
|
43
|
-
if (Object.values(record.entries).some(entry => !Number.isFinite(entry.at) || !entry.value
|
|
44
|
-
|| !Array.isArray(entry.value.turns) && !Array.isArray(entry.value.data))) {
|
|
45
|
-
throw new Error('历史缓存页结构无效,需重新读取云端历史。');
|
|
46
|
-
}
|
|
47
|
-
return record;
|
|
48
|
-
} catch (error) { if (error.code !== 'ENOENT') this.report(error); return { version: 1, entries: {} }; }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
run(operation, fallback) {
|
|
52
|
-
const work = this.tail.then(operation).catch(error => { this.report(error); return fallback; });
|
|
53
|
-
this.tail = work.then(() => {}); return work;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
get(scope, key) {
|
|
57
|
-
if (!scope) return Promise.resolve(null);
|
|
58
|
-
return this.run(async () => (await this.record(scope)).entries?.[key]?.value ?? null, null);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
put(scope, key, value) {
|
|
62
|
-
if (!scope) return Promise.resolve();
|
|
63
|
-
// Freeze before queuing; live events may mutate the source window during disk I/O.
|
|
64
|
-
const snapshot = structuredClone(value);
|
|
65
|
-
return this.run(async () => {
|
|
66
|
-
const record = await this.record(scope);
|
|
67
|
-
record.entries ||= {};
|
|
68
|
-
record.entries[key] = { at: Date.now(), value: snapshot };
|
|
69
|
-
let text = JSON.stringify(record);
|
|
70
|
-
for (const [oldKey] of Object.entries(record.entries).sort((a, b) => a[1].at - b[1].at)) {
|
|
71
|
-
if (Buffer.byteLength(text) <= this.maxThreadBytes) break;
|
|
72
|
-
delete record.entries[oldKey]; text = JSON.stringify(record);
|
|
73
|
-
}
|
|
74
|
-
await this.write(scope, text);
|
|
75
|
-
await this.prune(scope);
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
invalidate(scope, turnId) {
|
|
80
|
-
if (!scope) return Promise.resolve();
|
|
81
|
-
return this.run(async () => {
|
|
82
|
-
const record = await this.record(scope);
|
|
83
|
-
for (const [key, entry] of Object.entries(record.entries || {})) {
|
|
84
|
-
if (!turnId || entry.value.turnIds?.includes(turnId) || entry.value.turns?.some(turn => turn.id === turnId)
|
|
85
|
-
|| entry.value.thread?.turns?.some(turn => turn.id === turnId)) delete record.entries[key];
|
|
86
|
-
}
|
|
87
|
-
await this.write(scope, JSON.stringify(record));
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async write(scope, text) {
|
|
92
|
-
if (!/^[a-f0-9]{64}$/.test(scope)) throw new Error('历史缓存身份无效');
|
|
93
|
-
await fs.mkdir(this.directory, { recursive: true, mode: 0o700 });
|
|
94
|
-
const file = path.join(this.directory, `${scope}.json`), temporary = `${file}.tmp`;
|
|
95
|
-
try {
|
|
96
|
-
await fs.writeFile(temporary, text, { mode: 0o600 });
|
|
97
|
-
await fs.rename(temporary, file);
|
|
98
|
-
} finally {
|
|
99
|
-
try { await fs.unlink(temporary); } catch (error) { if (error.code !== 'ENOENT') this.report(error); }
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async prune(current) {
|
|
104
|
-
const files = await Promise.all((await fs.readdir(this.directory)).filter(name => /^[a-f0-9]{64}\.json$/.test(name))
|
|
105
|
-
.map(async name => ({ name, ...await fs.stat(path.join(this.directory, name)) })));
|
|
106
|
-
let bytes = files.reduce((sum, file) => sum + file.size, 0);
|
|
107
|
-
for (const file of files.sort((a, b) => a.mtimeMs - b.mtimeMs)) {
|
|
108
|
-
if (bytes <= this.maxTotalBytes) break;
|
|
109
|
-
if (file.name === `${current}.json`) continue;
|
|
110
|
-
await fs.unlink(path.join(this.directory, file.name)); bytes -= file.size;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
const threadBudget = 8 * 1024 * 1024;
|
|
6
|
+
const totalBudget = 128 * 1024 * 1024;
|
|
7
|
+
const digest = value => createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
8
|
+
|
|
9
|
+
// Store confirmed ranges, not connection tokens, execution context or image bytes.
|
|
10
|
+
export function historyCacheScope(identity, source, threadId) {
|
|
11
|
+
return identity?.accountId && threadId ? digest([identity.accountId, identity.userId || '', source, threadId]) : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function completedHistory(turns) {
|
|
15
|
+
if (!turns?.length || turns.some(turn => !['completed', 'failed', 'interrupted'].includes(turn.status)
|
|
16
|
+
|| turn.cacheStub || turn.items?.some(item => item.dotsStreaming
|
|
17
|
+
|| item.type === 'imageGeneration' && !item.savedPath && item.result))) return false;
|
|
18
|
+
// Signed media addresses and embedded bytes must be reread/refreshed, not replayed from disk.
|
|
19
|
+
const media = value => value && typeof value === 'object' && Object.entries(value).some(([key, part]) =>
|
|
20
|
+
typeof part === 'string' && (/^data:/.test(part) || /[?&](?:X-Amz-|X-Goog-|signature=|sig=|token=)/i.test(part))
|
|
21
|
+
|| key === 'imageBytes' || media(part));
|
|
22
|
+
return !media(turns);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function turnMetadata(turn) {
|
|
26
|
+
return digest(Object.entries(turn || {}).filter(([key]) => !['items', 'itemPage', 'cacheStub'].includes(key))
|
|
27
|
+
.sort(([a], [b]) => a.localeCompare(b)));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class HostedHistoryCache {
|
|
31
|
+
constructor(stateRoot, { report = error => console.error(`托管历史缓存失败:${error.message}`), maxThreadBytes = threadBudget, maxTotalBytes = totalBudget } = {}) {
|
|
32
|
+
this.directory = path.join(stateRoot, 'hosted-history');
|
|
33
|
+
this.report = report; this.maxThreadBytes = maxThreadBytes; this.maxTotalBytes = maxTotalBytes;
|
|
34
|
+
this.tail = Promise.resolve();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async record(scope) {
|
|
38
|
+
try {
|
|
39
|
+
const record = JSON.parse(await fs.readFile(path.join(this.directory, `${scope}.json`), 'utf8'));
|
|
40
|
+
if (record.version !== 1 || !record.entries || Array.isArray(record.entries) || typeof record.entries !== 'object') {
|
|
41
|
+
throw new Error('历史缓存结构无效,需重新读取云端历史。');
|
|
42
|
+
}
|
|
43
|
+
if (Object.values(record.entries).some(entry => !Number.isFinite(entry.at) || !entry.value
|
|
44
|
+
|| !Array.isArray(entry.value.turns) && !Array.isArray(entry.value.data))) {
|
|
45
|
+
throw new Error('历史缓存页结构无效,需重新读取云端历史。');
|
|
46
|
+
}
|
|
47
|
+
return record;
|
|
48
|
+
} catch (error) { if (error.code !== 'ENOENT') this.report(error); return { version: 1, entries: {} }; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
run(operation, fallback) {
|
|
52
|
+
const work = this.tail.then(operation).catch(error => { this.report(error); return fallback; });
|
|
53
|
+
this.tail = work.then(() => {}); return work;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get(scope, key) {
|
|
57
|
+
if (!scope) return Promise.resolve(null);
|
|
58
|
+
return this.run(async () => (await this.record(scope)).entries?.[key]?.value ?? null, null);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
put(scope, key, value) {
|
|
62
|
+
if (!scope) return Promise.resolve();
|
|
63
|
+
// Freeze before queuing; live events may mutate the source window during disk I/O.
|
|
64
|
+
const snapshot = structuredClone(value);
|
|
65
|
+
return this.run(async () => {
|
|
66
|
+
const record = await this.record(scope);
|
|
67
|
+
record.entries ||= {};
|
|
68
|
+
record.entries[key] = { at: Date.now(), value: snapshot };
|
|
69
|
+
let text = JSON.stringify(record);
|
|
70
|
+
for (const [oldKey] of Object.entries(record.entries).sort((a, b) => a[1].at - b[1].at)) {
|
|
71
|
+
if (Buffer.byteLength(text) <= this.maxThreadBytes) break;
|
|
72
|
+
delete record.entries[oldKey]; text = JSON.stringify(record);
|
|
73
|
+
}
|
|
74
|
+
await this.write(scope, text);
|
|
75
|
+
await this.prune(scope);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
invalidate(scope, turnId) {
|
|
80
|
+
if (!scope) return Promise.resolve();
|
|
81
|
+
return this.run(async () => {
|
|
82
|
+
const record = await this.record(scope);
|
|
83
|
+
for (const [key, entry] of Object.entries(record.entries || {})) {
|
|
84
|
+
if (!turnId || entry.value.turnIds?.includes(turnId) || entry.value.turns?.some(turn => turn.id === turnId)
|
|
85
|
+
|| entry.value.thread?.turns?.some(turn => turn.id === turnId)) delete record.entries[key];
|
|
86
|
+
}
|
|
87
|
+
await this.write(scope, JSON.stringify(record));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async write(scope, text) {
|
|
92
|
+
if (!/^[a-f0-9]{64}$/.test(scope)) throw new Error('历史缓存身份无效');
|
|
93
|
+
await fs.mkdir(this.directory, { recursive: true, mode: 0o700 });
|
|
94
|
+
const file = path.join(this.directory, `${scope}.json`), temporary = `${file}.tmp`;
|
|
95
|
+
try {
|
|
96
|
+
await fs.writeFile(temporary, text, { mode: 0o600 });
|
|
97
|
+
await fs.rename(temporary, file);
|
|
98
|
+
} finally {
|
|
99
|
+
try { await fs.unlink(temporary); } catch (error) { if (error.code !== 'ENOENT') this.report(error); }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async prune(current) {
|
|
104
|
+
const files = await Promise.all((await fs.readdir(this.directory)).filter(name => /^[a-f0-9]{64}\.json$/.test(name))
|
|
105
|
+
.map(async name => ({ name, ...await fs.stat(path.join(this.directory, name)) })));
|
|
106
|
+
let bytes = files.reduce((sum, file) => sum + file.size, 0);
|
|
107
|
+
for (const file of files.sort((a, b) => a.mtimeMs - b.mtimeMs)) {
|
|
108
|
+
if (bytes <= this.maxTotalBytes) break;
|
|
109
|
+
if (file.name === `${current}.json`) continue;
|
|
110
|
+
await fs.unlink(path.join(this.directory, file.name)); bytes -= file.size;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|