@sickr/cli 0.9.12 → 0.9.14

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.
Files changed (2) hide show
  1. package/dist/run.js +45 -2
  2. package/package.json +1 -1
package/dist/run.js CHANGED
@@ -24,7 +24,7 @@ import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, rea
24
24
  import { homedir } from 'node:os';
25
25
  import { join } from 'node:path';
26
26
  import { randomUUID } from 'node:crypto';
27
- import { execFileSync } from 'node:child_process';
27
+ import { execFileSync, spawn } from 'node:child_process';
28
28
  import { setTimeout as sleep } from 'node:timers/promises';
29
29
  import { readCredentials } from './auth.js';
30
30
  import { runsDir } from './recorder.js';
@@ -143,6 +143,7 @@ export function stripAnsi(input) {
143
143
  export function cleanPtyResponse(input, lastPrompt = '') {
144
144
  const prompt = lastPrompt.trim();
145
145
  return stripAnsi(input)
146
+ .replace(/[\u2800-\u28ff]/g, '')
146
147
  .replace(/\r/g, '\n')
147
148
  .split('\n')
148
149
  .map((line) => line.replace(/^>+\s?/, '').trimEnd())
@@ -152,7 +153,7 @@ export function cleanPtyResponse(input, lastPrompt = '') {
152
153
  return false;
153
154
  if (prompt && trimmed === prompt)
154
155
  return false;
155
- if (trimmed === 'Send a message (/? for help)')
156
+ if (trimmed.includes('Send a message (/? for help)'))
156
157
  return false;
157
158
  return true;
158
159
  })
@@ -166,6 +167,7 @@ export class HooklessPtyEventSynth {
166
167
  inputBuffer = '';
167
168
  outputBuffer = '';
168
169
  lastPrompt = '';
170
+ awaitingResponse = false;
169
171
  responseTimer = null;
170
172
  constructor(identity, send, responseDelayMs = 700) {
171
173
  this.identity = identity;
@@ -193,9 +195,13 @@ export class HooklessPtyEventSynth {
193
195
  if (!prompt)
194
196
  return;
195
197
  this.lastPrompt = prompt;
198
+ this.outputBuffer = '';
199
+ this.awaitingResponse = true;
196
200
  this.sendEvent('prompt', 'Prompt', prompt);
197
201
  }
198
202
  observeOutput(chunk) {
203
+ if (!this.awaitingResponse)
204
+ return;
199
205
  this.outputBuffer += chunk;
200
206
  if (this.responseTimer)
201
207
  clearTimeout(this.responseTimer);
@@ -210,6 +216,7 @@ export class HooklessPtyEventSynth {
210
216
  this.outputBuffer = '';
211
217
  if (!detail)
212
218
  return;
219
+ this.awaitingResponse = false;
213
220
  this.sendEvent('response', this.identity.agent, detail);
214
221
  }
215
222
  sendEvent(kind, label, detail) {
@@ -396,6 +403,40 @@ export function providerArgsFor(agent, existingArgs) {
396
403
  return ['run', model, ...existingArgs];
397
404
  return existingArgs;
398
405
  }
406
+ function ollamaHost() {
407
+ return process.env.OLLAMA_HOST || 'http://127.0.0.1:11434';
408
+ }
409
+ async function ollamaServerReady() {
410
+ try {
411
+ const res = await fetch(`${ollamaHost().replace(/\/$/, '')}/api/tags`, { signal: AbortSignal.timeout(1000) });
412
+ return res.ok;
413
+ }
414
+ catch {
415
+ return false;
416
+ }
417
+ }
418
+ export async function ensureOllamaServer(agentBin, opts = {}) {
419
+ if (await ollamaServerReady())
420
+ return;
421
+ const needsShell = process.platform === 'win32' && /\.(cmd|bat)$/i.test(agentBin);
422
+ const child = spawn(agentBin, ['serve'], {
423
+ cwd: process.cwd(),
424
+ env: process.env,
425
+ detached: true,
426
+ stdio: 'ignore',
427
+ windowsHide: true,
428
+ shell: needsShell,
429
+ });
430
+ child.unref();
431
+ const attempts = opts.attempts ?? 20;
432
+ const delayMs = opts.delayMs ?? 250;
433
+ for (let i = 0; i < attempts; i++) {
434
+ await sleep(delayMs);
435
+ if (await ollamaServerReady())
436
+ return;
437
+ }
438
+ throw new Error('Ollama server did not start. Try running `ollama serve` in another terminal, then retry `sickr run ollama`.');
439
+ }
399
440
  export async function startRun(opts) {
400
441
  const creds = readCredentials();
401
442
  if (!creds) {
@@ -417,6 +458,8 @@ export async function startRun(opts) {
417
458
  ensureRecordingHooks(opts.agent);
418
459
  const agentBin = resolveAgent(opts.agent);
419
460
  const provider = providerForAgent(opts.agent);
461
+ if (provider === 'ollama')
462
+ await ensureOllamaServer(agentBin);
420
463
  const agentLabel = provider ? PROVIDERS[provider].recordLabel : opts.agent;
421
464
  const runnerId = randomUUID();
422
465
  const runnerIdentity = { agent: agentLabel, runner: runnerId, sessions: new Set() };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sickr/cli",
3
- "version": "0.9.12",
3
+ "version": "0.9.14",
4
4
  "type": "module",
5
5
  "description": "npx @sickr/cli - replay, live look, and workflow orchestration for AI coding agents.",
6
6
  "bin": {