ai-or-die 0.1.80 → 0.1.81

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-or-die",
3
- "version": "0.1.80",
3
+ "version": "0.1.81",
4
4
  "description": "Universal AI coding terminal — Claude, Copilot, Gemini & more in your browser",
5
5
  "main": "src/server.js",
6
6
  "bin": {
@@ -487,8 +487,46 @@ function createArtifactReviewRouter(options) {
487
487
  if (typeof validatePath !== 'function') throw new TypeError('validatePath is required');
488
488
  if (typeof mintAssetToken !== 'function') throw new TypeError('mintAssetToken is required');
489
489
 
490
+ // Auto live-reload: one chokidar watcher per session, scoped to the canonical
491
+ // artifact file. On a settled write we broadcast a reload SIGNAL (the panel
492
+ // cache-busts the iframe; the feedback box lives outside it and survives).
493
+ // Capped, replaced on re-open, and torn down on /end. Best-effort: chokidar
494
+ // is loaded lazily so tests/headless runs without it degrade to no reload.
495
+ const MAX_WATCHERS = 64;
496
+ const watchers = new Map(); // sessionId -> { watcher, file }
497
+ function stopWatch(sessionId) {
498
+ const w = watchers.get(sessionId);
499
+ if (!w) return;
500
+ watchers.delete(sessionId);
501
+ try { w.watcher.close(); } catch (_) { /* already closed */ }
502
+ }
503
+ function startWatch(sessionId, file) {
504
+ const existing = watchers.get(sessionId);
505
+ if (existing) { if (existing.file === file) return; stopWatch(sessionId); }
506
+ if (watchers.size >= MAX_WATCHERS) { const first = watchers.keys().next().value; if (first) stopWatch(first); }
507
+ let chokidar;
508
+ try { chokidar = require('chokidar'); } catch (_) { return; }
509
+ let watcher;
510
+ try {
511
+ watcher = chokidar.watch(file, {
512
+ ignoreInitial: true,
513
+ depth: 0,
514
+ awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 30 },
515
+ });
516
+ } catch (_) { return; }
517
+ const onChange = () => {
518
+ if (!store.get(sessionId)) { stopWatch(sessionId); return; }
519
+ broadcastToSession(sessionId, { type: 'artifact_review_reload', sessionId });
520
+ };
521
+ watcher.on('change', onChange);
522
+ watcher.on('add', onChange);
523
+ watcher.on('error', () => stopWatch(sessionId));
524
+ watchers.set(sessionId, { watcher, file });
525
+ }
526
+
490
527
  const router = express.Router();
491
528
 
529
+
492
530
  router.post('/:sessionId/open', (req, res) => {
493
531
  const sessionId = req.params.sessionId;
494
532
  const file = req.body && req.body.file;
@@ -510,6 +548,7 @@ function createArtifactReviewRouter(options) {
510
548
  }
511
549
 
512
550
  const review = store.open(sessionId, validation.path);
551
+ startWatch(sessionId, validation.path);
513
552
  const viewUrl = artifactPath(sessionId, '/view', req);
514
553
  broadcastToSession(sessionId, {
515
554
  type: 'artifact_review_opened',
@@ -688,6 +727,7 @@ function createArtifactReviewRouter(options) {
688
727
  router.post('/:sessionId/end', (req, res) => {
689
728
  const sessionId = req.params.sessionId;
690
729
  const review = store.end(sessionId);
730
+ stopWatch(sessionId);
691
731
  if (!review) return res.status(404).json({ error: 'artifact review not found' });
692
732
  broadcastToSession(sessionId, { type: 'artifact_review_ended', sessionId });
693
733
  res.json({ ok: true, status: review.status });
package/src/public/app.js CHANGED
@@ -2738,6 +2738,10 @@ class ClaudeCodeWebInterface {
2738
2738
  if (this._artifactPanel) this._artifactPanel.endReview(message);
2739
2739
  break;
2740
2740
 
2741
+ case 'artifact_review_reload':
2742
+ if (this._artifactPanel) this._artifactPanel.reloadReview(message);
2743
+ break;
2744
+
2741
2745
  case 'artifact_agent_reply':
2742
2746
  if (this._artifactPanel) this._artifactPanel.agentReply(message);
2743
2747
  break;
@@ -172,6 +172,14 @@
172
172
  this._iframe.src = review.viewUrl + sep + '_r=' + Date.now();
173
173
  }
174
174
 
175
+ // Server-driven auto live-reload: file changed under review. Only the active
176
+ // tab's iframe is cache-busted; the chat/feedback box is untouched.
177
+ reloadReview(message) {
178
+ const sessionId = message && message.sessionId ? String(message.sessionId) : this.activeSessionId;
179
+ if (!sessionId || sessionId !== this.activeSessionId || !this.reviews.has(sessionId)) return;
180
+ this.reload();
181
+ }
182
+
175
183
  // ---- iframe <-> server bridge ----------------------------------------
176
184
  _handleIframeMessage(event) {
177
185
  const data = event && event.data;
package/src/server.js CHANGED
@@ -5159,6 +5159,19 @@ class ClaudeCodeWebServer {
5159
5159
  if (sidecarPath) terminalExtraEnv.AIORDIE_CLAUDE_BIND = sidecarPath;
5160
5160
  }
5161
5161
 
5162
+ // Artifact-review parity for the manual (non-fleet) claude tab: a normally
5163
+ // started claude tab gets the same env trio that _controlStartAgent injects,
5164
+ // so the in-tab agent's artifact_* tools activate standalone — no control
5165
+ // plane required. Always-on when auth is set; never injected without auth
5166
+ // (so the artifact routes stay non-public). Token leak to nested children
5167
+ // is blocked by github-router's STRIPPED_PARENT_ENV_KEYS.
5168
+ const claudeArtifactEnv = {};
5169
+ if (toolName === 'claude' && this.auth) {
5170
+ claudeArtifactEnv.AIORDIE_BASE_URL = `${this.useHttps ? 'https' : 'http'}://127.0.0.1:${this.port}`;
5171
+ claudeArtifactEnv.AIORDIE_TOKEN = this.auth;
5172
+ claudeArtifactEnv.AIORDIE_SESSION_ID = sessionId;
5173
+ }
5174
+
5162
5175
  const osc7Hooks = (toolName === 'terminal') ? {
5163
5176
  validatePath: (p) => this.validatePath(p),
5164
5177
  onCwdChange: (cwd, prev) => {
@@ -5238,12 +5251,11 @@ class ClaudeCodeWebServer {
5238
5251
  this.broadcastSessionActivity(sessionId, 'session_error');
5239
5252
  },
5240
5253
  ...options,
5241
- extraEnv: toolName === 'terminal'
5242
- ? {
5243
- ...((options.extraEnv && typeof options.extraEnv === 'object') ? options.extraEnv : {}),
5244
- ...terminalExtraEnv,
5245
- }
5246
- : options.extraEnv
5254
+ extraEnv: {
5255
+ ...((options.extraEnv && typeof options.extraEnv === 'object') ? options.extraEnv : {}),
5256
+ ...terminalExtraEnv,
5257
+ ...claudeArtifactEnv,
5258
+ }
5247
5259
  });
5248
5260
 
5249
5261
  session.lastActivity = new Date();