Replace WebSocket with SSE, move live scripts into skill (self-contained)

Two architectural changes that make the live variant mode self-contained:

1. SSE replaces WebSocket: the server now uses Server-Sent Events for
   server→browser push and regular fetch POST for browser→server
   events. This eliminates the ws npm dependency entirely. The live
   server is now zero-dependency pure Node.js (http, crypto, fs, net).

   Browser: EventSource replaces WebSocket. sendEvent() uses fetch POST.
   Server: GET /events returns SSE stream, POST /events receives browser
   events. All other endpoints (poll, source, health, stop) unchanged.

2. Scripts moved to source/skills/impeccable/scripts/: live-server.mjs,
   live-poll.mjs, live-wrap.mjs, live-browser.js are now part of the
   skill itself. Users who install the skill via npx skills get the live
   mode without needing npm install impeccable separately.

   The skill reference uses {{scripts_path}}/live-server.mjs etc.
   The CLI (bin/cli.js) delegates to the skill scripts as a convenience.

   Removed ws from package.json dependencies.

The old src/live/ files remain as the development copy. The build system
syncs source/skills/ to all harness dirs (11 providers).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-04-12 19:19:36 -07:00
co-authored by Claude Opus 4.6
parent a832fe778c
commit 5bad08723d
63 changed files with 24842 additions and 112 deletions
+4 -4
View File
@@ -53,16 +53,16 @@ if (command === 'detect') {
const { detectCli } = await import('../src/detect-antipatterns.mjs');
await detectCli();
} else if (command === 'live') {
// Delegate to the self-contained skill script (also works via node scripts_path/live-server.mjs)
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
const { liveCli } = await import('../src/detect-antipatterns.mjs');
await liveCli();
await import('../source/skills/impeccable/scripts/live-server.mjs');
} else if (command === 'poll') {
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
const { pollCli } = await import('../src/live/poll.mjs');
const { pollCli } = await import('../source/skills/impeccable/scripts/live-poll.mjs');
await pollCli();
} else if (command === 'wrap') {
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
const { wrapCli } = await import('../src/live/wrap.mjs');
const { wrapCli } = await import('../source/skills/impeccable/scripts/live-wrap.mjs');
await wrapCli();
} else if (command === 'skills') {
const { run } = await import('./commands/skills.mjs');