bertrand 0.20.0 → 0.21.0
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/README.md +3 -2
- package/dist/bertrand.js +182 -57
- package/dist/dashboard/assets/index-D_l3EPhC.css +1 -0
- package/dist/dashboard/assets/index-mg96b7q-.js +684 -0
- package/dist/dashboard/index.html +3 -3
- package/dist/run-screen.js +430 -501
- package/package.json +1 -1
- package/dist/dashboard/assets/index-DgvcDkSY.js +0 -673
- package/dist/dashboard/assets/index-qVHXGTGL.css +0 -1
- package/dist/dashboard/assets/workbox-window.prod.es5-BBnX5xw4.js +0 -2
- package/dist/dashboard/manifest.webmanifest +0 -1
- package/dist/dashboard/sw.js +0 -1
- package/dist/dashboard/workbox-e4022e15.js +0 -1
package/dist/run-screen.js
CHANGED
|
@@ -15,17 +15,12 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// src/tui/run-screen.tsx
|
|
18
|
-
import { writeFileSync as writeFileSync2 } from "fs";
|
|
18
|
+
import { writeFileSync as writeFileSync2, appendFileSync } from "fs";
|
|
19
19
|
import { render } from "@orchetron/storm";
|
|
20
20
|
|
|
21
|
-
// src/tui/screens/
|
|
22
|
-
import { useState as
|
|
23
|
-
import { useTui } from "@orchetron/storm";
|
|
24
|
-
|
|
25
|
-
// src/tui/screens/project-picker/index.tsx
|
|
26
|
-
import { useMemo as useMemo2 } from "react";
|
|
27
|
-
import { Box as Box5, Text as Text6 } from "@orchetron/storm";
|
|
28
|
-
import { existsSync as existsSync2 } from "fs";
|
|
21
|
+
// src/tui/screens/launch/index.tsx
|
|
22
|
+
import { useMemo as useMemo2, useState as useState3 } from "react";
|
|
23
|
+
import { Box as Box5, Text as Text6, useTui } from "@orchetron/storm";
|
|
29
24
|
|
|
30
25
|
// src/tui/components/app-details.tsx
|
|
31
26
|
import { Box, Text } from "@orchetron/storm";
|
|
@@ -565,6 +560,16 @@ function StatusDot({ status }) {
|
|
|
565
560
|
children: "\u25CF"
|
|
566
561
|
}, undefined, false, undefined, this);
|
|
567
562
|
}
|
|
563
|
+
// src/db/queries/sessions.ts
|
|
564
|
+
import { eq, and, inArray, sql as sql2 } from "drizzle-orm";
|
|
565
|
+
|
|
566
|
+
// src/db/client.ts
|
|
567
|
+
import { Database } from "bun:sqlite";
|
|
568
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
569
|
+
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
|
570
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
571
|
+
import { dirname } from "path";
|
|
572
|
+
|
|
568
573
|
// src/lib/projects/registry.ts
|
|
569
574
|
import {
|
|
570
575
|
existsSync,
|
|
@@ -575,7 +580,6 @@ import {
|
|
|
575
580
|
statSync,
|
|
576
581
|
writeFileSync
|
|
577
582
|
} from "fs";
|
|
578
|
-
import { randomBytes } from "crypto";
|
|
579
583
|
import { join as join2 } from "path";
|
|
580
584
|
|
|
581
585
|
// src/lib/paths.ts
|
|
@@ -586,6 +590,7 @@ var paths = {
|
|
|
586
590
|
root: join(homedir(), BERTRAND_DIR),
|
|
587
591
|
hooks: join(homedir(), BERTRAND_DIR, "hooks"),
|
|
588
592
|
sessions: join(homedir(), BERTRAND_DIR, "sessions"),
|
|
593
|
+
runtime: join(homedir(), BERTRAND_DIR, "run"),
|
|
589
594
|
db: join(homedir(), BERTRAND_DIR, "bertrand.db"),
|
|
590
595
|
syncEnv: join(homedir(), BERTRAND_DIR, "sync.env")
|
|
591
596
|
};
|
|
@@ -616,14 +621,6 @@ function readRegistry() {
|
|
|
616
621
|
return null;
|
|
617
622
|
}
|
|
618
623
|
}
|
|
619
|
-
function writeRegistry(registry) {
|
|
620
|
-
const path = registryPath();
|
|
621
|
-
mkdirSync(_registryDir, { recursive: true });
|
|
622
|
-
const tmp = `${path}.tmp-${process.pid}-${process.hrtime.bigint()}-${randomBytes(4).toString("hex")}`;
|
|
623
|
-
writeFileSync(tmp, JSON.stringify(registry, null, 2) + `
|
|
624
|
-
`);
|
|
625
|
-
renameSync(tmp, path);
|
|
626
|
-
}
|
|
627
624
|
function recoverFromDisk() {
|
|
628
625
|
const dir = projectsDir();
|
|
629
626
|
if (!existsSync(dir))
|
|
@@ -673,52 +670,6 @@ function listProjects() {
|
|
|
673
670
|
function getActiveProjectSlug() {
|
|
674
671
|
return loadRegistry()?.activeProjectSlug ?? DEFAULT_PROJECT_SLUG;
|
|
675
672
|
}
|
|
676
|
-
function setActiveProjectSlug(slug) {
|
|
677
|
-
const registry = loadRegistry();
|
|
678
|
-
if (!registry) {
|
|
679
|
-
throw new Error(`No registry to update \u2014 create a project first (no ${registryPath()} and no ${projectsDir()}/* to recover from)`);
|
|
680
|
-
}
|
|
681
|
-
if (!registry.projects.some((p) => p.slug === slug)) {
|
|
682
|
-
throw new Error(`Unknown project slug "${slug}"`);
|
|
683
|
-
}
|
|
684
|
-
registry.activeProjectSlug = slug;
|
|
685
|
-
const idx = registry.projects.findIndex((p) => p.slug === slug);
|
|
686
|
-
if (idx !== -1) {
|
|
687
|
-
registry.projects[idx].lastUsedAt = new Date().toISOString();
|
|
688
|
-
}
|
|
689
|
-
writeRegistry(registry);
|
|
690
|
-
}
|
|
691
|
-
function registerProject(opts) {
|
|
692
|
-
const registry = loadRegistry() ?? {
|
|
693
|
-
activeProjectSlug: opts.slug,
|
|
694
|
-
projects: []
|
|
695
|
-
};
|
|
696
|
-
if (registry.projects.some((p) => p.slug === opts.slug)) {
|
|
697
|
-
throw new Error(`Project "${opts.slug}" already exists`);
|
|
698
|
-
}
|
|
699
|
-
const now = new Date().toISOString();
|
|
700
|
-
const entry = {
|
|
701
|
-
slug: opts.slug,
|
|
702
|
-
name: opts.name,
|
|
703
|
-
color: opts.color,
|
|
704
|
-
createdAt: now,
|
|
705
|
-
lastUsedAt: now
|
|
706
|
-
};
|
|
707
|
-
registry.projects.push(entry);
|
|
708
|
-
writeRegistry(registry);
|
|
709
|
-
return entry;
|
|
710
|
-
}
|
|
711
|
-
function removeProject(slug) {
|
|
712
|
-
const registry = loadRegistry();
|
|
713
|
-
if (!registry)
|
|
714
|
-
return;
|
|
715
|
-
registry.projects = registry.projects.filter((p) => p.slug !== slug);
|
|
716
|
-
if (registry.activeProjectSlug === slug) {
|
|
717
|
-
const survivor = [...registry.projects].sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt))[0];
|
|
718
|
-
registry.activeProjectSlug = survivor?.slug ?? DEFAULT_PROJECT_SLUG;
|
|
719
|
-
}
|
|
720
|
-
writeRegistry(registry);
|
|
721
|
-
}
|
|
722
673
|
function isRegistry(value) {
|
|
723
674
|
if (typeof value !== "object" || value === null)
|
|
724
675
|
return false;
|
|
@@ -747,13 +698,6 @@ function projectPaths(slug) {
|
|
|
747
698
|
};
|
|
748
699
|
}
|
|
749
700
|
|
|
750
|
-
// src/db/client.ts
|
|
751
|
-
import { Database } from "bun:sqlite";
|
|
752
|
-
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
753
|
-
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
|
754
|
-
import { mkdirSync as mkdirSync2 } from "fs";
|
|
755
|
-
import { dirname } from "path";
|
|
756
|
-
|
|
757
701
|
// src/lib/projects/resolve.ts
|
|
758
702
|
var _cached = null;
|
|
759
703
|
function resolveActiveProject() {
|
|
@@ -771,9 +715,6 @@ function resolveActiveProject() {
|
|
|
771
715
|
_cached = { slug, name, ...projectPaths(slug) };
|
|
772
716
|
return _cached;
|
|
773
717
|
}
|
|
774
|
-
function _resetActiveProjectCache() {
|
|
775
|
-
_cached = null;
|
|
776
|
-
}
|
|
777
718
|
|
|
778
719
|
// src/db/schema.ts
|
|
779
720
|
var exports_schema = {};
|
|
@@ -936,207 +877,6 @@ function hasSessionsTable(sqlite) {
|
|
|
936
877
|
return row !== null;
|
|
937
878
|
}
|
|
938
879
|
|
|
939
|
-
// src/lib/format.ts
|
|
940
|
-
var SECOND = 1000;
|
|
941
|
-
var MINUTE = 60 * SECOND;
|
|
942
|
-
var HOUR = 60 * MINUTE;
|
|
943
|
-
var DAY = 24 * HOUR;
|
|
944
|
-
function formatDuration(ms) {
|
|
945
|
-
if (ms < MINUTE)
|
|
946
|
-
return `${Math.round(ms / SECOND)}s`;
|
|
947
|
-
const days = Math.floor(ms / DAY);
|
|
948
|
-
const hours = Math.floor(ms % DAY / HOUR);
|
|
949
|
-
const minutes = Math.floor(ms % HOUR / MINUTE);
|
|
950
|
-
if (days > 0)
|
|
951
|
-
return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
|
|
952
|
-
if (hours > 0)
|
|
953
|
-
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
954
|
-
return `${minutes}m`;
|
|
955
|
-
}
|
|
956
|
-
function formatAgo(isoOrDate) {
|
|
957
|
-
const date = typeof isoOrDate === "string" ? new Date(isoOrDate) : isoOrDate;
|
|
958
|
-
const ms = Date.now() - date.getTime();
|
|
959
|
-
if (ms < MINUTE)
|
|
960
|
-
return "just now";
|
|
961
|
-
if (ms < HOUR)
|
|
962
|
-
return `${Math.floor(ms / MINUTE)}m ago`;
|
|
963
|
-
if (ms < DAY)
|
|
964
|
-
return `${Math.floor(ms / HOUR)}h ago`;
|
|
965
|
-
if (ms < 2 * DAY)
|
|
966
|
-
return "yesterday";
|
|
967
|
-
if (ms < 7 * DAY)
|
|
968
|
-
return `${Math.floor(ms / DAY)}d ago`;
|
|
969
|
-
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// src/tui/screens/project-picker/index.tsx
|
|
973
|
-
import { jsxDEV as jsxDEV6, Fragment } from "react/jsx-dev-runtime";
|
|
974
|
-
var SLUG_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
975
|
-
function statsFor(slug) {
|
|
976
|
-
const dbFile = projectPaths(slug).db;
|
|
977
|
-
if (!existsSync2(dbFile))
|
|
978
|
-
return { total: 0, active: 0, unreadable: false };
|
|
979
|
-
try {
|
|
980
|
-
const db = getDbForProject(slug);
|
|
981
|
-
const all = db.select({ status: sessions.status }).from(sessions).all();
|
|
982
|
-
return {
|
|
983
|
-
total: all.length,
|
|
984
|
-
active: all.filter((s) => s.status === "active" || s.status === "waiting").length,
|
|
985
|
-
unreadable: false
|
|
986
|
-
};
|
|
987
|
-
} catch {
|
|
988
|
-
return { total: 0, active: 0, unreadable: true };
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
function projectRow(entry, isActive) {
|
|
992
|
-
const stats = statsFor(entry.slug);
|
|
993
|
-
const sessionsLabel = stats.unreadable ? "?" : `${stats.active}/${stats.total}`;
|
|
994
|
-
const lastUsed = formatAgo(entry.lastUsedAt);
|
|
995
|
-
return {
|
|
996
|
-
value: entry.slug,
|
|
997
|
-
label: `${entry.slug} ${entry.name} ${sessionsLabel}`,
|
|
998
|
-
meta: lastUsed,
|
|
999
|
-
display: (isCursor) => {
|
|
1000
|
-
const cursorColor = "green";
|
|
1001
|
-
const slugColor = isCursor ? cursorColor : undefined;
|
|
1002
|
-
const nameColor = isCursor ? cursorColor : undefined;
|
|
1003
|
-
return /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1004
|
-
children: [
|
|
1005
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1006
|
-
color: cursorColor,
|
|
1007
|
-
bold: true,
|
|
1008
|
-
children: isCursor ? "\u276F " : " "
|
|
1009
|
-
}, undefined, false, undefined, this),
|
|
1010
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1011
|
-
bold: isActive,
|
|
1012
|
-
color: isActive ? "gold" : slugColor,
|
|
1013
|
-
children: isActive ? "\u25CF " : " "
|
|
1014
|
-
}, undefined, false, undefined, this),
|
|
1015
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1016
|
-
color: slugColor,
|
|
1017
|
-
bold: isCursor || isActive,
|
|
1018
|
-
children: entry.slug.padEnd(16)
|
|
1019
|
-
}, undefined, false, undefined, this),
|
|
1020
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1021
|
-
color: nameColor,
|
|
1022
|
-
dim: !isCursor,
|
|
1023
|
-
children: entry.name.padEnd(20)
|
|
1024
|
-
}, undefined, false, undefined, this),
|
|
1025
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1026
|
-
dim: true,
|
|
1027
|
-
children: stats.unreadable ? " (unreadable)" : ` ${sessionsLabel} active/total`
|
|
1028
|
-
}, undefined, false, undefined, this)
|
|
1029
|
-
]
|
|
1030
|
-
}, undefined, true, undefined, this);
|
|
1031
|
-
}
|
|
1032
|
-
};
|
|
1033
|
-
}
|
|
1034
|
-
function ProjectPicker({ onSelect }) {
|
|
1035
|
-
const allProjects = useMemo2(() => listProjects(), []);
|
|
1036
|
-
const activeSlug = useMemo2(() => getActiveProjectSlug(), []);
|
|
1037
|
-
const items = useMemo2(() => {
|
|
1038
|
-
return allProjects.slice().sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt)).map((p) => projectRow(p, p.slug === activeSlug));
|
|
1039
|
-
}, [allProjects, activeSlug]);
|
|
1040
|
-
const suggestions = useMemo2(() => allProjects.map((p) => p.slug), [allProjects]);
|
|
1041
|
-
const knownSlugs = useMemo2(() => new Set(allProjects.map((p) => p.slug)), [allProjects]);
|
|
1042
|
-
const select = (selection) => {
|
|
1043
|
-
onSelect(selection);
|
|
1044
|
-
};
|
|
1045
|
-
const handleSubmit = (value) => {
|
|
1046
|
-
const trimmed = value.trim();
|
|
1047
|
-
if (!trimmed)
|
|
1048
|
-
return;
|
|
1049
|
-
if (knownSlugs.has(trimmed)) {
|
|
1050
|
-
select({ type: "select", slug: trimmed });
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
if (!SLUG_PATTERN.test(trimmed)) {
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
select({ type: "create", slug: trimmed });
|
|
1057
|
-
};
|
|
1058
|
-
return /* @__PURE__ */ jsxDEV6(Box5, {
|
|
1059
|
-
flexDirection: "column",
|
|
1060
|
-
paddingY: 1,
|
|
1061
|
-
gap: 1,
|
|
1062
|
-
children: [
|
|
1063
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1064
|
-
marginX: 1,
|
|
1065
|
-
children: /* @__PURE__ */ jsxDEV6(Logo, {}, undefined, false, undefined, this)
|
|
1066
|
-
}, undefined, false, undefined, this),
|
|
1067
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1068
|
-
flexDirection: "column",
|
|
1069
|
-
marginX: 2,
|
|
1070
|
-
gap: 1,
|
|
1071
|
-
children: [
|
|
1072
|
-
/* @__PURE__ */ jsxDEV6(AppDetails, {}, undefined, false, undefined, this),
|
|
1073
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1074
|
-
flexDirection: "column",
|
|
1075
|
-
gap: 1,
|
|
1076
|
-
children: [
|
|
1077
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1078
|
-
flexDirection: "row",
|
|
1079
|
-
gap: 1,
|
|
1080
|
-
children: [
|
|
1081
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1082
|
-
bold: true,
|
|
1083
|
-
children: "Projects"
|
|
1084
|
-
}, undefined, false, undefined, this),
|
|
1085
|
-
allProjects.length > 0 ? /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1086
|
-
children: [
|
|
1087
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1088
|
-
dim: true,
|
|
1089
|
-
children: "\xB7"
|
|
1090
|
-
}, undefined, false, undefined, this),
|
|
1091
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1092
|
-
dim: true,
|
|
1093
|
-
children: [
|
|
1094
|
-
allProjects.length,
|
|
1095
|
-
" total",
|
|
1096
|
-
activeSlug ? ` \xB7 active: ${activeSlug}` : ""
|
|
1097
|
-
]
|
|
1098
|
-
}, undefined, true, undefined, this)
|
|
1099
|
-
]
|
|
1100
|
-
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV6(Text6, {
|
|
1101
|
-
dim: true,
|
|
1102
|
-
children: "\xB7 none \u2014 type a slug to create your first project"
|
|
1103
|
-
}, undefined, false, undefined, this)
|
|
1104
|
-
]
|
|
1105
|
-
}, undefined, true, undefined, this),
|
|
1106
|
-
/* @__PURE__ */ jsxDEV6(Picker, {
|
|
1107
|
-
mode: "single",
|
|
1108
|
-
items,
|
|
1109
|
-
isFocused: true,
|
|
1110
|
-
maxVisible: 16,
|
|
1111
|
-
suggest: suggestions,
|
|
1112
|
-
placeholder: "Filter or type a new slug to create\u2026",
|
|
1113
|
-
emptyHint: "No projects. Type a slug to create one.",
|
|
1114
|
-
onSubmit: handleSubmit,
|
|
1115
|
-
onKey: (e) => {
|
|
1116
|
-
if (e.key === "c" && e.ctrl) {
|
|
1117
|
-
select({ type: "quit" });
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
}, undefined, false, undefined, this),
|
|
1121
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1122
|
-
dim: true,
|
|
1123
|
-
children: "\u2191\u2193 navigate \xB7 enter select \xB7 type to filter or create new \xB7 ctrl+c quit"
|
|
1124
|
-
}, undefined, false, undefined, this)
|
|
1125
|
-
]
|
|
1126
|
-
}, undefined, true, undefined, this)
|
|
1127
|
-
]
|
|
1128
|
-
}, undefined, true, undefined, this)
|
|
1129
|
-
]
|
|
1130
|
-
}, undefined, true, undefined, this);
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
// src/tui/screens/launch/index.tsx
|
|
1134
|
-
import { useMemo as useMemo3, useState as useState3 } from "react";
|
|
1135
|
-
import { Box as Box6, Text as Text7 } from "@orchetron/storm";
|
|
1136
|
-
|
|
1137
|
-
// src/db/queries/sessions.ts
|
|
1138
|
-
import { eq, and, inArray, sql as sql2 } from "drizzle-orm";
|
|
1139
|
-
|
|
1140
880
|
// src/lib/id.ts
|
|
1141
881
|
import { nanoid } from "nanoid";
|
|
1142
882
|
|
|
@@ -1186,6 +926,39 @@ function unarchiveSession(id) {
|
|
|
1186
926
|
return { ok: true, session: updated };
|
|
1187
927
|
}
|
|
1188
928
|
|
|
929
|
+
// src/lib/format.ts
|
|
930
|
+
var SECOND = 1000;
|
|
931
|
+
var MINUTE = 60 * SECOND;
|
|
932
|
+
var HOUR = 60 * MINUTE;
|
|
933
|
+
var DAY = 24 * HOUR;
|
|
934
|
+
function formatDuration(ms) {
|
|
935
|
+
if (ms < MINUTE)
|
|
936
|
+
return `${Math.round(ms / SECOND)}s`;
|
|
937
|
+
const days = Math.floor(ms / DAY);
|
|
938
|
+
const hours = Math.floor(ms % DAY / HOUR);
|
|
939
|
+
const minutes = Math.floor(ms % HOUR / MINUTE);
|
|
940
|
+
if (days > 0)
|
|
941
|
+
return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
|
|
942
|
+
if (hours > 0)
|
|
943
|
+
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
944
|
+
return `${minutes}m`;
|
|
945
|
+
}
|
|
946
|
+
function formatAgo(isoOrDate) {
|
|
947
|
+
const date = typeof isoOrDate === "string" ? new Date(isoOrDate) : isoOrDate;
|
|
948
|
+
const ms = Date.now() - date.getTime();
|
|
949
|
+
if (ms < MINUTE)
|
|
950
|
+
return "just now";
|
|
951
|
+
if (ms < HOUR)
|
|
952
|
+
return `${Math.floor(ms / MINUTE)}m ago`;
|
|
953
|
+
if (ms < DAY)
|
|
954
|
+
return `${Math.floor(ms / HOUR)}h ago`;
|
|
955
|
+
if (ms < 2 * DAY)
|
|
956
|
+
return "yesterday";
|
|
957
|
+
if (ms < 7 * DAY)
|
|
958
|
+
return `${Math.floor(ms / DAY)}d ago`;
|
|
959
|
+
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
960
|
+
}
|
|
961
|
+
|
|
1189
962
|
// src/lib/parse-session-name.ts
|
|
1190
963
|
function parseSessionName(input) {
|
|
1191
964
|
const trimmed = input.trim().replace(/^\/+|\/+$/g, "");
|
|
@@ -1207,7 +980,7 @@ function parseSessionName(input) {
|
|
|
1207
980
|
}
|
|
1208
981
|
|
|
1209
982
|
// src/tui/screens/launch/index.tsx
|
|
1210
|
-
import { jsxDEV as
|
|
983
|
+
import { jsxDEV as jsxDEV6, Fragment } from "react/jsx-dev-runtime";
|
|
1211
984
|
var STATUS_COLOR = {
|
|
1212
985
|
paused: "gold",
|
|
1213
986
|
waiting: "red",
|
|
@@ -1241,14 +1014,14 @@ function sessionRow(s) {
|
|
|
1241
1014
|
const textColor = isCursor ? cursorColor : color;
|
|
1242
1015
|
const slugColor = isCursor ? cursorColor : undefined;
|
|
1243
1016
|
const dimText = !isCursor && (disabled || isArchived);
|
|
1244
|
-
return /* @__PURE__ */
|
|
1017
|
+
return /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1245
1018
|
children: [
|
|
1246
|
-
/* @__PURE__ */
|
|
1019
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1247
1020
|
color: cursorColor,
|
|
1248
1021
|
bold: true,
|
|
1249
1022
|
children: isCursor ? "\u276F " : " "
|
|
1250
1023
|
}, undefined, false, undefined, this),
|
|
1251
|
-
/* @__PURE__ */
|
|
1024
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1252
1025
|
color: dotColor,
|
|
1253
1026
|
bold: isCursor,
|
|
1254
1027
|
children: [
|
|
@@ -1256,17 +1029,17 @@ function sessionRow(s) {
|
|
|
1256
1029
|
" "
|
|
1257
1030
|
]
|
|
1258
1031
|
}, undefined, true, undefined, this),
|
|
1259
|
-
/* @__PURE__ */
|
|
1032
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1260
1033
|
color: textColor,
|
|
1261
1034
|
bold: isCursor,
|
|
1262
1035
|
dim: dimText,
|
|
1263
1036
|
children: status.padEnd(8)
|
|
1264
1037
|
}, undefined, false, undefined, this),
|
|
1265
|
-
/* @__PURE__ */
|
|
1038
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1266
1039
|
color: slugColor,
|
|
1267
1040
|
children: " "
|
|
1268
1041
|
}, undefined, false, undefined, this),
|
|
1269
|
-
/* @__PURE__ */
|
|
1042
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1270
1043
|
color: slugColor,
|
|
1271
1044
|
bold: isCursor,
|
|
1272
1045
|
dim: dimText,
|
|
@@ -1285,12 +1058,13 @@ function categoryHeader(categoryPath) {
|
|
|
1285
1058
|
};
|
|
1286
1059
|
}
|
|
1287
1060
|
function Launch({ onSelect }) {
|
|
1061
|
+
const { exit } = useTui();
|
|
1288
1062
|
const [error, setError] = useState3(null);
|
|
1289
1063
|
const [notice, setNotice] = useState3(null);
|
|
1290
1064
|
const [showArchived, setShowArchived] = useState3(false);
|
|
1291
1065
|
const [refreshKey, setRefreshKey] = useState3(0);
|
|
1292
|
-
const allSessions =
|
|
1293
|
-
const visibleSessions =
|
|
1066
|
+
const allSessions = useMemo2(() => getAllSessions({ excludeArchived: !showArchived }), [showArchived, refreshKey]);
|
|
1067
|
+
const visibleSessions = useMemo2(() => {
|
|
1294
1068
|
return allSessions.filter((s) => {
|
|
1295
1069
|
const st = s.session.status;
|
|
1296
1070
|
if (st === "paused" || st === "waiting")
|
|
@@ -1308,7 +1082,7 @@ function Launch({ onSelect }) {
|
|
|
1308
1082
|
return recencyKey(b).localeCompare(recencyKey(a));
|
|
1309
1083
|
});
|
|
1310
1084
|
}, [allSessions, showArchived]);
|
|
1311
|
-
const items =
|
|
1085
|
+
const items = useMemo2(() => {
|
|
1312
1086
|
const rows = [];
|
|
1313
1087
|
let lastCategory = null;
|
|
1314
1088
|
for (const s of visibleSessions) {
|
|
@@ -1320,7 +1094,7 @@ function Launch({ onSelect }) {
|
|
|
1320
1094
|
}
|
|
1321
1095
|
return rows;
|
|
1322
1096
|
}, [visibleSessions]);
|
|
1323
|
-
const suggestions =
|
|
1097
|
+
const suggestions = useMemo2(() => {
|
|
1324
1098
|
const categories2 = new Set;
|
|
1325
1099
|
const names = [];
|
|
1326
1100
|
for (const s of allSessions) {
|
|
@@ -1329,7 +1103,7 @@ function Launch({ onSelect }) {
|
|
|
1329
1103
|
}
|
|
1330
1104
|
return [...categories2, ...names];
|
|
1331
1105
|
}, [allSessions]);
|
|
1332
|
-
const sessionByValue =
|
|
1106
|
+
const sessionByValue = useMemo2(() => {
|
|
1333
1107
|
const map = new Map;
|
|
1334
1108
|
for (const s of allSessions) {
|
|
1335
1109
|
map.set(`${s.categoryPath}/${s.session.slug}`, s);
|
|
@@ -1363,6 +1137,7 @@ function Launch({ onSelect }) {
|
|
|
1363
1137
|
};
|
|
1364
1138
|
const select = (selection) => {
|
|
1365
1139
|
onSelect(selection);
|
|
1140
|
+
exit();
|
|
1366
1141
|
};
|
|
1367
1142
|
const handleSubmit = (value) => {
|
|
1368
1143
|
const existing = sessionByValue.get(value);
|
|
@@ -1381,7 +1156,7 @@ function Launch({ onSelect }) {
|
|
|
1381
1156
|
setError(e instanceof Error ? e.message : "Invalid name");
|
|
1382
1157
|
}
|
|
1383
1158
|
};
|
|
1384
|
-
const counts =
|
|
1159
|
+
const counts = useMemo2(() => {
|
|
1385
1160
|
let paused = 0;
|
|
1386
1161
|
let waiting = 0;
|
|
1387
1162
|
let archived = 0;
|
|
@@ -1395,56 +1170,56 @@ function Launch({ onSelect }) {
|
|
|
1395
1170
|
}
|
|
1396
1171
|
return { paused, waiting, archived };
|
|
1397
1172
|
}, [visibleSessions]);
|
|
1398
|
-
return /* @__PURE__ */
|
|
1173
|
+
return /* @__PURE__ */ jsxDEV6(Box5, {
|
|
1399
1174
|
flexDirection: "column",
|
|
1400
1175
|
paddingY: 1,
|
|
1401
1176
|
gap: 1,
|
|
1402
1177
|
children: [
|
|
1403
|
-
/* @__PURE__ */
|
|
1178
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1404
1179
|
marginX: 1,
|
|
1405
|
-
children: /* @__PURE__ */
|
|
1180
|
+
children: /* @__PURE__ */ jsxDEV6(Logo, {}, undefined, false, undefined, this)
|
|
1406
1181
|
}, undefined, false, undefined, this),
|
|
1407
|
-
/* @__PURE__ */
|
|
1182
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1408
1183
|
flexDirection: "column",
|
|
1409
1184
|
marginX: 2,
|
|
1410
1185
|
gap: 1,
|
|
1411
1186
|
children: [
|
|
1412
|
-
/* @__PURE__ */
|
|
1413
|
-
/* @__PURE__ */
|
|
1187
|
+
/* @__PURE__ */ jsxDEV6(AppDetails, {}, undefined, false, undefined, this),
|
|
1188
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1414
1189
|
flexDirection: "column",
|
|
1415
1190
|
gap: 1,
|
|
1416
1191
|
children: [
|
|
1417
|
-
/* @__PURE__ */
|
|
1192
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1418
1193
|
flexDirection: "row",
|
|
1419
1194
|
gap: 1,
|
|
1420
1195
|
children: [
|
|
1421
|
-
/* @__PURE__ */
|
|
1196
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1422
1197
|
bold: true,
|
|
1423
1198
|
children: "Sessions"
|
|
1424
1199
|
}, undefined, false, undefined, this),
|
|
1425
|
-
visibleSessions.length === 0 ? /* @__PURE__ */
|
|
1200
|
+
visibleSessions.length === 0 ? /* @__PURE__ */ jsxDEV6(Text6, {
|
|
1426
1201
|
dim: true,
|
|
1427
1202
|
children: "\xB7 none \u2014 type category/slug to create"
|
|
1428
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */
|
|
1203
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1429
1204
|
children: [
|
|
1430
|
-
/* @__PURE__ */
|
|
1205
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1431
1206
|
dim: true,
|
|
1432
1207
|
children: "\xB7"
|
|
1433
1208
|
}, undefined, false, undefined, this),
|
|
1434
|
-
/* @__PURE__ */
|
|
1209
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1435
1210
|
color: "gold",
|
|
1436
1211
|
children: [
|
|
1437
1212
|
counts.paused,
|
|
1438
1213
|
" paused"
|
|
1439
1214
|
]
|
|
1440
1215
|
}, undefined, true, undefined, this),
|
|
1441
|
-
counts.waiting > 0 && /* @__PURE__ */
|
|
1216
|
+
counts.waiting > 0 && /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1442
1217
|
children: [
|
|
1443
|
-
/* @__PURE__ */
|
|
1218
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1444
1219
|
dim: true,
|
|
1445
1220
|
children: "\xB7"
|
|
1446
1221
|
}, undefined, false, undefined, this),
|
|
1447
|
-
/* @__PURE__ */
|
|
1222
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1448
1223
|
color: "red",
|
|
1449
1224
|
dim: true,
|
|
1450
1225
|
children: [
|
|
@@ -1454,13 +1229,13 @@ function Launch({ onSelect }) {
|
|
|
1454
1229
|
}, undefined, true, undefined, this)
|
|
1455
1230
|
]
|
|
1456
1231
|
}, undefined, true, undefined, this),
|
|
1457
|
-
showArchived && counts.archived > 0 && /* @__PURE__ */
|
|
1232
|
+
showArchived && counts.archived > 0 && /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1458
1233
|
children: [
|
|
1459
|
-
/* @__PURE__ */
|
|
1234
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1460
1235
|
dim: true,
|
|
1461
1236
|
children: "\xB7"
|
|
1462
1237
|
}, undefined, false, undefined, this),
|
|
1463
|
-
/* @__PURE__ */
|
|
1238
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1464
1239
|
color: "purple",
|
|
1465
1240
|
dim: true,
|
|
1466
1241
|
children: [
|
|
@@ -1474,7 +1249,7 @@ function Launch({ onSelect }) {
|
|
|
1474
1249
|
}, undefined, true, undefined, this)
|
|
1475
1250
|
]
|
|
1476
1251
|
}, undefined, true, undefined, this),
|
|
1477
|
-
/* @__PURE__ */
|
|
1252
|
+
/* @__PURE__ */ jsxDEV6(Picker, {
|
|
1478
1253
|
mode: "single",
|
|
1479
1254
|
items,
|
|
1480
1255
|
isFocused: true,
|
|
@@ -1495,15 +1270,15 @@ function Launch({ onSelect }) {
|
|
|
1495
1270
|
}
|
|
1496
1271
|
}
|
|
1497
1272
|
}, undefined, false, undefined, this),
|
|
1498
|
-
notice && /* @__PURE__ */
|
|
1273
|
+
notice && /* @__PURE__ */ jsxDEV6(Text6, {
|
|
1499
1274
|
color: "green",
|
|
1500
1275
|
children: notice
|
|
1501
1276
|
}, undefined, false, undefined, this),
|
|
1502
|
-
error && /* @__PURE__ */
|
|
1277
|
+
error && /* @__PURE__ */ jsxDEV6(Text6, {
|
|
1503
1278
|
color: "red",
|
|
1504
1279
|
children: error
|
|
1505
1280
|
}, undefined, false, undefined, this),
|
|
1506
|
-
/* @__PURE__ */
|
|
1281
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1507
1282
|
dim: true,
|
|
1508
1283
|
children: [
|
|
1509
1284
|
"\u2191\u2193 navigate \xB7 \u2190\u2192 skip category \xB7 enter continue/create \xB7 ctrl+a",
|
|
@@ -1523,104 +1298,9 @@ function Launch({ onSelect }) {
|
|
|
1523
1298
|
}, undefined, true, undefined, this);
|
|
1524
1299
|
}
|
|
1525
1300
|
|
|
1526
|
-
// src/lib/projects/create.ts
|
|
1527
|
-
import { mkdirSync as mkdirSync4 } from "fs";
|
|
1528
|
-
|
|
1529
|
-
// src/db/migrate.ts
|
|
1530
|
-
import { Database as Database2 } from "bun:sqlite";
|
|
1531
|
-
import { drizzle as drizzle2 } from "drizzle-orm/bun-sqlite";
|
|
1532
|
-
import { migrate as migrate2 } from "drizzle-orm/bun-sqlite/migrator";
|
|
1533
|
-
import { mkdirSync as mkdirSync3 } from "fs";
|
|
1534
|
-
import { dirname as dirname2 } from "path";
|
|
1535
|
-
var MIGRATIONS_FOLDER2 = import.meta.dir + "/migrations";
|
|
1536
|
-
function runMigrations(dbPath) {
|
|
1537
|
-
const target = dbPath ?? resolveActiveProject().db;
|
|
1538
|
-
mkdirSync3(dirname2(target), { recursive: true });
|
|
1539
|
-
const sqlite = new Database2(target);
|
|
1540
|
-
sqlite.exec("PRAGMA busy_timeout = 5000");
|
|
1541
|
-
sqlite.exec("PRAGMA journal_mode = WAL");
|
|
1542
|
-
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
1543
|
-
const db = drizzle2(sqlite);
|
|
1544
|
-
migrate2(db, { migrationsFolder: MIGRATIONS_FOLDER2 });
|
|
1545
|
-
sqlite.close();
|
|
1546
|
-
}
|
|
1547
|
-
if (false) {}
|
|
1548
|
-
|
|
1549
|
-
// src/lib/projects/create.ts
|
|
1550
|
-
function createProject(opts) {
|
|
1551
|
-
registerProject({ slug: opts.slug, name: opts.name ?? opts.slug });
|
|
1552
|
-
try {
|
|
1553
|
-
const paths2 = projectPaths(opts.slug);
|
|
1554
|
-
mkdirSync4(paths2.root, { recursive: true });
|
|
1555
|
-
runMigrations(paths2.db);
|
|
1556
|
-
} catch (err) {
|
|
1557
|
-
try {
|
|
1558
|
-
removeProject(opts.slug);
|
|
1559
|
-
} catch {}
|
|
1560
|
-
throw err;
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
// src/tui/screens/startup/index.tsx
|
|
1565
|
-
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
1566
|
-
function Startup({
|
|
1567
|
-
skipProjectPicker,
|
|
1568
|
-
initialProjectSlug,
|
|
1569
|
-
onSelect
|
|
1570
|
-
}) {
|
|
1571
|
-
const { exit } = useTui();
|
|
1572
|
-
const [activeSlug, setActiveSlug] = useState4(skipProjectPicker ? initialProjectSlug : null);
|
|
1573
|
-
const finish = (s) => {
|
|
1574
|
-
onSelect(s);
|
|
1575
|
-
exit();
|
|
1576
|
-
};
|
|
1577
|
-
const handleProjectSelect = (s) => {
|
|
1578
|
-
switch (s.type) {
|
|
1579
|
-
case "quit":
|
|
1580
|
-
finish({ type: "quit" });
|
|
1581
|
-
return;
|
|
1582
|
-
case "create":
|
|
1583
|
-
createProject({ slug: s.slug });
|
|
1584
|
-
setActiveProjectSlug(s.slug);
|
|
1585
|
-
_resetActiveProjectCache();
|
|
1586
|
-
setActiveSlug(s.slug);
|
|
1587
|
-
return;
|
|
1588
|
-
case "select":
|
|
1589
|
-
setActiveProjectSlug(s.slug);
|
|
1590
|
-
_resetActiveProjectCache();
|
|
1591
|
-
setActiveSlug(s.slug);
|
|
1592
|
-
return;
|
|
1593
|
-
}
|
|
1594
|
-
};
|
|
1595
|
-
const handleLaunchSelect = (s) => {
|
|
1596
|
-
if (s.type === "quit" || activeSlug === null) {
|
|
1597
|
-
finish({ type: "quit" });
|
|
1598
|
-
return;
|
|
1599
|
-
}
|
|
1600
|
-
if (s.type === "create") {
|
|
1601
|
-
finish({
|
|
1602
|
-
type: "create",
|
|
1603
|
-
projectSlug: activeSlug,
|
|
1604
|
-
categoryPath: s.categoryPath,
|
|
1605
|
-
slug: s.slug
|
|
1606
|
-
});
|
|
1607
|
-
return;
|
|
1608
|
-
}
|
|
1609
|
-
finish({ type: "pick", projectSlug: activeSlug, sessionId: s.sessionId });
|
|
1610
|
-
};
|
|
1611
|
-
if (activeSlug === null) {
|
|
1612
|
-
return /* @__PURE__ */ jsxDEV8(ProjectPicker, {
|
|
1613
|
-
onSelect: handleProjectSelect
|
|
1614
|
-
}, undefined, false, undefined, this);
|
|
1615
|
-
}
|
|
1616
|
-
return /* @__PURE__ */ jsxDEV8(Launch, {
|
|
1617
|
-
onSelect: handleLaunchSelect
|
|
1618
|
-
}, undefined, false, undefined, this);
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
1301
|
// src/tui/screens/Exit.tsx
|
|
1622
|
-
import { useState as
|
|
1623
|
-
import { Box as
|
|
1302
|
+
import { useState as useState4 } from "react";
|
|
1303
|
+
import { Box as Box6, Text as Text7, useInput as useInput3, useTui as useTui2 } from "@orchetron/storm";
|
|
1624
1304
|
|
|
1625
1305
|
// src/db/queries/conversations.ts
|
|
1626
1306
|
import { eq as eq2, and as and2, desc, sql as sql3 } from "drizzle-orm";
|
|
@@ -1629,7 +1309,7 @@ function getConversationsBySession(sessionId) {
|
|
|
1629
1309
|
}
|
|
1630
1310
|
|
|
1631
1311
|
// src/tui/screens/Exit.tsx
|
|
1632
|
-
import { jsxDEV as
|
|
1312
|
+
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
1633
1313
|
var OPTIONS = [
|
|
1634
1314
|
{ action: "save", label: "Save", hint: "Keep session paused for later" },
|
|
1635
1315
|
{
|
|
@@ -1645,12 +1325,12 @@ var OPTIONS = [
|
|
|
1645
1325
|
{
|
|
1646
1326
|
action: "resume",
|
|
1647
1327
|
label: "Resume",
|
|
1648
|
-
hint: "
|
|
1328
|
+
hint: "Continue a conversation or start a new one"
|
|
1649
1329
|
}
|
|
1650
1330
|
];
|
|
1651
1331
|
function Exit({ sessionId, onAction }) {
|
|
1652
1332
|
const { exit } = useTui2();
|
|
1653
|
-
const [cursor, setCursor] =
|
|
1333
|
+
const [cursor, setCursor] = useState4(0);
|
|
1654
1334
|
const session = getSession(sessionId);
|
|
1655
1335
|
const conversations2 = session ? getConversationsBySession(session.id) : [];
|
|
1656
1336
|
useInput3((e) => {
|
|
@@ -1670,37 +1350,37 @@ function Exit({ sessionId, onAction }) {
|
|
|
1670
1350
|
}
|
|
1671
1351
|
});
|
|
1672
1352
|
if (!session) {
|
|
1673
|
-
return /* @__PURE__ */
|
|
1353
|
+
return /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1674
1354
|
color: "red",
|
|
1675
1355
|
children: "Session not found"
|
|
1676
1356
|
}, undefined, false, undefined, this);
|
|
1677
1357
|
}
|
|
1678
1358
|
const duration = session.endedAt && session.startedAt ? formatDuration(new Date(session.endedAt).getTime() - new Date(session.startedAt).getTime()) : null;
|
|
1679
|
-
return /* @__PURE__ */
|
|
1359
|
+
return /* @__PURE__ */ jsxDEV7(Box6, {
|
|
1680
1360
|
flexDirection: "column",
|
|
1681
1361
|
padding: 1,
|
|
1682
1362
|
gap: 1,
|
|
1683
1363
|
children: [
|
|
1684
|
-
/* @__PURE__ */
|
|
1364
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1685
1365
|
bold: true,
|
|
1686
1366
|
color: "#82AAFF",
|
|
1687
1367
|
children: "Session ended"
|
|
1688
1368
|
}, undefined, false, undefined, this),
|
|
1689
|
-
/* @__PURE__ */
|
|
1369
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1690
1370
|
flexDirection: "column",
|
|
1691
1371
|
children: [
|
|
1692
|
-
/* @__PURE__ */
|
|
1372
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1693
1373
|
flexDirection: "row",
|
|
1694
1374
|
gap: 1,
|
|
1695
1375
|
children: [
|
|
1696
|
-
/* @__PURE__ */
|
|
1376
|
+
/* @__PURE__ */ jsxDEV7(StatusDot, {
|
|
1697
1377
|
status: session.status
|
|
1698
1378
|
}, undefined, false, undefined, this),
|
|
1699
|
-
/* @__PURE__ */
|
|
1379
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1700
1380
|
bold: true,
|
|
1701
1381
|
children: session.name
|
|
1702
1382
|
}, undefined, false, undefined, this),
|
|
1703
|
-
duration && /* @__PURE__ */
|
|
1383
|
+
duration && /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1704
1384
|
dim: true,
|
|
1705
1385
|
children: [
|
|
1706
1386
|
"(",
|
|
@@ -1710,7 +1390,7 @@ function Exit({ sessionId, onAction }) {
|
|
|
1710
1390
|
}, undefined, true, undefined, this)
|
|
1711
1391
|
]
|
|
1712
1392
|
}, undefined, true, undefined, this),
|
|
1713
|
-
/* @__PURE__ */
|
|
1393
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1714
1394
|
dim: true,
|
|
1715
1395
|
children: [
|
|
1716
1396
|
conversations2.length,
|
|
@@ -1720,28 +1400,28 @@ function Exit({ sessionId, onAction }) {
|
|
|
1720
1400
|
}, undefined, true, undefined, this)
|
|
1721
1401
|
]
|
|
1722
1402
|
}, undefined, true, undefined, this),
|
|
1723
|
-
/* @__PURE__ */
|
|
1403
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1724
1404
|
flexDirection: "column",
|
|
1725
|
-
children: OPTIONS.map((opt, i) => /* @__PURE__ */
|
|
1405
|
+
children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsxDEV7(Box6, {
|
|
1726
1406
|
flexDirection: "row",
|
|
1727
1407
|
gap: 1,
|
|
1728
1408
|
children: [
|
|
1729
|
-
/* @__PURE__ */
|
|
1409
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1730
1410
|
children: i === cursor ? "\u276F" : " "
|
|
1731
1411
|
}, undefined, false, undefined, this),
|
|
1732
|
-
/* @__PURE__ */
|
|
1412
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1733
1413
|
bold: i === cursor,
|
|
1734
1414
|
children: opt.label
|
|
1735
1415
|
}, undefined, false, undefined, this),
|
|
1736
|
-
/* @__PURE__ */
|
|
1416
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1737
1417
|
dim: true,
|
|
1738
1418
|
children: opt.hint
|
|
1739
1419
|
}, undefined, false, undefined, this)
|
|
1740
1420
|
]
|
|
1741
1421
|
}, opt.action, true, undefined, this))
|
|
1742
1422
|
}, undefined, false, undefined, this),
|
|
1743
|
-
/* @__PURE__ */
|
|
1744
|
-
children: /* @__PURE__ */
|
|
1423
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1424
|
+
children: /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1745
1425
|
dim: true,
|
|
1746
1426
|
children: "\u2191\u2193 navigate \xB7 enter select \xB7 q save & quit"
|
|
1747
1427
|
}, undefined, false, undefined, this)
|
|
@@ -1751,12 +1431,12 @@ function Exit({ sessionId, onAction }) {
|
|
|
1751
1431
|
}
|
|
1752
1432
|
|
|
1753
1433
|
// src/tui/screens/Resume.tsx
|
|
1754
|
-
import { useState as
|
|
1755
|
-
import { Box as
|
|
1756
|
-
import { jsxDEV as
|
|
1434
|
+
import { useState as useState5 } from "react";
|
|
1435
|
+
import { Box as Box7, Text as Text8, useInput as useInput4, useTui as useTui3 } from "@orchetron/storm";
|
|
1436
|
+
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
1757
1437
|
function Resume({ sessionId, onSelect }) {
|
|
1758
1438
|
const { exit } = useTui3();
|
|
1759
|
-
const [cursor, setCursor] =
|
|
1439
|
+
const [cursor, setCursor] = useState5(0);
|
|
1760
1440
|
const session = getSession(sessionId);
|
|
1761
1441
|
const conversations2 = getConversationsBySession(sessionId);
|
|
1762
1442
|
const totalOptions = conversations2.length + 1;
|
|
@@ -1783,17 +1463,17 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1783
1463
|
}
|
|
1784
1464
|
});
|
|
1785
1465
|
if (!session) {
|
|
1786
|
-
return /* @__PURE__ */
|
|
1466
|
+
return /* @__PURE__ */ jsxDEV8(Text8, {
|
|
1787
1467
|
color: "red",
|
|
1788
1468
|
children: "Session not found"
|
|
1789
1469
|
}, undefined, false, undefined, this);
|
|
1790
1470
|
}
|
|
1791
|
-
return /* @__PURE__ */
|
|
1471
|
+
return /* @__PURE__ */ jsxDEV8(Box7, {
|
|
1792
1472
|
flexDirection: "column",
|
|
1793
1473
|
padding: 1,
|
|
1794
1474
|
gap: 1,
|
|
1795
1475
|
children: [
|
|
1796
|
-
/* @__PURE__ */
|
|
1476
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1797
1477
|
bold: true,
|
|
1798
1478
|
color: "#82AAFF",
|
|
1799
1479
|
children: [
|
|
@@ -1801,17 +1481,17 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1801
1481
|
session.name
|
|
1802
1482
|
]
|
|
1803
1483
|
}, undefined, true, undefined, this),
|
|
1804
|
-
/* @__PURE__ */
|
|
1484
|
+
/* @__PURE__ */ jsxDEV8(Box7, {
|
|
1805
1485
|
flexDirection: "column",
|
|
1806
1486
|
children: [
|
|
1807
|
-
/* @__PURE__ */
|
|
1487
|
+
/* @__PURE__ */ jsxDEV8(Box7, {
|
|
1808
1488
|
flexDirection: "row",
|
|
1809
1489
|
gap: 1,
|
|
1810
1490
|
children: [
|
|
1811
|
-
/* @__PURE__ */
|
|
1491
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1812
1492
|
children: cursor === 0 ? "\u276F" : " "
|
|
1813
1493
|
}, undefined, false, undefined, this),
|
|
1814
|
-
/* @__PURE__ */
|
|
1494
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1815
1495
|
bold: cursor === 0,
|
|
1816
1496
|
color: "#34D399",
|
|
1817
1497
|
children: "+ New conversation"
|
|
@@ -1823,42 +1503,42 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1823
1503
|
const isSelected = cursor === idx;
|
|
1824
1504
|
const duration = conv.endedAt ? formatDuration(new Date(conv.endedAt).getTime() - new Date(conv.startedAt).getTime()) : "active";
|
|
1825
1505
|
const ago = formatAgo(conv.startedAt);
|
|
1826
|
-
return /* @__PURE__ */
|
|
1506
|
+
return /* @__PURE__ */ jsxDEV8(Box7, {
|
|
1827
1507
|
flexDirection: "row",
|
|
1828
1508
|
gap: 1,
|
|
1829
1509
|
children: [
|
|
1830
|
-
/* @__PURE__ */
|
|
1510
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1831
1511
|
children: isSelected ? "\u276F" : " "
|
|
1832
1512
|
}, undefined, false, undefined, this),
|
|
1833
|
-
/* @__PURE__ */
|
|
1513
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1834
1514
|
bold: isSelected,
|
|
1835
1515
|
dim: conv.discarded,
|
|
1836
1516
|
children: conv.id.slice(0, 8)
|
|
1837
1517
|
}, undefined, false, undefined, this),
|
|
1838
|
-
/* @__PURE__ */
|
|
1518
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1839
1519
|
dim: true,
|
|
1840
1520
|
children: ago
|
|
1841
1521
|
}, undefined, false, undefined, this),
|
|
1842
|
-
/* @__PURE__ */
|
|
1522
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1843
1523
|
dim: true,
|
|
1844
1524
|
children: "\xB7"
|
|
1845
1525
|
}, undefined, false, undefined, this),
|
|
1846
|
-
/* @__PURE__ */
|
|
1526
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1847
1527
|
dim: true,
|
|
1848
1528
|
children: [
|
|
1849
1529
|
conv.eventCount,
|
|
1850
1530
|
" events"
|
|
1851
1531
|
]
|
|
1852
1532
|
}, undefined, true, undefined, this),
|
|
1853
|
-
/* @__PURE__ */
|
|
1533
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1854
1534
|
dim: true,
|
|
1855
1535
|
children: "\xB7"
|
|
1856
1536
|
}, undefined, false, undefined, this),
|
|
1857
|
-
/* @__PURE__ */
|
|
1537
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1858
1538
|
dim: true,
|
|
1859
1539
|
children: duration
|
|
1860
1540
|
}, undefined, false, undefined, this),
|
|
1861
|
-
conv.discarded && /* @__PURE__ */
|
|
1541
|
+
conv.discarded && /* @__PURE__ */ jsxDEV8(Text8, {
|
|
1862
1542
|
color: "red",
|
|
1863
1543
|
dim: true,
|
|
1864
1544
|
children: "(discarded)"
|
|
@@ -1868,8 +1548,8 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1868
1548
|
})
|
|
1869
1549
|
]
|
|
1870
1550
|
}, undefined, true, undefined, this),
|
|
1871
|
-
/* @__PURE__ */
|
|
1872
|
-
children: /* @__PURE__ */
|
|
1551
|
+
/* @__PURE__ */ jsxDEV8(Box7, {
|
|
1552
|
+
children: /* @__PURE__ */ jsxDEV8(Text8, {
|
|
1873
1553
|
dim: true,
|
|
1874
1554
|
children: "\u2191\u2193 navigate \xB7 enter select \xB7 q back"
|
|
1875
1555
|
}, undefined, false, undefined, this)
|
|
@@ -1878,70 +1558,319 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1878
1558
|
}, undefined, true, undefined, this);
|
|
1879
1559
|
}
|
|
1880
1560
|
|
|
1561
|
+
// src/tui/screens/project-picker/index.tsx
|
|
1562
|
+
import { useMemo as useMemo3 } from "react";
|
|
1563
|
+
import { Box as Box8, Text as Text9, useTui as useTui4 } from "@orchetron/storm";
|
|
1564
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1565
|
+
import { jsxDEV as jsxDEV9, Fragment as Fragment2 } from "react/jsx-dev-runtime";
|
|
1566
|
+
var SLUG_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
1567
|
+
function statsFor(slug) {
|
|
1568
|
+
const dbFile = projectPaths(slug).db;
|
|
1569
|
+
if (!existsSync2(dbFile))
|
|
1570
|
+
return { total: 0, active: 0, unreadable: false };
|
|
1571
|
+
try {
|
|
1572
|
+
const db = getDbForProject(slug);
|
|
1573
|
+
const all = db.select({ status: sessions.status }).from(sessions).all();
|
|
1574
|
+
return {
|
|
1575
|
+
total: all.length,
|
|
1576
|
+
active: all.filter((s) => s.status === "active" || s.status === "waiting").length,
|
|
1577
|
+
unreadable: false
|
|
1578
|
+
};
|
|
1579
|
+
} catch {
|
|
1580
|
+
return { total: 0, active: 0, unreadable: true };
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
function projectRow(entry, isActive) {
|
|
1584
|
+
const stats = statsFor(entry.slug);
|
|
1585
|
+
const sessionsLabel = stats.unreadable ? "?" : `${stats.active}/${stats.total}`;
|
|
1586
|
+
const lastUsed = formatAgo(entry.lastUsedAt);
|
|
1587
|
+
return {
|
|
1588
|
+
value: entry.slug,
|
|
1589
|
+
label: `${entry.slug} ${entry.name} ${sessionsLabel}`,
|
|
1590
|
+
meta: lastUsed,
|
|
1591
|
+
display: (isCursor) => {
|
|
1592
|
+
const cursorColor = "green";
|
|
1593
|
+
const slugColor = isCursor ? cursorColor : undefined;
|
|
1594
|
+
const nameColor = isCursor ? cursorColor : undefined;
|
|
1595
|
+
return /* @__PURE__ */ jsxDEV9(Fragment2, {
|
|
1596
|
+
children: [
|
|
1597
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1598
|
+
color: cursorColor,
|
|
1599
|
+
bold: true,
|
|
1600
|
+
children: isCursor ? "\u276F " : " "
|
|
1601
|
+
}, undefined, false, undefined, this),
|
|
1602
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1603
|
+
bold: isActive,
|
|
1604
|
+
color: isActive ? "gold" : slugColor,
|
|
1605
|
+
children: isActive ? "\u25CF " : " "
|
|
1606
|
+
}, undefined, false, undefined, this),
|
|
1607
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1608
|
+
color: slugColor,
|
|
1609
|
+
bold: isCursor || isActive,
|
|
1610
|
+
children: entry.slug.padEnd(16)
|
|
1611
|
+
}, undefined, false, undefined, this),
|
|
1612
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1613
|
+
color: nameColor,
|
|
1614
|
+
dim: !isCursor,
|
|
1615
|
+
children: entry.name.padEnd(20)
|
|
1616
|
+
}, undefined, false, undefined, this),
|
|
1617
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1618
|
+
dim: true,
|
|
1619
|
+
children: stats.unreadable ? " (unreadable)" : ` ${sessionsLabel} active/total`
|
|
1620
|
+
}, undefined, false, undefined, this)
|
|
1621
|
+
]
|
|
1622
|
+
}, undefined, true, undefined, this);
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
function ProjectPicker({ onSelect }) {
|
|
1627
|
+
const { exit } = useTui4();
|
|
1628
|
+
const allProjects = useMemo3(() => listProjects(), []);
|
|
1629
|
+
const activeSlug = useMemo3(() => getActiveProjectSlug(), []);
|
|
1630
|
+
const items = useMemo3(() => {
|
|
1631
|
+
return allProjects.slice().sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt)).map((p) => projectRow(p, p.slug === activeSlug));
|
|
1632
|
+
}, [allProjects, activeSlug]);
|
|
1633
|
+
const suggestions = useMemo3(() => allProjects.map((p) => p.slug), [allProjects]);
|
|
1634
|
+
const knownSlugs = useMemo3(() => new Set(allProjects.map((p) => p.slug)), [allProjects]);
|
|
1635
|
+
const select = (selection) => {
|
|
1636
|
+
onSelect(selection);
|
|
1637
|
+
exit();
|
|
1638
|
+
};
|
|
1639
|
+
const handleSubmit = (value) => {
|
|
1640
|
+
const trimmed = value.trim();
|
|
1641
|
+
if (!trimmed)
|
|
1642
|
+
return;
|
|
1643
|
+
if (knownSlugs.has(trimmed)) {
|
|
1644
|
+
select({ type: "select", slug: trimmed });
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
if (!SLUG_PATTERN.test(trimmed)) {
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
select({ type: "create", slug: trimmed });
|
|
1651
|
+
};
|
|
1652
|
+
return /* @__PURE__ */ jsxDEV9(Box8, {
|
|
1653
|
+
flexDirection: "column",
|
|
1654
|
+
paddingY: 1,
|
|
1655
|
+
gap: 1,
|
|
1656
|
+
children: [
|
|
1657
|
+
/* @__PURE__ */ jsxDEV9(Box8, {
|
|
1658
|
+
marginX: 1,
|
|
1659
|
+
children: /* @__PURE__ */ jsxDEV9(Logo, {}, undefined, false, undefined, this)
|
|
1660
|
+
}, undefined, false, undefined, this),
|
|
1661
|
+
/* @__PURE__ */ jsxDEV9(Box8, {
|
|
1662
|
+
flexDirection: "column",
|
|
1663
|
+
marginX: 2,
|
|
1664
|
+
gap: 1,
|
|
1665
|
+
children: [
|
|
1666
|
+
/* @__PURE__ */ jsxDEV9(AppDetails, {}, undefined, false, undefined, this),
|
|
1667
|
+
/* @__PURE__ */ jsxDEV9(Box8, {
|
|
1668
|
+
flexDirection: "column",
|
|
1669
|
+
gap: 1,
|
|
1670
|
+
children: [
|
|
1671
|
+
/* @__PURE__ */ jsxDEV9(Box8, {
|
|
1672
|
+
flexDirection: "row",
|
|
1673
|
+
gap: 1,
|
|
1674
|
+
children: [
|
|
1675
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1676
|
+
bold: true,
|
|
1677
|
+
children: "Projects"
|
|
1678
|
+
}, undefined, false, undefined, this),
|
|
1679
|
+
allProjects.length > 0 ? /* @__PURE__ */ jsxDEV9(Fragment2, {
|
|
1680
|
+
children: [
|
|
1681
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1682
|
+
dim: true,
|
|
1683
|
+
children: "\xB7"
|
|
1684
|
+
}, undefined, false, undefined, this),
|
|
1685
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1686
|
+
dim: true,
|
|
1687
|
+
children: [
|
|
1688
|
+
allProjects.length,
|
|
1689
|
+
" total",
|
|
1690
|
+
activeSlug ? ` \xB7 active: ${activeSlug}` : ""
|
|
1691
|
+
]
|
|
1692
|
+
}, undefined, true, undefined, this)
|
|
1693
|
+
]
|
|
1694
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV9(Text9, {
|
|
1695
|
+
dim: true,
|
|
1696
|
+
children: "\xB7 none \u2014 type a slug to create your first project"
|
|
1697
|
+
}, undefined, false, undefined, this)
|
|
1698
|
+
]
|
|
1699
|
+
}, undefined, true, undefined, this),
|
|
1700
|
+
/* @__PURE__ */ jsxDEV9(Picker, {
|
|
1701
|
+
mode: "single",
|
|
1702
|
+
items,
|
|
1703
|
+
isFocused: true,
|
|
1704
|
+
maxVisible: 16,
|
|
1705
|
+
suggest: suggestions,
|
|
1706
|
+
placeholder: "Filter or type a new slug to create\u2026",
|
|
1707
|
+
emptyHint: "No projects. Type a slug to create one.",
|
|
1708
|
+
onSubmit: handleSubmit,
|
|
1709
|
+
onKey: (e) => {
|
|
1710
|
+
if (e.key === "c" && e.ctrl) {
|
|
1711
|
+
select({ type: "quit" });
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}, undefined, false, undefined, this),
|
|
1715
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1716
|
+
dim: true,
|
|
1717
|
+
children: "\u2191\u2193 navigate \xB7 enter select \xB7 type to filter or create new \xB7 ctrl+c quit"
|
|
1718
|
+
}, undefined, false, undefined, this)
|
|
1719
|
+
]
|
|
1720
|
+
}, undefined, true, undefined, this)
|
|
1721
|
+
]
|
|
1722
|
+
}, undefined, true, undefined, this)
|
|
1723
|
+
]
|
|
1724
|
+
}, undefined, true, undefined, this);
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1881
1727
|
// src/tui/run-screen.tsx
|
|
1882
|
-
import { jsxDEV as
|
|
1728
|
+
import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
|
|
1883
1729
|
var [, , screen, outputPath, ...args] = process.argv;
|
|
1884
1730
|
if (!screen || !outputPath) {
|
|
1885
1731
|
console.error("Usage: run-screen <screen> <outputPath> [args...]");
|
|
1886
1732
|
process.exit(1);
|
|
1887
1733
|
}
|
|
1734
|
+
var RESULT_PATH = outputPath;
|
|
1735
|
+
var DEBUG_PATH = process.env.BERTRAND_DEBUG_TUI || null;
|
|
1736
|
+
var START_HR = process.hrtime.bigint();
|
|
1737
|
+
function phase(name, detail) {
|
|
1738
|
+
if (!DEBUG_PATH)
|
|
1739
|
+
return;
|
|
1740
|
+
const elapsedMs = Number(process.hrtime.bigint() - START_HR) / 1e6;
|
|
1741
|
+
const line = `${elapsedMs.toFixed(2).padStart(8)}ms ${name}${detail ? " " + detail : ""}
|
|
1742
|
+
`;
|
|
1743
|
+
try {
|
|
1744
|
+
appendFileSync(DEBUG_PATH, line);
|
|
1745
|
+
} catch {}
|
|
1746
|
+
}
|
|
1747
|
+
phase("spawn", `screen=${screen} pid=${process.pid} isTTY=${process.stdout.isTTY ?? "?"} term=${process.env.TERM_PROGRAM || process.env.TERM || "?"}`);
|
|
1888
1748
|
var result;
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1749
|
+
var resultWritten = false;
|
|
1750
|
+
function writeResult() {
|
|
1751
|
+
if (resultWritten)
|
|
1752
|
+
return;
|
|
1753
|
+
resultWritten = true;
|
|
1754
|
+
try {
|
|
1755
|
+
writeFileSync2(RESULT_PATH, JSON.stringify(result));
|
|
1756
|
+
} catch {}
|
|
1757
|
+
}
|
|
1758
|
+
process.on("SIGINT", writeResult);
|
|
1759
|
+
process.on("SIGTERM", writeResult);
|
|
1760
|
+
process.on("SIGHUP", writeResult);
|
|
1761
|
+
process.on("uncaughtException", writeResult);
|
|
1762
|
+
process.on("unhandledRejection", writeResult);
|
|
1763
|
+
var renderCount = 0;
|
|
1764
|
+
var onRender = (m) => {
|
|
1765
|
+
renderCount++;
|
|
1766
|
+
phase(`paint #${renderCount}`, `cells=${m.cellsChanged} took=${m.renderTime.toFixed(2)}ms`);
|
|
1767
|
+
};
|
|
1768
|
+
try {
|
|
1769
|
+
switch (screen) {
|
|
1770
|
+
case "launch": {
|
|
1771
|
+
let selection = { type: "quit" };
|
|
1772
|
+
result = selection;
|
|
1773
|
+
phase("render() pre");
|
|
1774
|
+
const app = render(/* @__PURE__ */ jsxDEV10(Launch, {
|
|
1775
|
+
onSelect: (s) => {
|
|
1776
|
+
selection = s;
|
|
1777
|
+
result = selection;
|
|
1778
|
+
}
|
|
1779
|
+
}, undefined, false, undefined, this), { alternateScreen: true, patchConsole: true, onRender });
|
|
1780
|
+
phase("render() post");
|
|
1781
|
+
app.screen.invalidate();
|
|
1782
|
+
app.requestRepaint();
|
|
1783
|
+
phase("post-mount invalidate+requestRepaint");
|
|
1784
|
+
await app.waitUntilExit();
|
|
1785
|
+
phase("waitUntilExit returned");
|
|
1786
|
+
app.unmount();
|
|
1787
|
+
phase("unmount done");
|
|
1788
|
+
result = selection;
|
|
1789
|
+
break;
|
|
1911
1790
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1791
|
+
case "project-picker": {
|
|
1792
|
+
let selection = { type: "quit" };
|
|
1793
|
+
result = selection;
|
|
1794
|
+
phase("render() pre");
|
|
1795
|
+
const app = render(/* @__PURE__ */ jsxDEV10(ProjectPicker, {
|
|
1796
|
+
onSelect: (s) => {
|
|
1797
|
+
selection = s;
|
|
1798
|
+
result = selection;
|
|
1799
|
+
}
|
|
1800
|
+
}, undefined, false, undefined, this), { alternateScreen: true, patchConsole: true, onRender });
|
|
1801
|
+
phase("render() post");
|
|
1802
|
+
app.screen.invalidate();
|
|
1803
|
+
app.requestRepaint();
|
|
1804
|
+
phase("post-mount invalidate+requestRepaint");
|
|
1805
|
+
await app.waitUntilExit();
|
|
1806
|
+
phase("waitUntilExit returned");
|
|
1807
|
+
app.unmount();
|
|
1808
|
+
phase("unmount done");
|
|
1809
|
+
result = selection;
|
|
1810
|
+
break;
|
|
1811
|
+
}
|
|
1812
|
+
case "exit": {
|
|
1813
|
+
const sessionId = args[0];
|
|
1814
|
+
if (!sessionId) {
|
|
1815
|
+
console.error("exit requires sessionId");
|
|
1816
|
+
process.exit(1);
|
|
1917
1817
|
}
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1818
|
+
let action = "save";
|
|
1819
|
+
result = action;
|
|
1820
|
+
phase("render() pre");
|
|
1821
|
+
const app = render(/* @__PURE__ */ jsxDEV10(Exit, {
|
|
1822
|
+
sessionId,
|
|
1823
|
+
onAction: (a) => {
|
|
1824
|
+
action = a;
|
|
1825
|
+
result = action;
|
|
1826
|
+
}
|
|
1827
|
+
}, undefined, false, undefined, this), { alternateScreen: true, patchConsole: true, onRender });
|
|
1828
|
+
phase("render() post");
|
|
1829
|
+
app.screen.invalidate();
|
|
1830
|
+
app.requestRepaint();
|
|
1831
|
+
phase("post-mount invalidate+requestRepaint");
|
|
1832
|
+
await app.waitUntilExit();
|
|
1833
|
+
phase("waitUntilExit returned");
|
|
1834
|
+
app.unmount();
|
|
1835
|
+
phase("unmount done");
|
|
1836
|
+
result = action;
|
|
1837
|
+
break;
|
|
1929
1838
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
sessionId
|
|
1933
|
-
|
|
1934
|
-
|
|
1839
|
+
case "resume": {
|
|
1840
|
+
const sessionId = args[0];
|
|
1841
|
+
if (!sessionId) {
|
|
1842
|
+
console.error("resume requires sessionId");
|
|
1843
|
+
process.exit(1);
|
|
1935
1844
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1845
|
+
let selection = { type: "back" };
|
|
1846
|
+
result = selection;
|
|
1847
|
+
phase("render() pre");
|
|
1848
|
+
const app = render(/* @__PURE__ */ jsxDEV10(Resume, {
|
|
1849
|
+
sessionId,
|
|
1850
|
+
onSelect: (s) => {
|
|
1851
|
+
selection = s;
|
|
1852
|
+
result = selection;
|
|
1853
|
+
}
|
|
1854
|
+
}, undefined, false, undefined, this), { alternateScreen: true, patchConsole: true, onRender });
|
|
1855
|
+
phase("render() post");
|
|
1856
|
+
app.screen.invalidate();
|
|
1857
|
+
app.requestRepaint();
|
|
1858
|
+
phase("post-mount invalidate+requestRepaint");
|
|
1859
|
+
await app.waitUntilExit();
|
|
1860
|
+
phase("waitUntilExit returned");
|
|
1861
|
+
app.unmount();
|
|
1862
|
+
phase("unmount done");
|
|
1863
|
+
result = selection;
|
|
1864
|
+
break;
|
|
1865
|
+
}
|
|
1866
|
+
default:
|
|
1867
|
+
console.error(`Unknown screen: ${screen}`);
|
|
1868
|
+
process.exit(1);
|
|
1941
1869
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1870
|
+
writeResult();
|
|
1871
|
+
phase("result written");
|
|
1872
|
+
} finally {
|
|
1873
|
+
writeResult();
|
|
1874
|
+
phase("finally complete");
|
|
1945
1875
|
}
|
|
1946
|
-
writeFileSync2(outputPath, JSON.stringify(result));
|
|
1947
1876
|
process.exit(0);
|