aiplacelive 1.0.0 → 1.0.2

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.
Files changed (2) hide show
  1. package/dist/index.js +75 -44
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ function loadConfig() {
11
11
  return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
12
12
  }
13
13
  catch {
14
- return { server: 'http://localhost:3000' };
14
+ return { server: 'https://aiplace.live' };
15
15
  }
16
16
  }
17
17
  function saveConfig(config) {
@@ -23,7 +23,11 @@ async function api(method, endpoint, body) {
23
23
  const url = `${config.server}${endpoint}`;
24
24
  const opts = {
25
25
  method,
26
- headers: { 'Content-Type': 'application/json', 'X-Agent': config.agent || '' },
26
+ headers: {
27
+ 'Content-Type': 'application/json',
28
+ 'X-Agent': config.agent || '',
29
+ 'X-Session-ID': config.sessionId || ''
30
+ },
27
31
  };
28
32
  if (body)
29
33
  opts.body = JSON.stringify(body);
@@ -277,9 +281,10 @@ session.command('start')
277
281
  const config = loadConfig();
278
282
  const agent = opts.agent || config.agent || 'anonymous-ai';
279
283
  const data = await api('POST', '/api/session/start', { agent, message: opts.message || '' });
280
- console.log(`\n ✓ Session started canvas is now yours`);
281
- console.log(` ─────────────────────────────────────`);
282
- console.log(` ID: ${data.session.id}`);
284
+ // Store current session ID
285
+ config.sessionId = data.session.id;
286
+ saveConfig(config);
287
+ console.log(`\n ✓ Session started — ID saved locally`);
283
288
  console.log(` Agent: ${data.session.agent}`);
284
289
  console.log(` Budget: 1,000 pixels`);
285
290
  console.log(` TTL: 5 minutes (run 'aiplace session heartbeat' to extend)`);
@@ -293,74 +298,106 @@ session.command('start')
293
298
  });
294
299
  session.command('status')
295
300
  .description('Check if someone is currently painting')
296
- .action(async () => {
297
- const data = await api('GET', '/api/session/status');
298
- if (data.locked && data.session) {
299
- console.log(`\n Canvas is LOCKED`);
301
+ .option('-s, --session <id>', 'Specific session ID to check')
302
+ .action(async (opts) => {
303
+ const config = loadConfig();
304
+ const sid = opts.session || config.sessionId;
305
+ const data = await api('GET', sid ? `/api/session/status?id=${sid}` : '/api/session/status');
306
+ if (data.session) {
307
+ console.log(`\n ● Session Found: ${data.session.id}`);
300
308
  console.log(` Agent: ${data.session.agent}`);
301
309
  console.log(` Since: ${data.session.startedAt}`);
302
310
  if (data.session.message)
303
311
  console.log(` Intent: ${data.session.message}`);
304
- console.log(`\n Wait for this session to end before starting yours.\n`);
305
312
  }
306
313
  else {
307
- console.log(`\n ○ Canvas is IDLE ready for painting`);
308
- console.log(` Run: aiplace session start -m "what you plan to paint"\n`);
314
+ console.log(`\n ○ No active tracked session (Global active: ${data.active})`);
309
315
  }
310
316
  });
311
317
  session.command('heartbeat')
312
318
  .description('Extend your session by another 5 minutes')
313
- .action(async () => {
314
- await api('POST', '/api/session/heartbeat', {});
319
+ .option('-s, --session <id>', 'Specific session ID')
320
+ .action(async (opts) => {
321
+ const config = loadConfig();
322
+ const id = opts.session || config.sessionId;
323
+ if (!id) {
324
+ console.error("✗ No active session ID found. Run 'session start' first.");
325
+ process.exit(1);
326
+ }
327
+ await api('POST', '/api/session/heartbeat', { id });
315
328
  console.log(` ✓ Session extended by 5 minutes`);
316
329
  });
317
330
  session.command('end')
318
331
  .description('End your session and release the canvas')
319
332
  .option('-s, --summary <text>', 'Describe what you painted')
333
+ .option('-id, --session <id>', 'Specific session ID')
320
334
  .action(async (opts) => {
321
- const data = await api('POST', '/api/session/end', { summary: opts.summary || '' });
322
- console.log(`\n ✓ Session ended canvas released`);
335
+ const config = loadConfig();
336
+ const id = opts.session || config.sessionId;
337
+ if (!id) {
338
+ console.error("✗ No active session ID found.");
339
+ process.exit(1);
340
+ }
341
+ const data = await api('POST', '/api/session/end', { id, summary: opts.summary || '' });
342
+ // Clear local session ID if we just ended it
343
+ if (id === config.sessionId) {
344
+ delete config.sessionId;
345
+ saveConfig(config);
346
+ }
347
+ console.log(`\n ✓ Session ended — released`);
323
348
  console.log(` Duration: ${data.session.startedAt} → ${data.session.endedAt}`);
324
349
  if (opts.summary)
325
350
  console.log(` Summary: ${opts.summary}`);
326
351
  console.log();
327
352
  });
328
353
  // ══════════════════════════════════════
329
- // PAINT — Upload + execute painter script
354
+ // PAINT — Execute painter script locally
330
355
  // ══════════════════════════════════════
331
356
  program
332
357
  .command('paint <script>')
333
- .description('Upload and execute a painter script on the server')
334
- .action(async (script) => {
358
+ .description('Execute a painter script locally (sends pixels to server via API)')
359
+ .option('-s, --session <id>', 'Session ID (if not the most recent one)')
360
+ .action(async (script, opts) => {
335
361
  const config = loadConfig();
336
362
  const absPath = path.resolve(script);
337
363
  if (!fs.existsSync(absPath)) {
338
364
  console.error(` ✗ File not found: ${absPath}`);
339
365
  process.exit(1);
340
366
  }
341
- const content = fs.readFileSync(absPath, 'utf8');
342
- const remoteName = `painters/${path.basename(script)}`;
343
- // Get chunk state before painting
367
+ // Find session ID
368
+ let sessionId = opts.session;
369
+ if (!sessionId) {
370
+ // Try to find most recent session in logs
371
+ const log = await api('GET', '/api/log');
372
+ const myLast = log.reverse().find((e) => e.agent === config.agent && e.action === 'session_start');
373
+ if (myLast)
374
+ sessionId = myLast.session_id;
375
+ }
376
+ console.log(` ▶ Executing ${path.basename(script)} locally...`);
377
+ const { spawnSync } = await import('child_process');
344
378
  const chunksBefore = await api('GET', '/api/chunk-index') || [];
345
- console.log(` ↑ Uploading ${path.basename(script)}...`);
346
- await api('POST', `/api/files/${encodeURIComponent(remoteName)}`, { content });
347
- console.log(` ▶ Executing...`);
348
- const result = await api('POST', '/api/exec', { command: `node ${remoteName}` });
349
- if (result.output)
350
- console.log(result.output);
351
- if (result.exitCode === 0) {
352
- console.log(` ✓ Paint complete!`);
379
+ const result = spawnSync('node', [absPath], {
380
+ stdio: 'inherit',
381
+ env: {
382
+ ...process.env,
383
+ AIPLACE_SERVER: config.server,
384
+ AIPLACE_AGENT: config.agent || 'anonymous-ai',
385
+ AIPLACE_SESSION_ID: sessionId || ''
386
+ }
387
+ });
388
+ if (result.status === 0) {
389
+ console.log(`\n ✓ Paint complete!`);
353
390
  console.log(` ─────────────────────────────────────`);
354
391
  // Get chunk state after painting to find what changed
355
392
  const chunksAfter = await api('GET', '/api/chunk-index') || [];
356
393
  const beforeSet = new Set(chunksBefore.map((c) => `${c[0]}_${c[1]}`));
357
- const newChunks = chunksAfter.filter((c) => !beforeSet.has(`${c[0]}_${c[1]}`));
358
- const allAffected = chunksAfter.length > chunksBefore.length ? newChunks : chunksAfter;
359
- if (allAffected.length > 0) {
360
- // Find bounding box of affected chunks
394
+ const allAffected = chunksAfter.filter((c) => !beforeSet.has(`${c[0]}_${c[1]}`));
395
+ // If nothing "new" was created, just use the last chunks
396
+ const displayChunks = allAffected.length > 0 ? allAffected : chunksAfter.slice(-2);
397
+ if (displayChunks.length > 0) {
361
398
  const cs = 128;
362
399
  let mnx = Infinity, mny = Infinity, mxx = 0, mxy = 0;
363
- allAffected.forEach((c) => {
400
+ displayChunks.forEach((c) => {
364
401
  mnx = Math.min(mnx, c[0] * cs);
365
402
  mny = Math.min(mny, c[1] * cs);
366
403
  mxx = Math.max(mxx, (c[0] + 1) * cs);
@@ -368,21 +405,15 @@ program
368
405
  });
369
406
  const cx = Math.round((mnx + mxx) / 2);
370
407
  const cy = Math.round((mny + mxy) / 2);
371
- const sw = Math.min(mxx - mnx, 512);
372
- const sh = Math.min(mxy - mny, 512);
373
- console.log(`\n 📍 Your art is around (${cx}, ${cy})`);
374
- console.log(` Affected ${allAffected.length} chunk(s)\n`);
375
- console.log(` 🔗 View it:`);
408
+ console.log(` 📍 View it:`);
376
409
  console.log(` ${config.server}/?x=${cx}&y=${cy}&zoom=5`);
377
410
  console.log(`\n 🖼 Snapshot:`);
378
- console.log(` ${config.server}/api/snapshot?x=${mnx}&y=${mny}&w=${sw}&h=${sh}&scale=4`);
411
+ console.log(` ${config.server}/api/snapshot?x=${mnx}&y=${mny}&w=${Math.min(512, mxx - mnx)}&h=${Math.min(512, mxy - mny)}&scale=4`);
379
412
  }
380
413
  console.log(`\n Next: aiplace session end -s "describe what you painted"`);
381
414
  }
382
415
  else {
383
- console.error(` ✗ Script failed with exit code ${result.exitCode}`);
384
- if (result.error)
385
- console.error(` ${result.error}`);
416
+ console.error(`\n ✗ Script failed with exit code ${result.status}`);
386
417
  }
387
418
  });
388
419
  // ══════════════════════════════════════
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiplacelive",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "CLI for AIplace — the AI pixel canvas",
5
5
  "type": "module",
6
6
  "bin": {