@sickr/cli 0.9.8 → 0.9.9
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/dist/recorder.js +2 -1
- package/dist/run.js +35 -3
- package/package.json +1 -1
package/dist/recorder.js
CHANGED
|
@@ -93,7 +93,8 @@ export function mapEvent(cc, now = new Date(), ctx = {}) {
|
|
|
93
93
|
const rawSession = String(cc.session_id ?? '');
|
|
94
94
|
const session = rawSession ? rawSession.slice(0, 12) : undefined;
|
|
95
95
|
const agent = ctx.agent;
|
|
96
|
-
const
|
|
96
|
+
const runner = process.env.SICKR_RUN_INSTANCE ? process.env.SICKR_RUN_INSTANCE.slice(0, 36) : undefined;
|
|
97
|
+
const base = { at, agent, session, runner };
|
|
97
98
|
switch (name) {
|
|
98
99
|
case 'SessionStart':
|
|
99
100
|
return { kind: 'start', label: 'Session', detail: redact(String(cc.cwd ?? '')), ...base };
|
package/dist/run.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, readdirSync, statSync, openSync, readSync, closeSync, unlinkSync } from 'node:fs';
|
|
24
24
|
import { homedir } from 'node:os';
|
|
25
25
|
import { join } from 'node:path';
|
|
26
|
+
import { randomUUID } from 'node:crypto';
|
|
26
27
|
import { execFileSync } from 'node:child_process';
|
|
27
28
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
28
29
|
import { readCredentials } from './auth.js';
|
|
@@ -111,6 +112,18 @@ function appendInbox(urlid, text, at) {
|
|
|
111
112
|
writeFileSync(file, `# steer inbox — ${urlid}\n\n`);
|
|
112
113
|
appendFileSync(file, `\n## ${at}\n\n${text}\n`);
|
|
113
114
|
}
|
|
115
|
+
function normTarget(s) {
|
|
116
|
+
return String(s ?? '').trim().toLowerCase();
|
|
117
|
+
}
|
|
118
|
+
export function steerMatchesRunner(msg, identity) {
|
|
119
|
+
if (msg.targetRunner)
|
|
120
|
+
return msg.targetRunner === identity.runner;
|
|
121
|
+
if (msg.targetSession)
|
|
122
|
+
return identity.sessions.has(msg.targetSession);
|
|
123
|
+
if (msg.targetAgent)
|
|
124
|
+
return normTarget(msg.targetAgent) === normTarget(identity.agent);
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
114
127
|
export function decideSteer(msg, defaultMode = 'pty') {
|
|
115
128
|
const text = String(msg.text ?? '');
|
|
116
129
|
if (!text)
|
|
@@ -282,6 +295,10 @@ export async function startRun(opts) {
|
|
|
282
295
|
// browser /r/<urlid> stays empty until events flow.
|
|
283
296
|
ensureRecordingHooks(opts.agent);
|
|
284
297
|
const agentBin = resolveAgent(opts.agent);
|
|
298
|
+
const provider = providerForAgent(opts.agent);
|
|
299
|
+
const agentLabel = provider ? PROVIDERS[provider].recordLabel : opts.agent;
|
|
300
|
+
const runnerId = randomUUID();
|
|
301
|
+
const runnerIdentity = { agent: agentLabel, runner: runnerId, sessions: new Set() };
|
|
285
302
|
const cols = process.stdout.columns ?? 80;
|
|
286
303
|
const rows = process.stdout.rows ?? 24;
|
|
287
304
|
// mode=auto (default) injects the agent's "no prompt / full perms" flag
|
|
@@ -307,7 +324,7 @@ export async function startRun(opts) {
|
|
|
307
324
|
name: 'xterm-256color',
|
|
308
325
|
cols, rows,
|
|
309
326
|
cwd: process.cwd(),
|
|
310
|
-
env: process.env,
|
|
327
|
+
env: { ...process.env, SICKR_RUN_INSTANCE: runnerId, SICKR_RUN_AGENT: agentLabel },
|
|
311
328
|
});
|
|
312
329
|
// Wire stdio: parent stdin -> pty; pty -> parent stdout.
|
|
313
330
|
if (process.stdin.isTTY)
|
|
@@ -379,7 +396,11 @@ export async function startRun(opts) {
|
|
|
379
396
|
let opened = false;
|
|
380
397
|
ws.addEventListener('open', () => {
|
|
381
398
|
opened = true;
|
|
382
|
-
|
|
399
|
+
try {
|
|
400
|
+
ws.send(JSON.stringify({ kind: 'hello', agent: runnerIdentity.agent, runner: runnerIdentity.runner }));
|
|
401
|
+
}
|
|
402
|
+
catch { /* reconnect loop handles failures */ }
|
|
403
|
+
tailTimer = setInterval(() => pumpNewLines(ws, offsets, runnerIdentity), 500);
|
|
383
404
|
});
|
|
384
405
|
ws.addEventListener('message', (ev) => {
|
|
385
406
|
const raw = decodeWsPayload(ev.data);
|
|
@@ -393,6 +414,11 @@ export async function startRun(opts) {
|
|
|
393
414
|
return;
|
|
394
415
|
}
|
|
395
416
|
if (m.kind === 'steer' && m.text) {
|
|
417
|
+
if (!steerMatchesRunner(m, runnerIdentity)) {
|
|
418
|
+
if (opts.verbose)
|
|
419
|
+
process.stderr.write(`sickr: ignored steer for another agent/session\n`);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
396
422
|
const decision = decideSteer(m);
|
|
397
423
|
if (decision.target === 'pty' && decision.bytes != null) {
|
|
398
424
|
// Submit-fix (2026-06-01): Claude Code's TUI treats a burst of
|
|
@@ -459,7 +485,7 @@ async function loadWsShim() {
|
|
|
459
485
|
throw new Error('`ws` is unavailable. It ships as an optional dependency of @sickr/cli; if it failed to install, try: `npm install -g ws`.');
|
|
460
486
|
}
|
|
461
487
|
}
|
|
462
|
-
function pumpNewLines(ws, offsets) {
|
|
488
|
+
function pumpNewLines(ws, offsets, identity) {
|
|
463
489
|
const dir = runsDir();
|
|
464
490
|
if (!existsSync(dir))
|
|
465
491
|
return;
|
|
@@ -484,6 +510,12 @@ function pumpNewLines(ws, offsets) {
|
|
|
484
510
|
for (const fragment of splitJsonObjects(line)) {
|
|
485
511
|
try {
|
|
486
512
|
const event = JSON.parse(fragment);
|
|
513
|
+
if (event.runner && event.runner !== identity.runner)
|
|
514
|
+
continue;
|
|
515
|
+
if (!event.runner && event.agent && normTarget(event.agent) !== normTarget(identity.agent))
|
|
516
|
+
continue;
|
|
517
|
+
if (event.session)
|
|
518
|
+
identity.sessions.add(event.session);
|
|
487
519
|
ws.send(JSON.stringify({ kind: 'event', event }));
|
|
488
520
|
}
|
|
489
521
|
catch { /* skip malformed */ }
|