@sickr/cli 0.9.8 → 0.9.10
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 +45 -4
- package/package.json +2 -2
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,31 @@ 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
|
+
}
|
|
127
|
+
export function normalizeRunEventForRunner(event, identity) {
|
|
128
|
+
if (event.runner && event.runner !== identity.runner)
|
|
129
|
+
return null;
|
|
130
|
+
if (!event.runner && event.agent && normTarget(event.agent) !== normTarget(identity.agent))
|
|
131
|
+
return null;
|
|
132
|
+
if (!event.agent)
|
|
133
|
+
event.agent = identity.agent;
|
|
134
|
+
if (!event.runner)
|
|
135
|
+
event.runner = identity.runner;
|
|
136
|
+
if (event.session)
|
|
137
|
+
identity.sessions.add(event.session);
|
|
138
|
+
return event;
|
|
139
|
+
}
|
|
114
140
|
export function decideSteer(msg, defaultMode = 'pty') {
|
|
115
141
|
const text = String(msg.text ?? '');
|
|
116
142
|
if (!text)
|
|
@@ -282,6 +308,10 @@ export async function startRun(opts) {
|
|
|
282
308
|
// browser /r/<urlid> stays empty until events flow.
|
|
283
309
|
ensureRecordingHooks(opts.agent);
|
|
284
310
|
const agentBin = resolveAgent(opts.agent);
|
|
311
|
+
const provider = providerForAgent(opts.agent);
|
|
312
|
+
const agentLabel = provider ? PROVIDERS[provider].recordLabel : opts.agent;
|
|
313
|
+
const runnerId = randomUUID();
|
|
314
|
+
const runnerIdentity = { agent: agentLabel, runner: runnerId, sessions: new Set() };
|
|
285
315
|
const cols = process.stdout.columns ?? 80;
|
|
286
316
|
const rows = process.stdout.rows ?? 24;
|
|
287
317
|
// mode=auto (default) injects the agent's "no prompt / full perms" flag
|
|
@@ -307,7 +337,7 @@ export async function startRun(opts) {
|
|
|
307
337
|
name: 'xterm-256color',
|
|
308
338
|
cols, rows,
|
|
309
339
|
cwd: process.cwd(),
|
|
310
|
-
env: process.env,
|
|
340
|
+
env: { ...process.env, SICKR_RUN_INSTANCE: runnerId, SICKR_RUN_AGENT: agentLabel },
|
|
311
341
|
});
|
|
312
342
|
// Wire stdio: parent stdin -> pty; pty -> parent stdout.
|
|
313
343
|
if (process.stdin.isTTY)
|
|
@@ -379,7 +409,11 @@ export async function startRun(opts) {
|
|
|
379
409
|
let opened = false;
|
|
380
410
|
ws.addEventListener('open', () => {
|
|
381
411
|
opened = true;
|
|
382
|
-
|
|
412
|
+
try {
|
|
413
|
+
ws.send(JSON.stringify({ kind: 'hello', agent: runnerIdentity.agent, runner: runnerIdentity.runner }));
|
|
414
|
+
}
|
|
415
|
+
catch { /* reconnect loop handles failures */ }
|
|
416
|
+
tailTimer = setInterval(() => pumpNewLines(ws, offsets, runnerIdentity), 500);
|
|
383
417
|
});
|
|
384
418
|
ws.addEventListener('message', (ev) => {
|
|
385
419
|
const raw = decodeWsPayload(ev.data);
|
|
@@ -393,6 +427,11 @@ export async function startRun(opts) {
|
|
|
393
427
|
return;
|
|
394
428
|
}
|
|
395
429
|
if (m.kind === 'steer' && m.text) {
|
|
430
|
+
if (!steerMatchesRunner(m, runnerIdentity)) {
|
|
431
|
+
if (opts.verbose)
|
|
432
|
+
process.stderr.write(`sickr: ignored steer for another agent/session\n`);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
396
435
|
const decision = decideSteer(m);
|
|
397
436
|
if (decision.target === 'pty' && decision.bytes != null) {
|
|
398
437
|
// Submit-fix (2026-06-01): Claude Code's TUI treats a burst of
|
|
@@ -459,7 +498,7 @@ async function loadWsShim() {
|
|
|
459
498
|
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
499
|
}
|
|
461
500
|
}
|
|
462
|
-
function pumpNewLines(ws, offsets) {
|
|
501
|
+
function pumpNewLines(ws, offsets, identity) {
|
|
463
502
|
const dir = runsDir();
|
|
464
503
|
if (!existsSync(dir))
|
|
465
504
|
return;
|
|
@@ -483,7 +522,9 @@ function pumpNewLines(ws, offsets) {
|
|
|
483
522
|
for (const line of result.lines) {
|
|
484
523
|
for (const fragment of splitJsonObjects(line)) {
|
|
485
524
|
try {
|
|
486
|
-
const event = JSON.parse(fragment);
|
|
525
|
+
const event = normalizeRunEventForRunner(JSON.parse(fragment), identity);
|
|
526
|
+
if (!event)
|
|
527
|
+
continue;
|
|
487
528
|
ws.send(JSON.stringify({ kind: 'event', event }));
|
|
488
529
|
}
|
|
489
530
|
catch { /* skip malformed */ }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sickr/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "npx @sickr/cli - replay, live look, and workflow orchestration for AI coding agents.",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"access": "public"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build": "tsc",
|
|
17
|
+
"build": "node --max-old-space-size=4096 ./node_modules/typescript/bin/tsc",
|
|
18
18
|
"test": "vitest run",
|
|
19
19
|
"dev": "tsc -w",
|
|
20
20
|
"prepublishOnly": "npm run build && npm test && node scripts/pre-publish-check.mjs"
|