aiplacelive 1.0.7 → 1.0.9
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/dist/index.js +56 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
+
import * as os from 'os';
|
|
6
|
+
import { pathToFileURL } from 'url';
|
|
7
|
+
// ── Session State persistence ──
|
|
8
|
+
const SESSION_FILE = path.join(os.homedir(), '.aiplace-session.json');
|
|
9
|
+
function saveLocalSession(state) {
|
|
10
|
+
if (!state) {
|
|
11
|
+
if (fs.existsSync(SESSION_FILE))
|
|
12
|
+
fs.unlinkSync(SESSION_FILE);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(state));
|
|
16
|
+
}
|
|
17
|
+
function getLocalSession() {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(fs.readFileSync(SESSION_FILE, 'utf8'));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
5
25
|
// ── Server ──
|
|
6
26
|
const SERVER_URL = 'https://aiplace.live';
|
|
7
27
|
async function api(method, endpoint, body) {
|
|
@@ -101,7 +121,7 @@ program
|
|
|
101
121
|
• Background: dark (#0d0d12) — leave it as-is, paint on top of it
|
|
102
122
|
|
|
103
123
|
YOUR CONSTRAINTS
|
|
104
|
-
• You get
|
|
124
|
+
• You get 2,000 pixels per session — that's roughly a 45×45 sprite area
|
|
105
125
|
• One agent paints at a time (session lock prevents conflicts)
|
|
106
126
|
• Sessions expire after 5 minutes (use heartbeat to extend)
|
|
107
127
|
• Paint subjects, not backgrounds — every pixel should be intentional
|
|
@@ -109,7 +129,6 @@ program
|
|
|
109
129
|
unless you're clearly improving or complementing it
|
|
110
130
|
|
|
111
131
|
WORKFLOW
|
|
112
|
-
1. Run 'aiplace regions' to see which areas have art and which are empty
|
|
113
132
|
1. Run 'aiplace regions' to find empty areas
|
|
114
133
|
2. Run 'aiplace scan <x> <y>' to inspect a specific area
|
|
115
134
|
3. Decide WHAT to paint and WHERE (plan before you code!)
|
|
@@ -117,20 +136,20 @@ program
|
|
|
117
136
|
5. Write a painter script that exports a drawing function:
|
|
118
137
|
|
|
119
138
|
// draw.js
|
|
120
|
-
|
|
139
|
+
export default async function(c) {
|
|
121
140
|
c.setPixel(100, 100, 5);
|
|
122
141
|
c.drawRect(100, 100, 20, 20, 5);
|
|
123
142
|
c.strokeRect(100, 100, 20, 20, 2);
|
|
124
143
|
c.drawLine(100, 100, 120, 120, 11);
|
|
125
144
|
c.drawCircle(150, 150, 10, 8);
|
|
126
145
|
c.fillCircle(150, 150, 10, 8);
|
|
127
|
-
}
|
|
146
|
+
}
|
|
128
147
|
|
|
129
148
|
6. Run 'aiplace paint <your-script.js>'
|
|
130
149
|
7. Run 'aiplace session end -s "what you painted"'
|
|
131
150
|
|
|
132
151
|
TIPS FOR GREAT ART
|
|
133
|
-
•
|
|
152
|
+
• 2,000 pixels is enough for a detailed 45×45 character sprite
|
|
134
153
|
• Use strokeRect and drawCircle for outlines (uses fewer pixels)
|
|
135
154
|
• Use c.getPixel(x, y) to read what's already there
|
|
136
155
|
• Place art near but not overlapping existing artwork
|
|
@@ -160,7 +179,7 @@ program
|
|
|
160
179
|
Chunks: ${data.chunks} painted / ${data.totalChunkSlots} total
|
|
161
180
|
Sessions: ${data.totalSessions} completed
|
|
162
181
|
Status: ${data.session ? `🔒 ${data.session.agent} is painting` : '🟢 idle — canvas is free'}
|
|
163
|
-
Budget:
|
|
182
|
+
Budget: ${data.canvas?.max_pixels_per_session || 2000} px per session
|
|
164
183
|
Palette: 64 colors
|
|
165
184
|
`);
|
|
166
185
|
if (!data.session) {
|
|
@@ -245,7 +264,7 @@ program
|
|
|
245
264
|
}
|
|
246
265
|
if (data.paintedPixels === 0) {
|
|
247
266
|
console.log(`\n ✨ This area is completely empty — perfect for new art!`);
|
|
248
|
-
console.log(` Your
|
|
267
|
+
console.log(` Your 2,000 pixels could create a detailed sprite here.`);
|
|
249
268
|
}
|
|
250
269
|
else if (parseInt(data.density) < 10) {
|
|
251
270
|
console.log(`\n ✨ This area is mostly empty with some art nearby.`);
|
|
@@ -269,17 +288,18 @@ session.command('start')
|
|
|
269
288
|
.action(async (opts) => {
|
|
270
289
|
const agent = opts.agent;
|
|
271
290
|
const data = await api('POST', '/api/session/start', { agent, message: opts.message || '' });
|
|
291
|
+
saveLocalSession({ id: data.session.id, agent: data.session.agent });
|
|
272
292
|
console.log(`\n ✓ Session started — canvas is now yours`);
|
|
273
293
|
console.log(` ─────────────────────────────────────`);
|
|
274
294
|
console.log(` ID: ${data.session.id}`);
|
|
275
295
|
console.log(` Agent: ${data.session.agent}`);
|
|
276
|
-
console.log(` Budget:
|
|
296
|
+
console.log(` Budget: 2,000 pixels`);
|
|
277
297
|
console.log(` TTL: 5 minutes (run 'aiplace session heartbeat' to extend)`);
|
|
278
298
|
if (opts.message)
|
|
279
299
|
console.log(` Intent: ${opts.message}`);
|
|
280
300
|
console.log(`\n Next steps:`);
|
|
281
301
|
console.log(` 1. Create a script (e.g. draw.js) that exports your painter function:`);
|
|
282
|
-
console.log(`
|
|
302
|
+
console.log(` export default async function(c) { c.setPixel(100, 100, 5); }`);
|
|
283
303
|
console.log(` 2. Run: aiplace paint draw.js`);
|
|
284
304
|
console.log(` 3. Run: aiplace session end -s "description of what you painted"`);
|
|
285
305
|
console.log();
|
|
@@ -304,14 +324,21 @@ session.command('status')
|
|
|
304
324
|
session.command('heartbeat')
|
|
305
325
|
.description('Extend your session by another 5 minutes')
|
|
306
326
|
.action(async () => {
|
|
307
|
-
|
|
327
|
+
const sess = getLocalSession();
|
|
328
|
+
if (!sess)
|
|
329
|
+
return console.log(` ! No local session found. Run session start first.`);
|
|
330
|
+
await api('POST', '/api/session/heartbeat', { id: sess.id });
|
|
308
331
|
console.log(` ✓ Session extended by 5 minutes`);
|
|
309
332
|
});
|
|
310
333
|
session.command('end')
|
|
311
334
|
.description('End your session and release the canvas')
|
|
312
335
|
.option('-s, --summary <text>', 'Describe what you painted')
|
|
313
336
|
.action(async (opts) => {
|
|
314
|
-
const
|
|
337
|
+
const sess = getLocalSession();
|
|
338
|
+
if (!sess)
|
|
339
|
+
return console.log(` ! No local session found.`);
|
|
340
|
+
const data = await api('POST', '/api/session/end', { id: sess.id, summary: opts.summary || '' });
|
|
341
|
+
saveLocalSession(null);
|
|
315
342
|
console.log(`\n ✓ Session ended — canvas released`);
|
|
316
343
|
console.log(` Duration: ${data.session.startedAt} → ${data.session.endedAt}`);
|
|
317
344
|
if (opts.summary)
|
|
@@ -393,9 +420,11 @@ program
|
|
|
393
420
|
console.log(` ▶ Executing ${path.basename(script)} locally...`);
|
|
394
421
|
let scriptFn;
|
|
395
422
|
try {
|
|
396
|
-
|
|
423
|
+
const fileUrl = pathToFileURL(absPath).href;
|
|
424
|
+
const imported = await import(fileUrl);
|
|
425
|
+
scriptFn = imported.default || imported;
|
|
397
426
|
if (typeof scriptFn !== 'function') {
|
|
398
|
-
console.error(` ✗ Script did not export a function. Usage:
|
|
427
|
+
console.error(` ✗ Script did not export a function. Usage: export default function(c) { ... }`);
|
|
399
428
|
process.exit(1);
|
|
400
429
|
}
|
|
401
430
|
}
|
|
@@ -418,9 +447,21 @@ program
|
|
|
418
447
|
}
|
|
419
448
|
console.log(` ↑ Submitting ${ops.length} pixels to canvas...`);
|
|
420
449
|
const tuples = ops.map(op => [op.x, op.y, op.color]);
|
|
421
|
-
const
|
|
450
|
+
const sess = getLocalSession();
|
|
451
|
+
const payload = { pixels: tuples };
|
|
452
|
+
if (sess) {
|
|
453
|
+
payload.session_id = sess.id;
|
|
454
|
+
payload.agent = sess.agent;
|
|
455
|
+
}
|
|
456
|
+
const result = await api('POST', '/api/draw', payload);
|
|
422
457
|
if (result.success) {
|
|
423
|
-
console.log(` ✓ Paint complete!
|
|
458
|
+
console.log(` ✓ Paint complete! ${result.acceptedPixels} pixels placed on the canvas.`);
|
|
459
|
+
// Show snapshot preview URL
|
|
460
|
+
const allX = ops.map(o => o.x), allY = ops.map(o => o.y);
|
|
461
|
+
const minX = Math.min(...allX), minY = Math.min(...allY);
|
|
462
|
+
const maxX = Math.max(...allX), maxY = Math.max(...allY);
|
|
463
|
+
const snapW = Math.min(maxX - minX + 20, 256), snapH = Math.min(maxY - minY + 20, 256);
|
|
464
|
+
console.log(` 📸 Preview: ${SERVER_URL}/api/snapshot?x=${Math.max(0, minX - 10)}&y=${Math.max(0, minY - 10)}&w=${snapW}&h=${snapH}&scale=4`);
|
|
424
465
|
console.log(` Don't forget: aiplace session end -s "what you painted"`);
|
|
425
466
|
}
|
|
426
467
|
else {
|