accelr8 3.8.2 → 4.0.0-next.1

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.
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/protocol.ts
4
+ var VERSION = true ? "4.0.0-next.1" : "4.0.0-next.1";
5
+ var PROTO = 4;
6
+ var PORT = 8787;
7
+ var USER_COLORS = [39, 213, 220, 46, 208, 141, 51, 203, 118, 165];
8
+ function colorForId(id) {
9
+ let h = 0;
10
+ for (const ch of id) h = h * 31 + ch.charCodeAt(0) >>> 0;
11
+ return USER_COLORS[h % USER_COLORS.length];
12
+ }
13
+ var DEFAULT_SERVER = "wss://accelr8-canvas.patonchain.workers.dev";
14
+ var CANVAS_W = 100;
15
+ var CANVAS_H = 30;
16
+ var LIMITS = {
17
+ chatMaxCp: 4e3,
18
+ // code points per chat message
19
+ chatMaxJson: 8192,
20
+ // serialized bytes per chat message
21
+ chatPerWindow: 10,
22
+ // messages per 10s per session
23
+ chatTail: 50,
24
+ // messages delivered in `joined`
25
+ chatPage: 50,
26
+ // default history page (max 100)
27
+ chatSyncMaxGap: 200,
28
+ // bigger gap -> full resync
29
+ chatRetain: 2e3,
30
+ // messages kept per project
31
+ draftMinIntervalMs: 100,
32
+ // server drops faster arrivals
33
+ draftMaxCp: 4e3,
34
+ // Frame budgets, per connection, per 10s window. cursor and paint have their
35
+ // own budgets because they are continuous by nature (the client sends cursor
36
+ // at 25/s, and one paint per cell while dragging): over budget they are
37
+ // DROPPED, never a disconnect. So do the executor frames an owner's terminal
38
+ // streams back (agentAccept/Delta/Done/Error: each reply sends up to 10
39
+ // deltas a second, and one terminal may run several replies at once): over
40
+ // budget a delta is dropped, and the frames that start or end a reply always
41
+ // pass. Everything else shares framesPerWindow, and a connection over
42
+ // hardFramesPerWindow in total is closed as a flood.
43
+ framesPerWindow: 200,
44
+ // other frames per 10s -> disconnect
45
+ cursorPerWindow: 400,
46
+ // cursor frames per 10s (client sends <= 250) -> drop extras
47
+ paintPerWindow: 1500,
48
+ // paint frames per 10s (fast drags burst) -> drop extras
49
+ execPerWindow: 1e3,
50
+ // executor frames per 10s (10 replies at once) -> drop extra deltas
51
+ hardFramesPerWindow: 4e3,
52
+ // all non-voice frames per 10s -> disconnect
53
+ invokesPerProjectMin: 20,
54
+ triggersPerUserMin: 6,
55
+ // messages that @mention an agent, per user per minute
56
+ // Wrong join codes. A code is the only way into a project, so guesses are
57
+ // limited three ways: per account, per network (an IPv4 address or an IPv6
58
+ // /64; a network that keeps hitting it is locked out for 10, 20, 40...
59
+ // minutes, up to 6 hours), and server-wide. Right codes are never counted.
60
+ joinGuessesPerMin: 5,
61
+ // per signed-in user (was per connection)
62
+ joinGuessesPerNet: 10,
63
+ // per network per 10 minutes
64
+ joinGuessesServerPerMin: 60,
65
+ // all wrong codes on the server, per minute
66
+ joinCodeLength: 8,
67
+ // 32 symbols, uniform: 40 bits
68
+ renamesPerHour: 10,
69
+ revealsPerMin: 10,
70
+ secretsPerProject: 50,
71
+ secretValueMax: 8192,
72
+ agentQueueCap: 5,
73
+ agentMaxDepth: 3,
74
+ agentBudgetPerTrigger: 5,
75
+ agentRepeatPerChain: 2,
76
+ voiceFramesPerSec: 50,
77
+ // ~25/s real, 2x headroom; exempt from framesPerWindow
78
+ voiceFrameMaxB64: 4096
79
+ // a 40ms s16le mono chunk is ~1.7KB base64; cap slack
80
+ };
81
+ var DEFAULT_ACCESS = { mode: "owner", users: [] };
82
+ function encode(msg) {
83
+ return JSON.stringify(msg);
84
+ }
85
+ function decode(data) {
86
+ return JSON.parse(data);
87
+ }
88
+
89
+ export {
90
+ VERSION,
91
+ PROTO,
92
+ PORT,
93
+ colorForId,
94
+ DEFAULT_SERVER,
95
+ CANVAS_W,
96
+ CANVAS_H,
97
+ LIMITS,
98
+ DEFAULT_ACCESS,
99
+ encode,
100
+ decode
101
+ };
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/home.ts
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ function accelr8Dir() {
7
+ const override = process.env.ACCELR8_HOME;
8
+ return override && override.trim() ? path.resolve(override) : path.join(os.homedir(), ".accelr8");
9
+ }
10
+
11
+ export {
12
+ accelr8Dir
13
+ };
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/livekit-token.ts
4
+ function isVoiceUrl(url) {
5
+ try {
6
+ const u = new URL(url);
7
+ return ["ws:", "wss:", "http:", "https:"].includes(u.protocol) && !!u.hostname && !u.username && !u.password;
8
+ } catch {
9
+ return false;
10
+ }
11
+ }
12
+ function parseVoiceIdentity(identity) {
13
+ const m = /^sess:([A-Za-z0-9_.@-]{1,128})$/.exec(identity);
14
+ return m ? { sid: m[1] } : null;
15
+ }
16
+ function sanitizeVoiceName(v, maxCp = 32) {
17
+ let out = "";
18
+ let n = 0;
19
+ for (const ch of String(v ?? "")) {
20
+ if (n >= maxCp) break;
21
+ const cp = ch.codePointAt(0);
22
+ if (cp < 32 || cp >= 127 && cp <= 159) continue;
23
+ if (cp >= 8203 && cp <= 8207 || cp >= 8234 && cp <= 8238 || cp >= 8294 && cp <= 8297 || cp === 65279) continue;
24
+ out += ch;
25
+ n++;
26
+ }
27
+ return out.trim() || "guest";
28
+ }
29
+ function decodeVoiceMetadata(raw) {
30
+ if (typeof raw !== "string" || !raw || raw.length > 2048) return null;
31
+ try {
32
+ const o = JSON.parse(raw);
33
+ if (!o || typeof o !== "object" || o.v !== 1) return null;
34
+ return {
35
+ v: 1,
36
+ sid: typeof o.sid === "string" ? o.sid.slice(0, 128) : "",
37
+ userId: typeof o.userId === "string" ? o.userId.slice(0, 64) : "",
38
+ name: sanitizeVoiceName(o.name),
39
+ color: Number.isInteger(o.color) ? o.color : 7,
40
+ client: o.client === "web" ? "web" : "tui"
41
+ };
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ var enc = new TextEncoder();
47
+
48
+ // src/livekit-core.ts
49
+ function initialVoiceState(room = "", identity = "") {
50
+ return {
51
+ connection: "connecting",
52
+ room,
53
+ identity,
54
+ muted: true,
55
+ participants: [],
56
+ inputDeviceId: null,
57
+ playbackBlocked: false,
58
+ needsClick: false,
59
+ error: null,
60
+ endReason: null
61
+ };
62
+ }
63
+ var CONNECTIONS = ["connecting", "connected", "reconnecting", "disconnected"];
64
+ var REASONS = ["left", "replaced", "removed", "room_closed", "network", "error", "window_closed"];
65
+ function normalizeVoiceState(raw) {
66
+ if (!raw || typeof raw !== "object") return {};
67
+ const r = raw;
68
+ const out = {};
69
+ if (typeof r.connection === "string" && CONNECTIONS.includes(r.connection)) out.connection = r.connection;
70
+ if (typeof r.room === "string") out.room = r.room.slice(0, 256);
71
+ if (typeof r.identity === "string") out.identity = r.identity.slice(0, 256);
72
+ if (typeof r.muted === "boolean") out.muted = r.muted;
73
+ if (typeof r.playbackBlocked === "boolean") out.playbackBlocked = r.playbackBlocked;
74
+ if (typeof r.needsClick === "boolean") out.needsClick = r.needsClick;
75
+ if (r.inputDeviceId === null || typeof r.inputDeviceId === "string") out.inputDeviceId = r.inputDeviceId === null ? null : r.inputDeviceId.slice(0, 256);
76
+ if (r.error === null || typeof r.error === "string") out.error = r.error === null ? null : sanitizeText(r.error, 200);
77
+ if (r.endReason === null || typeof r.endReason === "string" && REASONS.includes(r.endReason)) out.endReason = r.endReason ?? null;
78
+ if (Array.isArray(r.participants)) {
79
+ out.participants = r.participants.slice(0, 100).flatMap((p) => {
80
+ if (!p || typeof p !== "object") return [];
81
+ const q = p;
82
+ if (typeof q.identity !== "string" || !q.identity) return [];
83
+ return [{
84
+ identity: q.identity.slice(0, 256),
85
+ sid: typeof q.sid === "string" ? q.sid.slice(0, 128) : null,
86
+ userId: typeof q.userId === "string" ? q.userId.slice(0, 64) : null,
87
+ name: sanitizeVoiceName(q.name),
88
+ color: Number.isInteger(q.color) ? q.color : null,
89
+ client: q.client === "web" ? "web" : q.client === "tui" ? "tui" : null,
90
+ isLocal: q.isLocal === true,
91
+ muted: q.muted !== false,
92
+ speaking: q.speaking === true
93
+ }];
94
+ });
95
+ }
96
+ return out;
97
+ }
98
+ function sanitizeText(s, max) {
99
+ let out = "";
100
+ for (const ch of s) {
101
+ const cp = ch.codePointAt(0);
102
+ if (cp < 32 || cp >= 127 && cp <= 159 || cp >= 8234 && cp <= 8238 || cp >= 8294 && cp <= 8297) continue;
103
+ out += ch;
104
+ if (out.length >= max) break;
105
+ }
106
+ return out;
107
+ }
108
+ function sortParticipants(list) {
109
+ return [...list].sort((a, b) => a.isLocal !== b.isLocal ? a.isLocal ? -1 : 1 : a.name.localeCompare(b.name) || a.identity.localeCompare(b.identity));
110
+ }
111
+ var VoiceStore = class {
112
+ state;
113
+ listeners = /* @__PURE__ */ new Set();
114
+ constructor(initial) {
115
+ this.state = initial;
116
+ }
117
+ snapshot() {
118
+ return this.state;
119
+ }
120
+ subscribe(fn) {
121
+ this.listeners.add(fn);
122
+ return () => {
123
+ this.listeners.delete(fn);
124
+ };
125
+ }
126
+ update(patch) {
127
+ const prev = this.state;
128
+ if (prev.connection === "disconnected" && prev.endReason) return;
129
+ const next = { ...prev, ...patch };
130
+ if (patch.participants) next.participants = sortParticipants(patch.participants);
131
+ if (prev.endReason && !next.endReason) next.endReason = prev.endReason;
132
+ if (next.connection === "disconnected") {
133
+ next.participants = [];
134
+ next.muted = true;
135
+ next.needsClick = false;
136
+ }
137
+ if (JSON.stringify(prev) === JSON.stringify(next)) return;
138
+ this.state = next;
139
+ for (const fn of [...this.listeners]) {
140
+ try {
141
+ fn(next);
142
+ } catch {
143
+ }
144
+ }
145
+ }
146
+ };
147
+ function mapLiveKitDisconnect(code, wasConnected = false) {
148
+ if (code === void 0 && wasConnected) return "network";
149
+ switch (code) {
150
+ case 1:
151
+ return "left";
152
+ // CLIENT_INITIATED
153
+ case 2:
154
+ return "replaced";
155
+ // DUPLICATE_IDENTITY
156
+ case 4:
157
+ return "removed";
158
+ // PARTICIPANT_REMOVED
159
+ case 5:
160
+ case 10:
161
+ return "room_closed";
162
+ case 3:
163
+ case 7:
164
+ case 9:
165
+ case 14:
166
+ case 15:
167
+ return "network";
168
+ default:
169
+ return "error";
170
+ }
171
+ }
172
+ var RTC_NODE = "@livekit/rtc-node";
173
+ var RTC_NODE_VERSION = "1.2.1";
174
+ function rtcNodeMissingHint() {
175
+ return `in-terminal voice needs ${RTC_NODE}@${RTC_NODE_VERSION} \xB7 npm i -g ${RTC_NODE}@${RTC_NODE_VERSION} \xB7 or /voice for the window`;
176
+ }
177
+ function describeEnd(reason) {
178
+ switch (reason) {
179
+ case "left":
180
+ return "voice off";
181
+ case "replaced":
182
+ return "voice moved to another window";
183
+ case "removed":
184
+ return "you were removed from voice";
185
+ case "room_closed":
186
+ return "the voice room closed";
187
+ case "network":
188
+ return "voice lost its connection";
189
+ case "window_closed":
190
+ return "voice off \xB7 the voice window closed";
191
+ case "error":
192
+ return "voice stopped because of an error";
193
+ default:
194
+ return "voice off";
195
+ }
196
+ }
197
+
198
+ export {
199
+ isVoiceUrl,
200
+ parseVoiceIdentity,
201
+ sanitizeVoiceName,
202
+ decodeVoiceMetadata,
203
+ initialVoiceState,
204
+ normalizeVoiceState,
205
+ VoiceStore,
206
+ mapLiveKitDisconnect,
207
+ RTC_NODE,
208
+ RTC_NODE_VERSION,
209
+ rtcNodeMissingHint,
210
+ describeEnd
211
+ };
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/geo.ts
4
+ function localTz() {
5
+ try {
6
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "";
7
+ } catch {
8
+ return "";
9
+ }
10
+ }
11
+ var TZ = {
12
+ // north america
13
+ "America/New_York": [40.7, -74],
14
+ "America/Detroit": [42.3, -83],
15
+ "America/Toronto": [43.7, -79.4],
16
+ "America/Chicago": [41.9, -87.6],
17
+ "America/Winnipeg": [49.9, -97.1],
18
+ "America/Mexico_City": [19.4, -99.1],
19
+ "America/Denver": [39.7, -104.99],
20
+ "America/Edmonton": [53.5, -113.5],
21
+ "America/Phoenix": [33.4, -112.1],
22
+ "America/Los_Angeles": [34.05, -118.24],
23
+ "America/Vancouver": [49.3, -123.1],
24
+ "America/Anchorage": [61.2, -149.9],
25
+ "Pacific/Honolulu": [21.3, -157.8],
26
+ "America/Halifax": [44.6, -63.6],
27
+ "America/St_Johns": [47.6, -52.7],
28
+ // south + central america
29
+ "America/Sao_Paulo": [-23.5, -46.6],
30
+ "America/Argentina/Buenos_Aires": [-34.6, -58.4],
31
+ "America/Santiago": [-33.5, -70.7],
32
+ "America/Lima": [-12, -77],
33
+ "America/Bogota": [4.7, -74.1],
34
+ "America/Caracas": [10.5, -66.9],
35
+ "America/Guatemala": [14.6, -90.5],
36
+ // europe
37
+ "Europe/London": [51.5, -0.13],
38
+ "Europe/Dublin": [53.3, -6.3],
39
+ "Europe/Lisbon": [38.7, -9.1],
40
+ "Europe/Madrid": [40.4, -3.7],
41
+ "Europe/Paris": [48.86, 2.35],
42
+ "Europe/Amsterdam": [52.4, 4.9],
43
+ "Europe/Brussels": [50.8, 4.4],
44
+ "Europe/Zurich": [47.4, 8.5],
45
+ "Europe/Berlin": [52.5, 13.4],
46
+ "Europe/Rome": [41.9, 12.5],
47
+ "Europe/Vienna": [48.2, 16.4],
48
+ "Europe/Prague": [50.1, 14.4],
49
+ "Europe/Copenhagen": [55.7, 12.6],
50
+ "Europe/Stockholm": [59.3, 18.1],
51
+ "Europe/Oslo": [59.9, 10.7],
52
+ "Europe/Warsaw": [52.2, 21],
53
+ "Europe/Athens": [38, 23.7],
54
+ "Europe/Bucharest": [44.4, 26.1],
55
+ "Europe/Kyiv": [50.5, 30.5],
56
+ "Europe/Kiev": [50.5, 30.5],
57
+ "Europe/Istanbul": [41, 28.98],
58
+ "Europe/Moscow": [55.75, 37.6],
59
+ "Europe/Helsinki": [60.2, 24.9],
60
+ // africa + middle east
61
+ "Africa/Casablanca": [33.6, -7.6],
62
+ "Africa/Lagos": [6.5, 3.4],
63
+ "Africa/Cairo": [30, 31.2],
64
+ "Africa/Nairobi": [-1.3, 36.8],
65
+ "Africa/Johannesburg": [-26.2, 28],
66
+ "Asia/Jerusalem": [31.8, 35.2],
67
+ "Asia/Dubai": [25.2, 55.3],
68
+ "Asia/Tehran": [35.7, 51.4],
69
+ "Asia/Riyadh": [24.7, 46.7],
70
+ "Asia/Baghdad": [33.3, 44.4],
71
+ // asia
72
+ "Asia/Karachi": [24.9, 67],
73
+ "Asia/Kolkata": [22.6, 88.4],
74
+ "Asia/Calcutta": [22.6, 88.4],
75
+ "Asia/Colombo": [6.9, 79.9],
76
+ "Asia/Dhaka": [23.8, 90.4],
77
+ "Asia/Bangkok": [13.8, 100.5],
78
+ "Asia/Jakarta": [-6.2, 106.8],
79
+ "Asia/Singapore": [1.35, 103.8],
80
+ "Asia/Kuala_Lumpur": [3.1, 101.7],
81
+ "Asia/Manila": [14.6, 121],
82
+ "Asia/Hong_Kong": [22.3, 114.2],
83
+ "Asia/Shanghai": [31.2, 121.5],
84
+ "Asia/Taipei": [25, 121.5],
85
+ "Asia/Seoul": [37.6, 127],
86
+ "Asia/Tokyo": [35.7, 139.7],
87
+ // oceania
88
+ "Australia/Perth": [-31.95, 115.9],
89
+ "Australia/Adelaide": [-34.9, 138.6],
90
+ "Australia/Brisbane": [-27.5, 153],
91
+ "Australia/Sydney": [-33.87, 151.2],
92
+ "Australia/Melbourne": [-37.8, 145],
93
+ "Pacific/Auckland": [-36.85, 174.76]
94
+ };
95
+ var REGION = {
96
+ America: [15, -90],
97
+ Europe: [50, 15],
98
+ Africa: [3, 22],
99
+ Asia: [35, 95],
100
+ Australia: [-25, 135],
101
+ Pacific: [-10, -160],
102
+ Atlantic: [35, -35],
103
+ Indian: [-15, 75],
104
+ Antarctica: [-80, 0]
105
+ };
106
+ function tzToCoords(tz) {
107
+ if (!tz) return null;
108
+ const exact = TZ[tz];
109
+ if (exact) return { lat: exact[0], lon: exact[1] };
110
+ const region = REGION[tz.split("/")[0]];
111
+ if (region) return { lat: region[0], lon: region[1] };
112
+ return null;
113
+ }
114
+ function tzCity(tz) {
115
+ if (!tz) return "";
116
+ const parts = tz.split("/");
117
+ return (parts[parts.length - 1] || tz).replace(/_/g, " ");
118
+ }
119
+
120
+ export {
121
+ localTz,
122
+ tzToCoords,
123
+ tzCity
124
+ };