sakuraai 0.0.2 → 0.0.4
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/index.js +74 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -379,16 +379,16 @@ function findFile(sessionId) {
|
|
|
379
379
|
}
|
|
380
380
|
function readMessages(file) {
|
|
381
381
|
const msgs = [];
|
|
382
|
-
let
|
|
382
|
+
let lastUserSecs = null;
|
|
383
383
|
for (const entry of jsonlEntries(file)) {
|
|
384
384
|
if (entry?.isSidechain === true) continue;
|
|
385
385
|
const tsStr = entry?.timestamp ?? "";
|
|
386
386
|
const tsSecs = parseIsoUnix(tsStr);
|
|
387
|
-
if (startSecs == null) startSecs = tsSecs;
|
|
388
387
|
if (entry?.type === "user") {
|
|
389
388
|
if (entry?.userType !== "external") continue;
|
|
390
389
|
const text = extractTextFromContent(entry?.message?.content);
|
|
391
390
|
if (text) {
|
|
391
|
+
lastUserSecs = tsSecs;
|
|
392
392
|
msgs.push({
|
|
393
393
|
id: entry?.uuid ?? "",
|
|
394
394
|
role: "user",
|
|
@@ -400,15 +400,16 @@ function readMessages(file) {
|
|
|
400
400
|
} else if (entry?.type === "assistant") {
|
|
401
401
|
const { text, hasTools } = extractAssistantText(entry?.message?.content);
|
|
402
402
|
if (!text) continue;
|
|
403
|
-
const
|
|
404
|
-
const
|
|
403
|
+
const u = entry?.message?.usage;
|
|
404
|
+
const total = (u?.input_tokens ?? 0) + (u?.cache_read_input_tokens ?? 0) + (u?.cache_creation_input_tokens ?? 0);
|
|
405
|
+
const elapsed = lastUserSecs != null && tsSecs != null ? Math.max(0, tsSecs - lastUserSecs) : void 0;
|
|
405
406
|
msgs.push({
|
|
406
407
|
id: entry?.uuid ?? "",
|
|
407
408
|
role: "assistant",
|
|
408
409
|
content: text,
|
|
409
410
|
timestamp: tsStr,
|
|
410
411
|
hasToolUse: hasTools,
|
|
411
|
-
inputTokens:
|
|
412
|
+
inputTokens: total > 0 ? total : void 0,
|
|
412
413
|
elapsedSecs: elapsed
|
|
413
414
|
});
|
|
414
415
|
}
|
|
@@ -419,33 +420,74 @@ function messages(sessionId) {
|
|
|
419
420
|
const file = findFile(sessionId);
|
|
420
421
|
return file ? readMessages(file) : [];
|
|
421
422
|
}
|
|
423
|
+
function sessionCwd(sessionId) {
|
|
424
|
+
const file = findFile(sessionId);
|
|
425
|
+
if (!file) return void 0;
|
|
426
|
+
for (const entry of jsonlEntries(file)) {
|
|
427
|
+
if (typeof entry?.cwd === "string" && entry.cwd) return entry.cwd;
|
|
428
|
+
}
|
|
429
|
+
return void 0;
|
|
430
|
+
}
|
|
422
431
|
function send(sessionId, message, onData) {
|
|
423
432
|
return new Promise((resolve) => {
|
|
424
433
|
const bin = process.env.CLAUDE_BIN || "claude";
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
434
|
+
const cwd = sessionCwd(sessionId);
|
|
435
|
+
const child = spawn(
|
|
436
|
+
bin,
|
|
437
|
+
[
|
|
438
|
+
"--resume",
|
|
439
|
+
sessionId,
|
|
440
|
+
"-p",
|
|
441
|
+
message,
|
|
442
|
+
"--output-format",
|
|
443
|
+
"stream-json",
|
|
444
|
+
"--include-partial-messages",
|
|
445
|
+
"--verbose"
|
|
446
|
+
],
|
|
447
|
+
{
|
|
448
|
+
cwd: cwd && fs3.existsSync(cwd) ? cwd : void 0,
|
|
449
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
450
|
+
}
|
|
451
|
+
);
|
|
452
|
+
let buf = "";
|
|
453
|
+
let finalText = "";
|
|
429
454
|
let err = "";
|
|
430
455
|
child.stdout.on("data", (d) => {
|
|
431
|
-
|
|
432
|
-
|
|
456
|
+
buf += d.toString();
|
|
457
|
+
let nl;
|
|
458
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
459
|
+
const line = buf.slice(0, nl);
|
|
460
|
+
buf = buf.slice(nl + 1);
|
|
461
|
+
if (!line.trim()) continue;
|
|
462
|
+
try {
|
|
463
|
+
const delta = textDelta(JSON.parse(line));
|
|
464
|
+
if (delta) {
|
|
465
|
+
finalText += delta;
|
|
466
|
+
onData?.(delta);
|
|
467
|
+
}
|
|
468
|
+
} catch {
|
|
469
|
+
}
|
|
470
|
+
}
|
|
433
471
|
});
|
|
434
472
|
child.stderr.on("data", (d) => err += d.toString());
|
|
435
|
-
child.on(
|
|
436
|
-
"error",
|
|
437
|
-
(e) => resolve({ ok: false, output: "", error: e.message })
|
|
438
|
-
);
|
|
473
|
+
child.on("error", (e) => resolve({ ok: false, output: "", error: e.message }));
|
|
439
474
|
child.on(
|
|
440
475
|
"close",
|
|
441
476
|
(code) => resolve({
|
|
442
477
|
ok: code === 0,
|
|
443
|
-
output:
|
|
478
|
+
output: finalText.trim(),
|
|
444
479
|
error: code === 0 ? void 0 : err.trim() || `exited ${code}`
|
|
445
480
|
})
|
|
446
481
|
);
|
|
447
482
|
});
|
|
448
483
|
}
|
|
484
|
+
function textDelta(ev) {
|
|
485
|
+
const obj = ev?.type === "stream_event" ? ev.event : ev;
|
|
486
|
+
if (obj?.type === "content_block_delta" && obj?.delta?.type === "text_delta") {
|
|
487
|
+
return obj.delta.text ?? null;
|
|
488
|
+
}
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
449
491
|
var PROJECTS_DIR;
|
|
450
492
|
var init_claude = __esm({
|
|
451
493
|
"src/runtime/claude.ts"() {
|
|
@@ -530,6 +572,7 @@ function readMessages2(file) {
|
|
|
530
572
|
return [];
|
|
531
573
|
}
|
|
532
574
|
const msgs = [];
|
|
575
|
+
let lastUserSecs = null;
|
|
533
576
|
for (const line of content.split("\n")) {
|
|
534
577
|
if (!line.trim()) continue;
|
|
535
578
|
let entry;
|
|
@@ -549,7 +592,16 @@ function readMessages2(file) {
|
|
|
549
592
|
const text = part?.text ?? "";
|
|
550
593
|
if (!text) continue;
|
|
551
594
|
const ts = entry?.timestamp ?? "";
|
|
552
|
-
|
|
595
|
+
const tsSecs = parseIsoUnix(ts);
|
|
596
|
+
if (role === "user") lastUserSecs = tsSecs;
|
|
597
|
+
msgs.push({
|
|
598
|
+
id: ts,
|
|
599
|
+
role,
|
|
600
|
+
content: text,
|
|
601
|
+
timestamp: ts,
|
|
602
|
+
hasToolUse: false,
|
|
603
|
+
elapsedSecs: role === "assistant" && lastUserSecs != null && tsSecs != null ? Math.max(0, tsSecs - lastUserSecs) : void 0
|
|
604
|
+
});
|
|
553
605
|
}
|
|
554
606
|
return msgs;
|
|
555
607
|
}
|
|
@@ -673,8 +725,8 @@ async function messages3(sessionId) {
|
|
|
673
725
|
} catch {
|
|
674
726
|
}
|
|
675
727
|
}
|
|
676
|
-
const startMs = msgRows[0]?.time_created ?? 0;
|
|
677
728
|
const out = [];
|
|
729
|
+
let lastUserMs = null;
|
|
678
730
|
for (const m of msgRows) {
|
|
679
731
|
let v;
|
|
680
732
|
try {
|
|
@@ -687,13 +739,15 @@ async function messages3(sessionId) {
|
|
|
687
739
|
const text = (partsMap.get(m.id) ?? []).join("");
|
|
688
740
|
if (!text) continue;
|
|
689
741
|
const mtime = Math.floor(m.time_created / 1e3);
|
|
742
|
+
if (role === "user") lastUserMs = m.time_created;
|
|
690
743
|
out.push({
|
|
691
744
|
id: m.id,
|
|
692
745
|
role,
|
|
693
746
|
content: text,
|
|
694
747
|
timestamp: chronoIso(mtime),
|
|
695
748
|
hasToolUse: false,
|
|
696
|
-
|
|
749
|
+
// Per-turn duration: assistant time − the user msg that started it.
|
|
750
|
+
elapsedSecs: role === "assistant" && lastUserMs != null ? Math.max(0, Math.floor((m.time_created - lastUserMs) / 1e3)) : void 0
|
|
697
751
|
});
|
|
698
752
|
}
|
|
699
753
|
return out;
|
|
@@ -1736,7 +1790,7 @@ var init_pair = __esm({
|
|
|
1736
1790
|
import { Command } from "commander";
|
|
1737
1791
|
|
|
1738
1792
|
// src/version.ts
|
|
1739
|
-
var VERSION = "0.0.
|
|
1793
|
+
var VERSION = "0.0.4";
|
|
1740
1794
|
|
|
1741
1795
|
// src/index.ts
|
|
1742
1796
|
init_config();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sakuraai",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Sakura Agent CLI + local runtime for managing AI coding sessions (Claude Code, Codex, OpenCode) and reaching them privately from the Sakura mobile app over Tailscale",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|