accelr8 1.0.0 → 3.0.0

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,106 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/update.ts
4
+ import fs from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { spawn } from "node:child_process";
8
+ var REGISTRY = "https://registry.npmjs.org/accelr8/latest";
9
+ var CACHE_FILE = "update-check.json";
10
+ var CHECK_EVERY_MS = 6 * 60 * 60 * 1e3;
11
+ function cachePath(baseDir) {
12
+ return path.join(baseDir ?? path.join(os.homedir(), ".accelr8"), CACHE_FILE);
13
+ }
14
+ function compareVersions(a, b) {
15
+ const clean = (v) => v.trim().replace(/^v/, "").split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
16
+ const pa = clean(a), pb = clean(b);
17
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
18
+ const x = pa[i] ?? 0, y = pb[i] ?? 0;
19
+ if (x !== y) return x < y ? -1 : 1;
20
+ }
21
+ return 0;
22
+ }
23
+ function cachedUpdate(current, baseDir) {
24
+ try {
25
+ const raw = fs.readFileSync(cachePath(baseDir), "utf8");
26
+ const c = JSON.parse(raw);
27
+ if (c && typeof c.latest === "string" && compareVersions(current, c.latest) < 0) {
28
+ return { latest: c.latest, current };
29
+ }
30
+ } catch {
31
+ }
32
+ return null;
33
+ }
34
+ function refreshUpdateCache(baseDir, now = Date.now()) {
35
+ let fresh = false;
36
+ try {
37
+ const c = JSON.parse(fs.readFileSync(cachePath(baseDir), "utf8"));
38
+ fresh = typeof c.checkedAt === "number" && now - c.checkedAt < CHECK_EVERY_MS;
39
+ } catch {
40
+ }
41
+ if (fresh) return;
42
+ const ac = new AbortController();
43
+ const timer = setTimeout(() => ac.abort(), 2500);
44
+ Promise.resolve().then(() => globalThis.fetch ? globalThis.fetch(REGISTRY, { signal: ac.signal, headers: { accept: "application/json" } }) : null).then((r) => r && r.ok ? r.json() : null).then((j) => {
45
+ const latest = j && typeof j.version === "string" ? j.version : null;
46
+ if (!latest) return;
47
+ const dir = baseDir ?? path.join(os.homedir(), ".accelr8");
48
+ try {
49
+ fs.mkdirSync(dir, { recursive: true });
50
+ } catch {
51
+ }
52
+ const tmp = path.join(dir, CACHE_FILE + ".tmp-" + process.pid);
53
+ fs.writeFileSync(tmp, JSON.stringify({ latest, checkedAt: now }));
54
+ fs.renameSync(tmp, cachePath(baseDir));
55
+ }).catch(() => {
56
+ }).finally(() => clearTimeout(timer));
57
+ }
58
+ function updateBanner(current, baseDir) {
59
+ refreshUpdateCache(baseDir);
60
+ const u = cachedUpdate(current, baseDir);
61
+ return u ? `update available \xB7 v${u.latest} \xB7 npm i -g accelr8@latest` : null;
62
+ }
63
+ function runUpdate(cb) {
64
+ let child;
65
+ const MANUAL = "run it yourself: npm i -g accelr8@latest";
66
+ try {
67
+ child = spawn("npm", ["install", "-g", "accelr8@latest", "--no-fund", "--no-audit"], { stdio: ["ignore", "pipe", "pipe"] });
68
+ } catch (e) {
69
+ queueMicrotask(() => cb.onDone(false, `couldn't start npm \xB7 ${MANUAL}`));
70
+ return () => {
71
+ };
72
+ }
73
+ let tail = "";
74
+ const feed = (chunk) => {
75
+ tail += chunk.toString();
76
+ let nl;
77
+ while ((nl = tail.indexOf("\n")) >= 0) {
78
+ const line = tail.slice(0, nl).trim();
79
+ tail = tail.slice(nl + 1);
80
+ if (line) cb.onLine(line);
81
+ }
82
+ };
83
+ child.stdout?.on("data", feed);
84
+ child.stderr?.on("data", feed);
85
+ child.on("error", (e) => {
86
+ cb.onDone(false, e.code === "ENOENT" ? `npm not found \xB7 ${MANUAL}` : `${e.message} \xB7 ${MANUAL}`);
87
+ });
88
+ child.on("close", (code) => {
89
+ if (tail.trim()) cb.onLine(tail.trim());
90
+ if (code === 0) cb.onDone(true, "updated \xB7 restart accelr8 to use the new version");
91
+ else cb.onDone(false, code === 243 || code === 3 ? `permission denied \xB7 ${MANUAL}` : `npm exited ${code} \xB7 ${MANUAL}`);
92
+ });
93
+ return () => {
94
+ try {
95
+ child.kill();
96
+ } catch {
97
+ }
98
+ };
99
+ }
100
+ export {
101
+ cachedUpdate,
102
+ compareVersions,
103
+ refreshUpdateCache,
104
+ runUpdate,
105
+ updateBanner
106
+ };
package/dist/cli.js ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ DEFAULT_SERVER,
4
+ PORT,
5
+ VERSION
6
+ } from "./chunks/chunk-SF6NNSYZ.js";
7
+
8
+ // src/cli.ts
9
+ var HELP = `
10
+ accelr8 v${VERSION} - collaborative ascii canvas in your terminal
11
+
12
+ USAGE
13
+ accelr8 open the app
14
+ accelr8 join <CODE> open straight into a project
15
+ accelr8 serve run your own sync server
16
+ accelr8 --intro replay the full boot animation
17
+
18
+ OPTIONS
19
+ --name <name> display name (remembered for next time)
20
+ --server <url> sync server url
21
+ (default: ${DEFAULT_SERVER})
22
+ --port <port> port for serve (default: ${PORT})
23
+ -v, --version print version
24
+ -h, --help this help
25
+
26
+ IN THE APP
27
+ mouse click tabs, buttons, project rows, brushes
28
+ click + drag on canvas paint (right-click erases)
29
+ tab / 1 2 3 switch tabs
30
+ q quit
31
+
32
+ Everyone in a project sees everyone else's cursor move live on the canvas.
33
+ `;
34
+ function argVal(args, flag) {
35
+ const i = args.indexOf(flag);
36
+ if (i >= 0 && i + 1 < args.length) return args[i + 1];
37
+ return void 0;
38
+ }
39
+ async function main() {
40
+ const args = process.argv.slice(2);
41
+ if (args.includes("-h") || args.includes("--help")) {
42
+ console.log(HELP);
43
+ return;
44
+ }
45
+ if (args.includes("-v") || args.includes("--version")) {
46
+ console.log(VERSION);
47
+ return;
48
+ }
49
+ if (args[0] === "serve") {
50
+ const port = Number(argVal(args, "--port") || PORT);
51
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
52
+ console.error(`invalid port: ${argVal(args, "--port")}`);
53
+ process.exit(1);
54
+ }
55
+ const { runServer } = await import("./chunks/server-ZP2DV4IO.js");
56
+ runServer(port);
57
+ return;
58
+ }
59
+ if (args[0] === "agent") {
60
+ const { runAgentCli } = await import("./chunks/agentcli-ZI2O7ID7.js");
61
+ await runAgentCli(args.slice(1));
62
+ return;
63
+ }
64
+ let joinCode;
65
+ if (args[0] === "join") {
66
+ joinCode = args[1];
67
+ if (!joinCode || joinCode.startsWith("-")) {
68
+ console.error("usage: accelr8 join <CODE>");
69
+ process.exit(1);
70
+ }
71
+ }
72
+ if (!process.stdout.isTTY) {
73
+ console.error("accelr8 is a terminal app and needs an interactive terminal (TTY).");
74
+ process.exit(1);
75
+ }
76
+ const { cachedUpdate, refreshUpdateCache } = await import("./chunks/update-UEPNGB6Y.js");
77
+ refreshUpdateCache();
78
+ const update = cachedUpdate(VERSION);
79
+ const { runClient } = await import("./chunks/client-7BVK2DEW.js");
80
+ runClient({
81
+ name: argVal(args, "--name"),
82
+ server: argVal(args, "--server"),
83
+ joinCode,
84
+ intro: args.includes("--intro"),
85
+ update
86
+ });
87
+ }
88
+ main();
package/package.json CHANGED
@@ -1,19 +1,70 @@
1
1
  {
2
2
  "name": "accelr8",
3
- "version": "1.0.0",
4
- "main": "index.js",
5
- "scripts": {
6
- "test": "echo \"Error: no test specified\" && exit 1"
7
- },
8
- "keywords": [],
9
- "author": "",
3
+ "version": "3.0.0",
4
+ "description": "Collaborative ascii canvas in your terminal. Real multiplayer, live cursors, mouse-driven TUI.",
5
+ "type": "module",
10
6
  "license": "ISC",
11
- "dependencies": {
12
- "reblessed": "^0.2.1"
7
+ "author": "Pat Santiago",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Patonchain/accelr8-cli.git"
13
11
  },
14
12
  "bin": {
15
- "accelr8": "bin/index.js"
13
+ "accelr8": "dist/cli.js"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "keywords": [
23
+ "cli",
24
+ "tui",
25
+ "ascii",
26
+ "collaborative",
27
+ "canvas",
28
+ "live-cursors",
29
+ "terminal",
30
+ "accelr8"
31
+ ],
32
+ "scripts": {
33
+ "build": "node scripts/build.mjs",
34
+ "dev": "tsx src/cli.ts",
35
+ "serve": "tsx src/cli.ts serve",
36
+ "smoketest": "tsx src/smoketest.ts",
37
+ "rendertest": "tsx src/rendertest.ts",
38
+ "typecheck": "tsc --noEmit",
39
+ "prepublishOnly": "npm run build",
40
+ "coretest": "tsx src/coretest.ts",
41
+ "orbtest": "tsx src/orbtest.ts",
42
+ "chattest": "tsx src/chattest.ts",
43
+ "inputtest": "tsx src/inputtest.ts",
44
+ "torturetest": "tsx src/torturetest.ts",
45
+ "test": "for t in core chat input ladder boot orb crypto accounts executor agentcontext map voice glyph materialize update contrast; do tsx src/${t}test.ts || exit 1; done && tsx src/rendertest.ts > /dev/null && echo RENDER OK",
46
+ "laddertest": "tsx src/laddertest.ts",
47
+ "boottest": "tsx src/boottest.ts",
48
+ "reconnecttest": "tsx src/reconnecttest.ts",
49
+ "agenttest": "tsx src/agenttest.ts",
50
+ "maptest": "tsx src/maptest.ts",
51
+ "voicetest": "tsx src/voicetest.ts",
52
+ "cryptotest": "tsx src/cryptotest.ts",
53
+ "accountstest": "tsx src/accountstest.ts",
54
+ "executortest": "tsx src/executortest.ts",
55
+ "agentcontexttest": "tsx src/agentcontexttest.ts",
56
+ "chaintest": "tsx src/chaintest.ts"
57
+ },
58
+ "dependencies": {
59
+ "terminal-kit": "3.1.2",
60
+ "ws": "8.18.0"
16
61
  },
17
- "devDependencies": {},
18
- "description": ""
62
+ "devDependencies": {
63
+ "@types/node": "22.10.2",
64
+ "@types/terminal-kit": "2.5.7",
65
+ "@types/ws": "8.5.13",
66
+ "esbuild": "0.28.1",
67
+ "tsx": "4.19.2",
68
+ "typescript": "5.7.2"
69
+ }
19
70
  }
package/ascii.txt DELETED
@@ -1,11 +0,0 @@
1
- ('-. ('-. _ .-')
2
- ( OO ).-. _( OO) ( \( -O )
3
- / . --. / .-----. .-----. (,------. ,--. ,------. .-----.
4
- | \-. \ ' .--./ ' .--./ | .---' | |.-') | /`. ' / .-. \
5
- .-'-' | | | |('-. | |('-. | | | | OO ) | / | | | \_.' /
6
- \| |_.' | /_) |OO ) /_) |OO ) (| '--. | |`-' | | |_.' | / .-. '.
7
- | .-. | || |`-'| || |`-'| | .--' (| '---.' | . '.' | | | |
8
- | | | | (_' '--'\ (_' '--'\ | `---. | | | |\ \ \ '-' /
9
- `--' `--' `-----' `-----' `------' `------' `--' '--' `----''
10
-
11
-
package/bin/index.js DELETED
@@ -1,314 +0,0 @@
1
- #!/usr/bin/env node
2
- const blessed = require('reblessed');
3
- const fs = require('fs');
4
- const { spawn } = require('child_process');
5
- const path = require('path');
6
-
7
- // Settings
8
- const MIN_WIDTH = 100;
9
- const MIN_HEIGHT = 30;
10
- const screen = blessed.screen({ smartCSR: true, title: 'ACCELR8 Terminal' });
11
-
12
- let hasLoadedUI = false;
13
- let asciiLogo, menu, errorBox, leftBox, rightBox;
14
-
15
- // Load ASCII logo
16
- const asciiContent = fs.readFileSync(path.join(__dirname, '../ascii.txt'), 'utf-8');
17
- const logoLines = asciiContent.split('\n');
18
- const logoHeight = logoLines.length;
19
- const logoWidth = Math.max(...logoLines.map(line => line.length));
20
-
21
- // Konami Code: up, up, down, down, left, right, left, right
22
- const konamiSequence = ['up', 'up', 'down', 'down', 'left', 'right', 'left', 'right'];
23
- let konamiIndex = 0;
24
-
25
- screen.on('keypress', (ch, key) => {
26
- if (!key) return;
27
- if (key.name === konamiSequence[konamiIndex]) {
28
- konamiIndex++;
29
- if (konamiIndex === konamiSequence.length) {
30
- launchNetHack();
31
- }
32
- } else {
33
- konamiIndex = 0;
34
- }
35
- });
36
-
37
- // === Size Check ===
38
- function tooSmall() {
39
- return screen.width < MIN_WIDTH || screen.height < MIN_HEIGHT;
40
- }
41
-
42
- function showError() {
43
- if (errorBox) return;
44
- errorBox = blessed.box({
45
- top: 'center',
46
- left: 'center',
47
- width: 'shrink',
48
- height: 5,
49
- content: '⚠️ Please enlarge your terminal window.\n(min size: 100x30)',
50
- align: 'center',
51
- valign: 'middle',
52
- border: { type: 'line' },
53
- style: { fg: 'red', border: { fg: 'red' } },
54
- });
55
- screen.append(errorBox);
56
- screen.render();
57
- }
58
-
59
- function hideError() {
60
- if (errorBox) {
61
- errorBox.destroy();
62
- errorBox = null;
63
- screen.render();
64
- }
65
- }
66
-
67
- function showUI() {
68
- if (hasLoadedUI) return;
69
-
70
- const logoTop = Math.floor((screen.height - logoHeight - 10) / 2);
71
- const logoLeft = Math.floor((screen.width - logoWidth) / 2);
72
-
73
- asciiLogo = blessed.box({
74
- top: logoTop,
75
- left: logoLeft,
76
- width: logoWidth,
77
- height: logoHeight,
78
- content: asciiContent,
79
- tags: false,
80
- style: { fg: 'cyan', bold: true },
81
- wrap: false,
82
- });
83
-
84
- const sideWidth = Math.floor(screen.width * 0.25);
85
- const sideHeight = screen.height - 2;
86
-
87
- leftBox = blessed.box({
88
- top: 1,
89
- left: 0,
90
- width: sideWidth,
91
- height: sideHeight,
92
- tags: true,
93
- style: { bg: 'default' },
94
- content: '',
95
- wrap: false,
96
- });
97
-
98
- rightBox = blessed.box({
99
- top: 1,
100
- left: screen.width - sideWidth,
101
- width: sideWidth,
102
- height: sideHeight,
103
- tags: true,
104
- style: { bg: 'default' },
105
- content: '',
106
- wrap: false,
107
- });
108
-
109
- menu = blessed.list({
110
- top: logoTop + logoHeight + 2,
111
- left: 'center',
112
- width: '40%',
113
- height: 7,
114
- items: [
115
- '[1] About the House',
116
- '[2] Meet the Founders',
117
- '[3] Upcoming Events',
118
- '[4] Apply to Stay',
119
- '[5] Exit',
120
- ],
121
- keys: true,
122
- mouse: true,
123
- border: { type: 'line' },
124
- style: {
125
- selected: { bg: 'blue', fg: 'white' },
126
- item: { hover: { bg: 'gray' } },
127
- },
128
- });
129
-
130
- menu.on('select', (item, index) => {
131
- if (index === 4) {
132
- screen.destroy();
133
- process.exit(0);
134
- } else {
135
- const messageBox = blessed.message({
136
- top: 'center',
137
- left: 'center',
138
- width: '50%',
139
- height: 5,
140
- border: { type: 'line' },
141
- style: { fg: 'yellow', border: { fg: 'cyan' } },
142
- });
143
- screen.append(messageBox);
144
- messageBox.display(`Loading: ${item.getText()}`, 2, () => {
145
- messageBox.destroy();
146
- screen.render();
147
- });
148
- }
149
- });
150
-
151
- screen.append(leftBox);
152
- screen.append(rightBox);
153
- screen.append(asciiLogo);
154
- screen.append(menu);
155
- menu.focus();
156
- screen.render();
157
-
158
- startFluidLoop(leftBox, sideWidth, sideHeight);
159
- startFluidLoop(rightBox, sideWidth, sideHeight);
160
-
161
- hasLoadedUI = true;
162
- }
163
-
164
- function destroyUI() {
165
- [asciiLogo, menu, leftBox, rightBox].forEach(el => el && el.destroy());
166
- asciiLogo = null;
167
- menu = null;
168
- leftBox = null;
169
- rightBox = null;
170
- hasLoadedUI = false;
171
- screen.render();
172
- }
173
-
174
- function checkSizeAndUpdate() {
175
- if (tooSmall()) {
176
- destroyUI();
177
- showError();
178
- } else {
179
- hideError();
180
- showUI();
181
- }
182
- }
183
-
184
- // === Boot Animation ===
185
- const bootLines = [
186
- 'Booting ACCELR8...',
187
- 'Verifying resident keys...',
188
- 'Warming up the coffee script engine...',
189
- 'Wiping whiteboard...',
190
- 'Ready.',
191
- ];
192
-
193
- const bootBox = blessed.box({
194
- top: 'center',
195
- left: 'center',
196
- width: '80%',
197
- height: bootLines.length + 4,
198
- tags: true,
199
- border: { type: 'line' },
200
- style: { fg: 'green', border: { fg: 'green' } },
201
- content: '',
202
- });
203
-
204
- screen.append(bootBox);
205
- screen.render();
206
-
207
- let i = 0;
208
- const showNextBootLine = () => {
209
- if (i < bootLines.length) {
210
- bootBox.setContent(bootBox.getContent() + `\n${bootLines[i++]}`);
211
- screen.render();
212
- setTimeout(showNextBootLine, 400);
213
- } else {
214
- setTimeout(() => {
215
- screen.remove(bootBox);
216
- screen.render();
217
- checkSizeAndUpdate();
218
- }, 1000);
219
- }
220
- };
221
- showNextBootLine();
222
-
223
- // === Resize Listener ===
224
- screen.on('resize', () => {
225
- checkSizeAndUpdate();
226
- });
227
-
228
- // === Quit keys ===
229
- screen.key(['escape', 'q', 'C-c'], () => {
230
- screen.destroy();
231
- process.exit(0);
232
- });
233
-
234
- // === Konami Easter Egg: Launch NetHack ===
235
- function launchNetHack() {
236
- screen.children.forEach(child => child.destroy());
237
- screen.render();
238
-
239
- playGlitchAnimation(() => {
240
- screen.destroy();
241
- const game = spawn('nethack', { stdio: 'inherit' });
242
- game.on('exit', code => process.exit(code));
243
- });
244
- }
245
-
246
- function playGlitchAnimation(callback) {
247
- const glitchChars = ['█', '▓', '▒', '░', '#', '%', '&', '@', 'Ξ', '∆'];
248
- const glitchBox = blessed.box({
249
- top: 0,
250
- left: 0,
251
- width: '100%',
252
- height: '100%',
253
- tags: true,
254
- style: { fg: 'magenta', bg: 'black' },
255
- content: '',
256
- });
257
-
258
- screen.append(glitchBox);
259
- let frame = 0;
260
- const totalFrames = 15;
261
-
262
- const interval = setInterval(() => {
263
- let output = '';
264
- for (let y = 0; y < screen.height; y++) {
265
- let line = '';
266
- for (let x = 0; x < screen.width; x++) {
267
- line += Math.random() > 0.92 ? glitchChars[Math.floor(Math.random() * glitchChars.length)] : ' ';
268
- }
269
- output += line + '\n';
270
- }
271
-
272
- glitchBox.setContent(output);
273
- screen.render();
274
- frame++;
275
-
276
- if (frame === totalFrames) {
277
- clearInterval(interval);
278
- glitchBox.setContent('{green-fg}ACCESS GRANTED: Launching Secret Mode...{/green-fg}');
279
- glitchBox.style.fg = 'green';
280
- screen.render();
281
- setTimeout(callback, 1500);
282
- }
283
- }, 50);
284
- }
285
-
286
- // === Animated Fluid Side Panels ===
287
- function startFluidLoop(box, width, height) {
288
- let t = 0;
289
- setInterval(() => {
290
- const frame = generateFluidFrame(width - 2, height - 2, t);
291
- box.setContent(frame);
292
- screen.render();
293
- t += 1;
294
- }, 80);
295
- }
296
-
297
- function generateFluidFrame(width, height, t) {
298
- let output = '';
299
- for (let y = 0; y < height; y++) {
300
- let line = '';
301
- for (let x = 0; x < width; x++) {
302
- const wave = Math.sin((x + t) / 4) + Math.cos((y + t) / 4);
303
- const char = wave > 1 ? '▓' : wave > 0.3 ? '▒' : wave > -0.5 ? '░' : ' ';
304
- line += `{${getColorCode(t + x + y)}-fg}${char}{/}`;
305
- }
306
- output += line + '\n';
307
- }
308
- return output;
309
- }
310
-
311
- function getColorCode(n) {
312
- const colors = ['red', 'magenta', 'blue', 'cyan', 'green', 'yellow'];
313
- return colors[Math.floor(n / 4) % colors.length];
314
- }