Monorepo for Aesthetic.Computer
aesthetic.computer
1#!/usr/bin/env node
2// Batch mint keeps from keep-queue.txt
3// Reads the AC auth from vault and mints each code sequentially
4
5import fs from 'fs';
6import path from 'path';
7import os from 'os';
8import { fileURLToPath } from 'url';
9
10const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
12// Load auth from ~/.ac-token (same as artery-tui.mjs)
13const authPath = path.join(os.homedir(), '.ac-token');
14
15if (!fs.existsSync(authPath)) {
16 console.error('❌ Auth not found at:', authPath);
17 console.error(' Run: node artery/artery-tui.mjs and login first (press "a" then "l")');
18 process.exit(1);
19}
20
21const auth = JSON.parse(fs.readFileSync(authPath, 'utf8'));
22console.log(`🔑 Loaded auth for: ${auth.email || auth.user?.email || 'unknown'}`);
23
24// Load queue
25const queuePath = path.join(__dirname, 'keep-queue.txt');
26const codes = fs.readFileSync(queuePath, 'utf8')
27 .split('\n')
28 .map(c => c.trim())
29 .filter(c => c.length > 0);
30
31console.log(`📋 ${codes.length} codes to mint\n`);
32
33// Disable SSL verification for localhost
34process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
35
36const API_URL = 'https://localhost:8888/api/keep-mint';
37
38async function mintCode(code, index) {
39 console.log(`\n[${index + 1}/${codes.length}] 🎨 Minting $${code}...`);
40
41 try {
42 const response = await fetch(API_URL, {
43 method: 'POST',
44 headers: {
45 'Authorization': `Bearer ${auth.access_token}`,
46 'Content-Type': 'application/json',
47 'Accept': 'text/event-stream',
48 },
49 body: JSON.stringify({ piece: code, mode: 'mint' }),
50 });
51
52 if (!response.ok) {
53 const text = await response.text();
54 console.log(` ❌ Error (${response.status}): ${text.slice(0, 200)}`);
55 return { code, success: false, error: text };
56 }
57
58 // Parse SSE stream
59 const reader = response.body.getReader();
60 const decoder = new TextDecoder();
61 let buffer = '';
62 let tokenId = null;
63 let currentEvent = 'progress';
64
65 while (true) {
66 const { done, value } = await reader.read();
67 if (done) break;
68
69 buffer += decoder.decode(value, { stream: true });
70 const lines = buffer.split('\n');
71 buffer = lines.pop() || '';
72
73 for (const line of lines) {
74 if (line.startsWith('event: ')) {
75 currentEvent = line.slice(7).trim();
76 continue;
77 }
78
79 if (line.startsWith('data: ')) {
80 try {
81 const data = JSON.parse(line.slice(6));
82
83 if (currentEvent === 'error' || data.error) {
84 console.log(` ⚠️ ${data.error}${data.minter ? ` by ${data.minter}` : ''}`);
85 if (data.tokenId) {
86 console.log(` Already kept as token #${data.tokenId}`);
87 }
88 return { code, success: false, error: data.error, tokenId: data.tokenId };
89 }
90
91 if (currentEvent === 'progress') {
92 process.stdout.write(` ${data.step || data.message || '...'}\r`);
93 }
94
95 if (currentEvent === 'complete' && data.success) {
96 tokenId = data.tokenId;
97 console.log(` ✅ Kept as token #${tokenId}`);
98 console.log(` ${data.objktUrl || ''}`);
99 return { code, success: true, tokenId };
100 }
101 } catch (e) {
102 // Ignore JSON parse errors for partial data
103 }
104 }
105 }
106 }
107
108 return { code, success: !!tokenId, tokenId };
109 } catch (e) {
110 console.log(` ❌ Fetch error: ${e.message}`);
111 return { code, success: false, error: e.message };
112 }
113}
114
115// Main
116async function main() {
117 const results = { success: 0, failed: 0, skipped: 0 };
118 const startTime = Date.now();
119
120 for (let i = 0; i < codes.length; i++) {
121 const result = await mintCode(codes[i], i);
122 if (result.success) {
123 results.success++;
124 } else if (result.tokenId) {
125 results.skipped++; // Already minted
126 } else {
127 results.failed++;
128 }
129
130 // Small delay between mints to avoid overwhelming
131 if (i < codes.length - 1) {
132 await new Promise(r => setTimeout(r, 500));
133 }
134 }
135
136 const elapsed = ((Date.now() - startTime) / 1000 / 60).toFixed(1);
137 console.log(`\n${'='.repeat(50)}`);
138 console.log(`✅ Complete in ${elapsed} minutes`);
139 console.log(` Success: ${results.success}`);
140 console.log(` Skipped (already minted): ${results.skipped}`);
141 console.log(` Failed: ${results.failed}`);
142}
143
144main().catch(console.error);