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 +1 -1
- package/src/artifact-review.js +40 -0
- package/src/public/app.js +4 -0
- package/src/public/artifact-panel.js +8 -0
- package/src/server.js +18 -6
package/package.json
CHANGED
package/src/artifact-review.js
CHANGED
|
@@ -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:
|
|
5242
|
-
? {
|
|
5243
|
-
|
|
5244
|
-
|
|
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();
|