@walkhi/code-relax 0.1.0-beta.26 → 0.1.0-beta.28
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 +80 -67
- package/dist/bin/codex-remote.mjs +286 -239
- package/dist/shared/app-server-events.cjs +102 -16
- package/dist/shared/code-browser.cjs +159 -0
- package/dist/shared/file-changes.cjs +31 -0
- package/dist/shared/file-citations.cjs +117 -0
- package/dist/shared/file-mentions.cjs +80 -35
- package/dist/shared/message-delivery.cjs +38 -0
- package/dist/shared/p2p-file-channel.cjs +74 -0
- package/dist/shared/server-requests.cjs +149 -41
- package/dist/shared/service-endpoints.cjs +11 -11
- package/dist/shared/timeline-sync.cjs +173 -156
- package/dist/skills/code-relax/SKILL.md +2 -1
- package/dist/src/bounded-process-log.mjs +63 -0
- package/dist/src/celsius-client.mjs +107 -0
- package/dist/src/code-browser.mjs +101 -0
- package/dist/src/desktop-launcher.mjs +30 -30
- package/dist/src/dots-approval-timeline.mjs +99 -0
- package/dist/src/dots-avatar.mjs +42 -0
- package/dist/src/dots-chat-projection.mjs +50 -0
- package/dist/src/dots-chat-server.mjs +412 -0
- package/dist/src/dots-message-images.mjs +319 -0
- package/dist/src/dots-profile.mjs +77 -0
- package/dist/src/dots-room-activity.mjs +94 -0
- package/dist/src/dots-room-client.mjs +135 -0
- package/dist/src/dots-room-projection.mjs +94 -0
- package/dist/src/dots-schedules.mjs +66 -0
- package/dist/src/dots-summary-tasks.mjs +32 -0
- package/dist/src/dots-task-catalog.mjs +411 -0
- package/dist/src/file-transfer-range.mjs +22 -0
- package/dist/src/file-upload-session.mjs +130 -124
- package/dist/src/hosted-history-app-server.mjs +103 -0
- package/dist/src/hosted-history-cache.mjs +113 -0
- package/dist/src/lan-socket-server.mjs +205 -160
- package/dist/src/managed-app-server.mjs +241 -238
- package/dist/src/message-upload-files.mjs +74 -0
- package/dist/src/platform/windows/activate-desktop.ps1 +48 -0
- package/dist/src/platform/windows/codex-runtime.mjs +253 -251
- package/dist/src/platform/windows/desktop-host.mjs +38 -32
- package/dist/src/platform/windows/desktop-info.ps1 +2 -2
- package/dist/src/platform/windows/desktop-project-registration.mjs +153 -0
- package/dist/src/platform/windows/dots-profile.mjs +49 -0
- package/dist/src/platform/windows/launch-worker.mjs +41 -42
- package/dist/src/platform/windows/project-locations.mjs +16 -0
- package/dist/src/project-create.mjs +119 -0
- package/dist/src/project-management.mjs +69 -0
- package/dist/src/self-relay/account-connections.mjs +52 -0
- package/dist/src/self-relay/admin-page.mjs +80 -80
- package/dist/src/self-relay/admin-style.mjs +14 -9
- package/dist/src/self-relay/connector.mjs +22 -4
- package/dist/src/self-relay/file-wire.mjs +13 -0
- package/dist/src/self-relay/p2p-probe.mjs +152 -123
- package/dist/src/self-relay/rtc-file-bridge.mjs +41 -0
- package/dist/src/self-relay/server.mjs +894 -849
- package/dist/src/server.mjs +463 -148
- package/dist/src/service-doctor.mjs +72 -61
- package/dist/src/service-lifecycle.mjs +51 -43
- package/dist/src/shared-app-server.mjs +166 -107
- package/dist/src/shared-catalog.mjs +132 -40
- package/dist/src/shared-thread-stream.mjs +103 -25
- package/dist/src/usage-reset.mjs +17 -0
- package/dist/src/workstation-downloads.mjs +120 -0
- package/package.json +1 -1
|
@@ -1,123 +1,152 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import P2pDiagnostics from '../../shared/p2p-diagnostics.cjs';
|
|
3
|
-
import P2pDataFramer from '../../shared/p2p-data-channel.cjs';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import P2pDiagnostics from '../../shared/p2p-diagnostics.cjs';
|
|
3
|
+
import P2pDataFramer from '../../shared/p2p-data-channel.cjs';
|
|
4
|
+
import FileChannel from '../../shared/p2p-file-channel.cjs';
|
|
5
|
+
|
|
6
|
+
const PROBE_UDP_PORT_RANGE = Object.freeze({ begin: 42000, end: 42031 });
|
|
7
|
+
const HIGH_WATER = 1024 * 1024;
|
|
8
|
+
const LOW_WATER = 512 * 1024;
|
|
9
|
+
const MAX_QUEUED = 16 * 1024 * 1024;
|
|
10
|
+
|
|
11
|
+
export async function createP2pProbe({ signal, report = () => {}, onData = () => {}, onFileChannel, iceServers = [] }) {
|
|
12
|
+
if (!validProbeIceServers(iceServers)) throw new Error('Invalid probe STUN configuration');
|
|
13
|
+
const { default: rtc } = await import('node-datachannel');
|
|
14
|
+
const peer = new rtc.PeerConnection('code-relax-probe', {
|
|
15
|
+
iceServers,
|
|
16
|
+
maxMessageSize: 65536,
|
|
17
|
+
portRangeBegin: PROBE_UDP_PORT_RANGE.begin,
|
|
18
|
+
portRangeEnd: PROBE_UDP_PORT_RANGE.end
|
|
19
|
+
});
|
|
20
|
+
const diagnostic = new P2pDiagnostics();
|
|
21
|
+
const framer = new P2pDataFramer();
|
|
22
|
+
const dataPrefix = randomUUID().replaceAll('-', '');
|
|
23
|
+
let channel, closed = false, answered = false, candidates = [], verified = false, remoteDirect = false;
|
|
24
|
+
let pingReceived = false, pongReceived = false, dataSerial = 0, queuedBytes = 0;
|
|
25
|
+
const outbound = [];
|
|
26
|
+
const files = new Map();
|
|
27
|
+
const nonce = randomUUID();
|
|
28
|
+
const timer = setTimeout(() => finish(verified ? 'closed' : 'timeout'), 15000);
|
|
29
|
+
function finish(state) {
|
|
30
|
+
if (closed) return;
|
|
31
|
+
closed = true; clearTimeout(timer); candidates = []; queuedBytes = 0; outbound.length = 0; framer.reset();
|
|
32
|
+
for (const socket of files.values()) socket.close(1011);
|
|
33
|
+
files.clear();
|
|
34
|
+
const summary = diagnostic.snapshot(state);
|
|
35
|
+
channel?.close(); peer.close(); report(state, summary);
|
|
36
|
+
}
|
|
37
|
+
function verify() {
|
|
38
|
+
if (verified || !pingReceived || !pongReceived) return;
|
|
39
|
+
verified = true; clearTimeout(timer);
|
|
40
|
+
report(remoteDirect ? 'direct' : 'verified', diagnostic.snapshot('verified'));
|
|
41
|
+
pump();
|
|
42
|
+
for (const raw of files.keys()) activateFile(raw);
|
|
43
|
+
}
|
|
44
|
+
function activateFile(raw) {
|
|
45
|
+
const socket = files.get(raw);
|
|
46
|
+
if (!socket || socket.readyState !== 0 || !raw.isOpen() || !verified || !remoteDirect || closed) return;
|
|
47
|
+
socket.opened();
|
|
48
|
+
raw.sendMessage('{"type":"rtc-file-ready"}');
|
|
49
|
+
}
|
|
50
|
+
peer.onDataChannel(raw => {
|
|
51
|
+
if (closed || !onFileChannel || raw.getLabel() !== 'code-relax-file-v1' || files.size >= 4) { raw.close(); return; }
|
|
52
|
+
const socket = new FileChannel({ buffered: () => raw.bufferedAmount(),
|
|
53
|
+
send: value => typeof value === 'string' ? raw.sendMessage(value) : raw.sendMessageBinary(value), close: () => raw.close() });
|
|
54
|
+
files.set(raw, socket);
|
|
55
|
+
socket.addEventListener('close', () => files.delete(raw));
|
|
56
|
+
try { onFileChannel(socket); }
|
|
57
|
+
catch { socket.close(1011); return; }
|
|
58
|
+
raw.setBufferedAmountLowThreshold(128 * 1024);
|
|
59
|
+
raw.onBufferedAmountLow(() => socket.pump());
|
|
60
|
+
raw.onOpen(() => activateFile(raw));
|
|
61
|
+
raw.onClosed(() => socket.close(1011)); raw.onError(() => socket.close(1011));
|
|
62
|
+
raw.onMessage(value => {
|
|
63
|
+
if (!verified || !remoteDirect || closed || socket.readyState !== 1) { socket.close(1008); return; }
|
|
64
|
+
socket.receive(value);
|
|
65
|
+
});
|
|
66
|
+
activateFile(raw);
|
|
67
|
+
});
|
|
68
|
+
function pump() {
|
|
69
|
+
if (!verified || !remoteDirect || closed || !channel?.isOpen()) return;
|
|
70
|
+
try {
|
|
71
|
+
while (outbound.length && channel.bufferedAmount() < HIGH_WATER) {
|
|
72
|
+
const item = outbound[0], frame = framer.frame(item);
|
|
73
|
+
if (!channel.sendMessage(frame)) return;
|
|
74
|
+
if (framer.commit(item)) { outbound.shift(); queuedBytes -= item.bytes; }
|
|
75
|
+
}
|
|
76
|
+
} catch { diagnostic.error = 'channel'; finish('failed'); }
|
|
77
|
+
}
|
|
78
|
+
function sendData(payload) {
|
|
79
|
+
if (!verified || !remoteDirect || closed || !channel?.isOpen()) return false;
|
|
80
|
+
let item;
|
|
81
|
+
try { item = framer.create(payload, `${dataPrefix}-${++dataSerial}`); }
|
|
82
|
+
catch { return false; }
|
|
83
|
+
if (queuedBytes + item.bytes > MAX_QUEUED) return false;
|
|
84
|
+
outbound.push(item); queuedBytes += item.bytes; pump(); return true;
|
|
85
|
+
}
|
|
86
|
+
peer.onIceStateChange(state => { if (!closed) diagnostic.ice = state; });
|
|
87
|
+
peer.onGatheringStateChange(state => { if (!closed) diagnostic.gathering = state; });
|
|
88
|
+
peer.onStateChange(state => { if (!closed && (state === 'failed' || state === 'closed')) { diagnostic.error = 'peer'; finish('failed'); } });
|
|
89
|
+
peer.onLocalDescription((sdp, type) => { if (!closed) { diagnostic.offer = type === 'offer'; signal({ kind: type, sdp, iceServers, direct: 1 }); } });
|
|
90
|
+
peer.onLocalCandidate((candidate, mid) => { if (!closed) { diagnostic.candidate('local', candidate); signal({ kind: 'candidate', candidate, mid }); } });
|
|
91
|
+
channel = peer.createDataChannel('code-relax-probe-v1');
|
|
92
|
+
channel.setBufferedAmountLowThreshold(LOW_WATER);
|
|
93
|
+
channel.onBufferedAmountLow(pump);
|
|
94
|
+
channel.onOpen(() => { if (!closed) { diagnostic.open = true; channel.sendMessage(JSON.stringify({ type: 'ping', nonce })); } });
|
|
95
|
+
channel.onError(() => { if (!closed) diagnostic.error = 'channel'; finish('failed'); });
|
|
96
|
+
channel.onClosed(() => finish('closed'));
|
|
97
|
+
channel.onMessage(value => {
|
|
98
|
+
try {
|
|
99
|
+
if (closed || typeof value !== 'string' || value.length > 65536) throw new Error('Invalid P2P frame');
|
|
100
|
+
const message = JSON.parse(value);
|
|
101
|
+
const data = framer.accept(message);
|
|
102
|
+
if (data.handled) {
|
|
103
|
+
if (!verified || !remoteDirect) throw new Error('P2P transport not verified');
|
|
104
|
+
if (data.complete) onData(data.payload);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (message.type === 'ping' && /^[\w-]{16,128}$/.test(message.nonce)) {
|
|
108
|
+
diagnostic.ping = true; pingReceived = true;
|
|
109
|
+
channel.sendMessage(JSON.stringify({ type: 'pong', nonce: message.nonce }));
|
|
110
|
+
} else if (message.type === 'pong' && message.nonce === nonce) {
|
|
111
|
+
diagnostic.pong = true; pongReceived = true;
|
|
112
|
+
} else throw new Error('Invalid P2P response');
|
|
113
|
+
verify();
|
|
114
|
+
} catch { diagnostic.error = 'frame'; finish('failed'); }
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
receive(message) {
|
|
118
|
+
if (closed) return;
|
|
119
|
+
if (message.kind === 'answer' && !answered) {
|
|
120
|
+
try { peer.setRemoteDescription(message.sdp, 'answer'); }
|
|
121
|
+
catch { diagnostic.error = 'description'; finish('failed'); return; }
|
|
122
|
+
answered = true; remoteDirect = message.direct === 1; diagnostic.answer = true;
|
|
123
|
+
for (const item of candidates) peer.addRemoteCandidate(item.candidate, item.mid);
|
|
124
|
+
candidates = [];
|
|
125
|
+
} else if (message.kind === 'candidate') {
|
|
126
|
+
diagnostic.candidate('remote', message.candidate);
|
|
127
|
+
if (answered) peer.addRemoteCandidate(message.candidate, message.mid);
|
|
128
|
+
else if (candidates.length < 64) candidates.push(message);
|
|
129
|
+
else finish('failed');
|
|
130
|
+
} else if (message.kind === 'close') finish('closed');
|
|
131
|
+
else finish('failed');
|
|
132
|
+
},
|
|
133
|
+
send: sendData,
|
|
134
|
+
close() { finish('closed'); }
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function validProbeSignal(value) {
|
|
139
|
+
if (!value || value.version !== 1 || !/^[\w-]{16,128}$/.test(value.probeId || '')) return false;
|
|
140
|
+
if (['start', 'close'].includes(value.kind)) return true;
|
|
141
|
+
if (['offer', 'answer'].includes(value.kind)) return typeof value.sdp === 'string' && value.sdp.length <= 65536 &&
|
|
142
|
+
(value.direct === undefined || value.direct === 0 || value.direct === 1) &&
|
|
143
|
+
(value.iceServers === undefined || validProbeIceServers(value.iceServers));
|
|
144
|
+
if (value.kind === 'candidate') return typeof value.candidate === 'string' && value.candidate.length <= 2048 &&
|
|
145
|
+
typeof value.mid === 'string' && value.mid.length <= 64;
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function validProbeIceServers(value) {
|
|
150
|
+
return Array.isArray(value) && value.length <= 4 && value.every(item =>
|
|
151
|
+
typeof item === 'string' && /^stun:[a-zA-Z0-9.-]+:[0-9]{1,5}$/.test(item) && Number(item.split(':').at(-1)) <= 65535);
|
|
152
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
import { loopbackUrl } from './wire.mjs';
|
|
3
|
+
|
|
4
|
+
// The RTC peer belongs to an authenticated relay chat session. Its teardown
|
|
5
|
+
// closes this channel on logout, expiry, revocation or signaling disconnect.
|
|
6
|
+
export function attachRtcFile(channel, connection, authorized) {
|
|
7
|
+
let local, authenticated = false;
|
|
8
|
+
const timer = setTimeout(() => channel.close(1008), 5000);
|
|
9
|
+
channel.addEventListener('close', () => { clearTimeout(timer); local?.terminate(); });
|
|
10
|
+
channel.onmessage = event => {
|
|
11
|
+
try {
|
|
12
|
+
if (!authorized()) throw new Error('File authorization expired');
|
|
13
|
+
if (!local) {
|
|
14
|
+
if (typeof event.data !== 'string') throw new Error('File authentication required');
|
|
15
|
+
const auth = JSON.parse(event.data);
|
|
16
|
+
if (auth.type !== 'auth' || !['upload', 'download'].includes(auth.purpose)) throw new Error('Invalid file purpose');
|
|
17
|
+
const url = new URL(connection.url); url.pathname = '/api/file-transfer';
|
|
18
|
+
loopbackUrl(url.href, '/api/file-transfer');
|
|
19
|
+
local = new WebSocket(url.href, { maxPayload: 512 * 1024, perMessageDeflate: false });
|
|
20
|
+
local.on('open', () => {
|
|
21
|
+
if (!authorized() || channel.readyState !== 1) { local.terminate(); return; }
|
|
22
|
+
local.send(JSON.stringify({ type: 'auth', token: connection.token, purpose: auth.purpose }));
|
|
23
|
+
});
|
|
24
|
+
local.on('message', (bytes, binary) => {
|
|
25
|
+
if (!authorized() || channel.readyState !== 1) { channel.close(4401); return; }
|
|
26
|
+
try {
|
|
27
|
+
if (!authenticated) {
|
|
28
|
+
if (binary || JSON.parse(bytes.toString()).type !== 'ready') throw new Error('File authentication failed');
|
|
29
|
+
authenticated = true; clearTimeout(timer);
|
|
30
|
+
}
|
|
31
|
+
channel.send(binary ? bytes : bytes.toString());
|
|
32
|
+
} catch { channel.close(1011); }
|
|
33
|
+
});
|
|
34
|
+
local.on('close', () => channel.close(1011)); local.on('error', () => channel.close(1011));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (!authenticated || local.readyState !== WebSocket.OPEN || local.bufferedAmount > 1024 * 1024) throw new Error('File channel unavailable');
|
|
38
|
+
local.send(typeof event.data === 'string' ? event.data : Buffer.from(event.data));
|
|
39
|
+
} catch { channel.close(1008); }
|
|
40
|
+
};
|
|
41
|
+
}
|