Files
root-at-skicandClaude Opus 5.5 22815a9940 Archify Diagram Viewer 0.1.0
VS Code extension that previews Archify diagrams from their JSON sources
(live, as you type) and opens rendered Archify HTML in a viewer tab.
Bundles the Archify 3.0.1 renderer and runs it on VS Code's Node runtime.
Adds validation diagnostics, JSON schema help, source-link navigation,
export saving, render-to-file and open-in-browser commands.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 12:44:15 +03:00

92 lines
3.6 KiB
JavaScript

import { spawn } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const childPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'scripts', 'delivery-update-child.mjs');
const DEADLINE_MS = 1_000;
const MAX_OUTPUT_BYTES = 16 * 1_024;
function unavailable(reason) {
return { status: 'unavailable', installedVersion: null, availableVersion: null,
releaseNotes: null, checkedAt: null, source: null, noticeRequired: false,
noticeText: null, reason };
}
function normalize(result) {
if (result?.status === 'current') return {
status: 'current', installedVersion: result.installedVersion,
availableVersion: result.availableVersion, releaseNotes: null,
checkedAt: result.checkedAt, source: result.source,
noticeRequired: false, noticeText: null,
};
if (result?.status !== 'update_available') {
return unavailable(result?.reason || (result?.status === 'silent' ? 'no-update' : 'invalid-result'));
}
const noticeRequired = result.noticeRequired !== false;
const cached = result.source === 'cache';
const noticeText = noticeRequired
? `Archify ${result.severity === 'security' ? 'security update' : 'update'}: ${result.installedVersion} → ${result.latestVersion}. ${cached
? `A previous check at ${result.checkedAt} found a newer release.`
: 'A newer release is available.'} Release notes: ${result.releaseNotes}. The installed Skill has not changed; ask to snooze or ignore this reminder.`
: null;
return {
status: 'update_available',
installedVersion: result.installedVersion,
availableVersion: result.latestVersion,
releaseNotes: result.releaseNotes,
severity: result.severity,
checkedAt: result.checkedAt,
source: result.source,
noticeRequired,
noticeText,
...(result.eventKey ? { eventKey: result.eventKey } : {}),
...(result.reason ? { reason: result.reason } : {}),
...(result.suppressedUntil ? { suppressedUntil: result.suppressedUntil } : {}),
};
}
export function startDeliveryUpdateCheck({
env = process.env, deadlineMs = DEADLINE_MS, checkerPath = childPath,
} = {}) {
if (env.ARCHIFY_UPDATE_CHECK_DISABLED === '1') return Promise.resolve(unavailable('disabled'));
return new Promise((resolve) => {
let child;
try {
const deadlineNs = process.hrtime.bigint() + BigInt(Math.floor(deadlineMs * 1_000_000));
child = spawn(process.execPath, [checkerPath, String(deadlineNs)], {
env,
stdio: ['ignore', 'pipe', 'ignore'],
windowsHide: true,
});
} catch {
resolve(unavailable('runtime-unavailable'));
return;
}
let output = '';
let timedOut = false;
let overflow = false;
const timer = setTimeout(() => {
timedOut = true;
child.kill('SIGKILL');
}, deadlineMs);
child.stdout.on('data', (chunk) => {
if (Buffer.byteLength(output) + chunk.length > MAX_OUTPUT_BYTES) {
overflow = true;
child.kill('SIGKILL');
} else output += chunk.toString('utf8');
});
child.on('error', () => {});
child.on('close', (code) => {
clearTimeout(timer);
if (overflow) return resolve(unavailable('invalid-result'));
// A synchronous renderer can delay both this callback and the parent's
// timer. A complete child result wins even if the timer fired meanwhile.
if (code === 0 && output.endsWith('\n')) {
try { return resolve(normalize(JSON.parse(output))); }
catch { return resolve(unavailable('invalid-result')); }
}
resolve(unavailable(timedOut || child.signalCode === 'SIGKILL' ? 'timeout' : 'check-failed'));
});
});
}