life-pulse 2.3.4 → 2.3.7
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/agent.js +1 -1
- package/dist/cli.js +1 -9
- package/dist/crm.d.ts +1 -1
- package/dist/crm.js +50 -21
- package/dist/tui.d.ts +0 -5
- package/dist/tui.js +15 -25
- package/dist/ui/app.d.ts +1 -1
- package/dist/ui/app.js +15 -1
- package/dist/ui/progress.js +6 -2
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -362,7 +362,7 @@ progress, onCard) {
|
|
|
362
362
|
|
|
363
363
|
Output JSON (no markdown, no code fences):
|
|
364
364
|
{
|
|
365
|
-
"greeting": "
|
|
365
|
+
"greeting": "Short, warm one-liner. No counts or statistics.",
|
|
366
366
|
"promises": [{ "title": "...", "urgency": "now|today|this_week", "who": "...", "when_due": "...", "options": [{"label":"...","description":"..."}] }],
|
|
367
367
|
"blockers": [{ "title": "...", "urgency": "now|today|this_week", "who": "...", "waiting_since": "...", "options": [{"label":"...","description":"..."}] }],
|
|
368
368
|
"bumps": [{ "title": "...", "who": "...", "context": "...", "options": [{"label":"...","description":"..."}] }],
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { saveState, saveDecisions } from './state.js';
|
|
|
6
6
|
// ProgressRenderer unused — removed
|
|
7
7
|
import { InkProgress } from './ui/progress.js';
|
|
8
8
|
import { addTodo, resolveTodos, pruneOld } from './todo.js';
|
|
9
|
-
import { renderIntro,
|
|
9
|
+
import { renderIntro, renderCRMList, renderCRMContact, pickCard, renderSection, renderSectionHeader, renderHandled, renderDivider, renderBrief, renderArchetype, Spinner, destroyUI, MAG, CYN, RED, AMB, MID, DIM } from './tui.js';
|
|
10
10
|
import { needsDiscovery, discoverPlatforms, savePlatforms } from './platforms.js';
|
|
11
11
|
import { generateArchetype } from './archetype.js';
|
|
12
12
|
import { runPermissionFlow, getMissingPermissions } from './permissions.js';
|
|
@@ -89,14 +89,6 @@ async function showCRM(apiKey, opts) {
|
|
|
89
89
|
return false;
|
|
90
90
|
}
|
|
91
91
|
opts?.spinner?.stop();
|
|
92
|
-
// The reveal — silence, then the numbers drop
|
|
93
|
-
const innerCircle = crm.threads.filter(t => t.threadTemp === 'hot' || t.threadTemp === 'warm' || t.msgs30d > 20).length;
|
|
94
|
-
const aboutToBreak = crm.threads.filter(t => t.waitingOnYou).length;
|
|
95
|
-
await renderReveal({
|
|
96
|
-
conversations: crm.threads.length,
|
|
97
|
-
innerCircle,
|
|
98
|
-
aboutToBreak,
|
|
99
|
-
});
|
|
100
92
|
const calendarContext = opts?.calendarContext ?? '';
|
|
101
93
|
const hour = new Date().getHours();
|
|
102
94
|
const timeOfDay = hour < 12 ? 'morning' : hour < 17 ? 'afternoon' : 'evening';
|
package/dist/crm.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export interface CRM {
|
|
|
35
35
|
generatedAt: string;
|
|
36
36
|
}
|
|
37
37
|
export declare function buildCRM(): Promise<CRM>;
|
|
38
|
-
export declare function streamEnrichedCRM(crm: CRM,
|
|
38
|
+
export declare function streamEnrichedCRM(crm: CRM, _apiKey?: string, opts?: {
|
|
39
39
|
calendarContext?: string;
|
|
40
40
|
timeOfDay?: string;
|
|
41
41
|
onBrief?: (brief: string) => void;
|
package/dist/crm.js
CHANGED
|
@@ -12,7 +12,8 @@ import { join } from 'path';
|
|
|
12
12
|
import { openDb, safeQuery } from './db.js';
|
|
13
13
|
import { resolveName } from './contacts.js';
|
|
14
14
|
import * as rply from './rply-client.js';
|
|
15
|
-
|
|
15
|
+
// OpenRouter key for fast CRM enrichment (GLM-4.7)
|
|
16
|
+
const OPENROUTER_KEY = process.env.OPENROUTER_API_KEY || '';
|
|
16
17
|
import dayjs from 'dayjs';
|
|
17
18
|
import relativeTime from 'dayjs/plugin/relativeTime.js';
|
|
18
19
|
dayjs.extend(relativeTime);
|
|
@@ -324,9 +325,9 @@ function buildCRMFromSQLite() {
|
|
|
324
325
|
return { threads: threads.slice(0, 25), yourAvgResponseSec: yourAvg, generatedAt: dayjs().toISOString() };
|
|
325
326
|
}
|
|
326
327
|
// ─── Stream-enrich CRM ──────────────────────────────────────────
|
|
327
|
-
export async function* streamEnrichedCRM(crm,
|
|
328
|
+
export async function* streamEnrichedCRM(crm, _apiKey, opts) {
|
|
328
329
|
const top = crm.threads.slice(0, 10);
|
|
329
|
-
if (!top.length || !
|
|
330
|
+
if (!top.length || !OPENROUTER_KEY) {
|
|
330
331
|
for (const t of top)
|
|
331
332
|
yield t;
|
|
332
333
|
return;
|
|
@@ -367,13 +368,20 @@ export async function* streamEnrichedCRM(crm, apiKey, opts) {
|
|
|
367
368
|
curPriority = null;
|
|
368
369
|
};
|
|
369
370
|
try {
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
371
|
+
const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
|
372
|
+
method: 'POST',
|
|
373
|
+
headers: {
|
|
374
|
+
'Authorization': `Bearer ${OPENROUTER_KEY}`,
|
|
375
|
+
'Content-Type': 'application/json',
|
|
376
|
+
'HTTP-Referer': 'https://life-pulse.dev',
|
|
377
|
+
},
|
|
378
|
+
body: JSON.stringify({
|
|
379
|
+
model: 'z-ai/glm-4.7',
|
|
380
|
+
max_tokens: 3000,
|
|
381
|
+
stream: true,
|
|
382
|
+
messages: [{
|
|
383
|
+
role: 'user',
|
|
384
|
+
content: `You are a personal chief of staff analyzing someone's message history this ${timeOfDay}.
|
|
377
385
|
|
|
378
386
|
Recent iMessage threads below. → = they sent, ← = contact sent.
|
|
379
387
|
${calendar}
|
|
@@ -404,12 +412,16 @@ STYLE GUIDE:
|
|
|
404
412
|
- If someone has a calendar meeting today, weave that in naturally.
|
|
405
413
|
|
|
406
414
|
${contactBlocks}`,
|
|
407
|
-
|
|
415
|
+
}],
|
|
416
|
+
}),
|
|
408
417
|
});
|
|
409
|
-
|
|
418
|
+
if (!res.ok || !res.body)
|
|
419
|
+
throw new Error(`OpenRouter ${res.status}`);
|
|
420
|
+
// Stream SSE deltas — yield contacts the moment each block completes
|
|
410
421
|
let section = 'preamble';
|
|
411
422
|
const briefLines = [];
|
|
412
423
|
let lineBuf = '';
|
|
424
|
+
let sseBuf = '';
|
|
413
425
|
const processLine = (rawLine) => {
|
|
414
426
|
const line = rawLine.trim();
|
|
415
427
|
if (/^CONTACTS:?$/i.test(line)) {
|
|
@@ -442,15 +454,32 @@ ${contactBlocks}`,
|
|
|
442
454
|
briefLines.push(line);
|
|
443
455
|
}
|
|
444
456
|
};
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
457
|
+
const decoder = new TextDecoder();
|
|
458
|
+
const reader = res.body.getReader();
|
|
459
|
+
while (true) {
|
|
460
|
+
const { done, value } = await reader.read();
|
|
461
|
+
if (done)
|
|
462
|
+
break;
|
|
463
|
+
sseBuf += decoder.decode(value, { stream: true });
|
|
464
|
+
const sseLines = sseBuf.split('\n');
|
|
465
|
+
sseBuf = sseLines.pop() || '';
|
|
466
|
+
for (const sseLine of sseLines) {
|
|
467
|
+
if (!sseLine.startsWith('data: ') || sseLine === 'data: [DONE]')
|
|
468
|
+
continue;
|
|
469
|
+
try {
|
|
470
|
+
const json = JSON.parse(sseLine.slice(6));
|
|
471
|
+
const delta = json.choices?.[0]?.delta?.content;
|
|
472
|
+
if (!delta)
|
|
473
|
+
continue;
|
|
474
|
+
lineBuf += delta;
|
|
475
|
+
const parts = lineBuf.split('\n');
|
|
476
|
+
lineBuf = parts.pop() || '';
|
|
477
|
+
for (const line of parts)
|
|
478
|
+
processLine(line);
|
|
479
|
+
while (ready.length)
|
|
480
|
+
yield ready.shift();
|
|
481
|
+
}
|
|
482
|
+
catch { }
|
|
454
483
|
}
|
|
455
484
|
}
|
|
456
485
|
// Flush remaining buffer
|
package/dist/tui.d.ts
CHANGED
|
@@ -17,11 +17,6 @@ export declare const HD: import("chalk").ChalkInstance;
|
|
|
17
17
|
export declare function renderIntro(name?: string): Promise<void>;
|
|
18
18
|
export declare function renderGreeting(name?: string): void;
|
|
19
19
|
export declare function renderContextLine(): void;
|
|
20
|
-
export declare function renderReveal(stats: {
|
|
21
|
-
conversations: number;
|
|
22
|
-
innerCircle: number;
|
|
23
|
-
aboutToBreak: number;
|
|
24
|
-
}): Promise<void>;
|
|
25
20
|
export declare function renderCRMList(contacts: {
|
|
26
21
|
name: string;
|
|
27
22
|
lastMsg: {
|
package/dist/tui.js
CHANGED
|
@@ -155,11 +155,20 @@ export async function renderIntro(name) {
|
|
|
155
155
|
}
|
|
156
156
|
// "Hope you're having an incredible morning."
|
|
157
157
|
const h = new Date().getHours();
|
|
158
|
+
const greeting = timeGreeting(h);
|
|
158
159
|
process.stdout.write(' ');
|
|
159
|
-
await typewrite(
|
|
160
|
+
await typewrite(greeting, G3, 18);
|
|
160
161
|
process.stdout.write('\n\n');
|
|
162
|
+
await _sleep(1200);
|
|
163
|
+
// Build greeting lines for Ink to preserve
|
|
164
|
+
const greetLines = [''];
|
|
165
|
+
greetLines.push(` ${G1(hey)}`);
|
|
166
|
+
if (loc)
|
|
167
|
+
greetLines.push(` ${G2(`I see you're in ${loc.city}. ${weatherRemark(loc.weather)}`)}`);
|
|
168
|
+
greetLines.push(` ${G3(greeting)}`);
|
|
169
|
+
greetLines.push('');
|
|
161
170
|
if (USE_INK)
|
|
162
|
-
ink.initInk();
|
|
171
|
+
ink.initInk(greetLines);
|
|
163
172
|
}
|
|
164
173
|
// ─── Greeting (legacy / non-interactive fallback) ───
|
|
165
174
|
export function renderGreeting(name) {
|
|
@@ -184,21 +193,6 @@ export function renderContextLine() {
|
|
|
184
193
|
out(` ${C.dim(`${days[d.getDay()]} · ${h12}:${min} ${ampm}`)}`);
|
|
185
194
|
out('');
|
|
186
195
|
}
|
|
187
|
-
// ─── Reveal (the moment after discovery) ───
|
|
188
|
-
export async function renderReveal(stats) {
|
|
189
|
-
await _sleep(600);
|
|
190
|
-
process.stdout.write(' ');
|
|
191
|
-
await typewrite(`${stats.conversations} conversations.`, G2, 22);
|
|
192
|
-
await _sleep(400);
|
|
193
|
-
process.stdout.write(' ');
|
|
194
|
-
await typewrite(`${stats.innerCircle} people who matter.`, G1, 25);
|
|
195
|
-
await _sleep(500);
|
|
196
|
-
if (stats.aboutToBreak > 0) {
|
|
197
|
-
process.stdout.write(' ');
|
|
198
|
-
await typewrite(`${stats.aboutToBreak} thread${stats.aboutToBreak === 1 ? '' : 's'} about to break.`, chalk.bold.hex('#c0caf5'), 28);
|
|
199
|
-
}
|
|
200
|
-
process.stdout.write('\n\n');
|
|
201
|
-
}
|
|
202
196
|
// ─── CRM List ───
|
|
203
197
|
export function renderCRMList(contacts) {
|
|
204
198
|
for (const t of contacts.slice(0, 12)) {
|
|
@@ -296,14 +290,12 @@ export async function renderCRMStream(source, spinner) {
|
|
|
296
290
|
out('');
|
|
297
291
|
first = false;
|
|
298
292
|
}
|
|
299
|
-
|
|
300
|
-
const ago = shortAgo(t.lastMsg?.ago || '');
|
|
301
|
-
const gap = Math.max(2, w - 4 - name.length - ago.length);
|
|
302
|
-
out(` ${C.hd(name)}${' '.repeat(gap)}${C.dim(ago)}`);
|
|
293
|
+
out(` ${C.hd(t.name.slice(0, 40))}`);
|
|
303
294
|
if (t.relationship) {
|
|
304
295
|
out(` ${C.mid(t.relationship.slice(0, w - 8))}`);
|
|
305
296
|
}
|
|
306
297
|
rendered.push(t);
|
|
298
|
+
await _sleep(180);
|
|
307
299
|
}
|
|
308
300
|
if (first)
|
|
309
301
|
spinner?.stop();
|
|
@@ -312,12 +304,10 @@ export async function renderCRMStream(source, spinner) {
|
|
|
312
304
|
}
|
|
313
305
|
export async function renderCRMContact(t) {
|
|
314
306
|
const w = W();
|
|
315
|
-
|
|
316
|
-
const ago = shortAgo(t.lastMsg?.ago || '');
|
|
317
|
-
const gap = Math.max(2, w - 4 - name.length - ago.length);
|
|
318
|
-
out(` ${C.hd(name)}${' '.repeat(gap)}${C.dim(ago)}`);
|
|
307
|
+
out(` ${C.hd(t.name.slice(0, 40))}`);
|
|
319
308
|
if (t.relationship)
|
|
320
309
|
out(` ${C.mid(t.relationship.slice(0, w - 8))}`);
|
|
310
|
+
await _sleep(180);
|
|
321
311
|
}
|
|
322
312
|
// ─── Destroy (call at exit) ───
|
|
323
313
|
export function destroyUI() {
|
package/dist/ui/app.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface CardData {
|
|
|
17
17
|
who?: string;
|
|
18
18
|
urgency?: string;
|
|
19
19
|
}
|
|
20
|
-
export declare function initInk(): void;
|
|
20
|
+
export declare function initInk(seedLines?: string[]): void;
|
|
21
21
|
export declare function destroy(): void;
|
|
22
22
|
export declare function log(text: string): void;
|
|
23
23
|
export declare function gap(): void;
|
package/dist/ui/app.js
CHANGED
|
@@ -89,6 +89,7 @@ const App = () => {
|
|
|
89
89
|
let _inst = null;
|
|
90
90
|
let _started = false;
|
|
91
91
|
const _origLog = console.log.bind(console);
|
|
92
|
+
let _seedLines = [];
|
|
92
93
|
function ensureStarted() {
|
|
93
94
|
if (_started)
|
|
94
95
|
return;
|
|
@@ -96,6 +97,15 @@ function ensureStarted() {
|
|
|
96
97
|
process.stdout.write('\x1Bc'); // clear screen
|
|
97
98
|
process.stdout.write('\x1B[?25l'); // hide cursor
|
|
98
99
|
_inst = inkRender(React.createElement(App));
|
|
100
|
+
// Seed initial lines (e.g. greeting from intro)
|
|
101
|
+
if (_seedLines.length) {
|
|
102
|
+
const lines = _seedLines;
|
|
103
|
+
_seedLines = [];
|
|
104
|
+
// Small delay to let React mount before pushing state
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
_update?.(p => ({ ...p, lines: [...lines, ...p.lines] }));
|
|
107
|
+
}, 50);
|
|
108
|
+
}
|
|
99
109
|
// Redirect console.log through Ink
|
|
100
110
|
console.log = (...args) => {
|
|
101
111
|
const text = args.map(a => typeof a === 'string' ? a : String(a)).join(' ');
|
|
@@ -105,7 +115,11 @@ function ensureStarted() {
|
|
|
105
115
|
};
|
|
106
116
|
}
|
|
107
117
|
// ─── Exports ──────────────────────────────────────
|
|
108
|
-
export function initInk() {
|
|
118
|
+
export function initInk(seedLines) {
|
|
119
|
+
if (seedLines)
|
|
120
|
+
_seedLines = seedLines;
|
|
121
|
+
ensureStarted();
|
|
122
|
+
}
|
|
109
123
|
export function destroy() {
|
|
110
124
|
console.log = _origLog;
|
|
111
125
|
_inst?.unmount();
|
package/dist/ui/progress.js
CHANGED
|
@@ -71,7 +71,8 @@ export class InkProgress {
|
|
|
71
71
|
const t = this.tools.find(t => t.name === name && !t.done);
|
|
72
72
|
if (t)
|
|
73
73
|
t.done = true;
|
|
74
|
-
|
|
74
|
+
// Delay repaint so the label lingers on screen
|
|
75
|
+
setTimeout(() => this.paint(), 400);
|
|
75
76
|
}
|
|
76
77
|
workerStart(id, label) {
|
|
77
78
|
this._thinking = false;
|
|
@@ -113,6 +114,9 @@ export class InkProgress {
|
|
|
113
114
|
}
|
|
114
115
|
pause() { this._paused = true; hideProgress(); }
|
|
115
116
|
resume() { this._paused = false; this.paint(); }
|
|
116
|
-
stop() {
|
|
117
|
+
stop() {
|
|
118
|
+
// Let the final state linger so it doesn't vanish instantly
|
|
119
|
+
setTimeout(() => hideProgress(), 800);
|
|
120
|
+
}
|
|
117
121
|
clear() { hideProgress(); }
|
|
118
122
|
}
|