diff --git a/.agents/skills/impeccable/scripts/live-inject.mjs b/.agents/skills/impeccable/scripts/live-inject.mjs
index d99c37fd5..a0ab8bee2 100644
--- a/.agents/skills/impeccable/scripts/live-inject.mjs
+++ b/.agents/skills/impeccable/scripts/live-inject.mjs
@@ -8,9 +8,14 @@
* with zero LLM involvement.
*
* Usage:
- * node live-inject.mjs --port PORT # Insert the live script tag
- * node live-inject.mjs --remove # Remove the live script tag
- * node live-inject.mjs --check # Check whether live config exists
+ * node live-inject.mjs --port PORT [--token TOKEN] # Insert the live script tag
+ * node live-inject.mjs --remove # Remove the live script tag
+ * node live-inject.mjs --check # Check whether live config exists
+ *
+ * When --token is supplied, it is appended to the /live.js src as `?token=...`
+ * so the server's token-gated /live.js handler will serve the bundle. Omitting
+ * the token yields a bare `/live.js` src (legacy behavior; the server returns
+ * 401 for it under the current gate).
*/
import fs from 'node:fs';
@@ -162,18 +167,22 @@ Output (JSON):
console.error(JSON.stringify({ ok: false, error: 'missing_port' }));
process.exit(1);
}
+ // Optional server token: appended to the /live.js src so the token-gated
+ // /live.js handler authorizes the browser fetch. `live.mjs` always passes it.
+ const tokenIdx = args.indexOf('--token');
+ const token = tokenIdx !== -1 ? args[tokenIdx + 1] : undefined;
const gitIgnore = ensureLiveGitIgnores(
process.cwd(),
nuxt ? [nuxt.pluginFile] : [],
);
if (svelteKit) {
- const adapterResult = applySvelteKitLiveAdapter({ cwd: process.cwd(), port, config });
+ const adapterResult = applySvelteKitLiveAdapter({ cwd: process.cwd(), port, token, config });
console.log(JSON.stringify({ ok: true, port, adapter: 'sveltekit', gitIgnore, results: [adapterResult] }));
return;
}
if (nuxt) {
- const adapterResult = applyNuxtLiveAdapter({ cwd: process.cwd(), port, project: nuxt });
+ const adapterResult = applyNuxtLiveAdapter({ cwd: process.cwd(), port, token, project: nuxt });
console.log(JSON.stringify({
ok: !adapterResult.error,
port,
@@ -190,7 +199,7 @@ Output (JSON):
if (!fs.existsSync(absFile)) return { file: relFile, error: 'file_not_found' };
const content = fs.readFileSync(absFile, 'utf-8');
const withoutOld = revertCspMeta(removeTag(content, config.commentSyntax));
- const withTag = insertTag(withoutOld, config, port, relFile);
+ const withTag = insertTag(withoutOld, config, port, relFile, token);
if (withTag === withoutOld) {
return { file: relFile, error: 'insertion_point_not_found', anchor: config.insertBefore || config.insertAfter };
}
@@ -276,9 +285,9 @@ export function detectNuxtProject(cwd = process.cwd()) {
return { configFile, appDir, pluginFile };
}
-export function buildNuxtPlugin(port) {
+export function buildNuxtPlugin(port, token) {
return `/* ${NUXT_PLUGIN_MARKER} */
-const liveSrc = 'http://localhost:${port}/live.js';
+const liveSrc = '${buildLiveScriptSrc(port, token)}';
const liveSelector = 'script[data-impeccable-live-nuxt]';
export default defineNuxtPlugin(() => {
@@ -303,7 +312,7 @@ export default defineNuxtPlugin(() => {
`;
}
-export function applyNuxtLiveAdapter({ cwd = process.cwd(), port, project = detectNuxtProject(cwd) }) {
+export function applyNuxtLiveAdapter({ cwd = process.cwd(), port, token, project = detectNuxtProject(cwd) }) {
if (!project) return { error: 'nuxt_not_detected' };
const absFile = path.join(cwd, project.pluginFile);
const existing = fs.existsSync(absFile) ? fs.readFileSync(absFile, 'utf-8') : null;
@@ -315,7 +324,7 @@ export function applyNuxtLiveAdapter({ cwd = process.cwd(), port, project = dete
};
}
- const content = buildNuxtPlugin(port);
+ const content = buildNuxtPlugin(port, token);
fs.mkdirSync(path.dirname(absFile), { recursive: true });
if (content !== existing) fs.writeFileSync(absFile, content, 'utf-8');
return {
@@ -497,7 +506,18 @@ function validateConfig(cfg) {
function commentOpen(syntax) { return syntax === 'jsx' ? '{/*' : ''; }
-function buildTagBlock(syntax, port, filePath) {
+/**
+ * Build the /live.js src the browser loads. When a token is supplied it rides
+ * as a `?token=...` query param so the server's token-gated /live.js handler
+ * authorizes the fetch. Shared by every injection path (HTML/JSX script tag,
+ * the Nuxt plugin, the SvelteKit root component) so they stay in sync.
+ */
+export function buildLiveScriptSrc(port, token) {
+ const base = 'http://localhost:' + port + '/live.js';
+ return token ? base + '?token=' + encodeURIComponent(token) : base;
+}
+
+function buildTagBlock(syntax, port, filePath, token) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Astro processes \n' +
+ '\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
}
@@ -528,9 +548,9 @@ function readLineEndingAt(content, index) {
return '';
}
-function insertTag(content, config, port, filePath) {
+function insertTag(content, config, port, filePath, token) {
const lineEnding = detectLineEnding(content);
- const block = normalizeLineEndings(buildTagBlock(config.commentSyntax, port, filePath), lineEnding);
+ const block = normalizeLineEndings(buildTagBlock(config.commentSyntax, port, filePath, token), lineEnding);
// insertBefore: match the LAST occurrence. Anchors like `