@kenkaiiii/ggcoder 4.10.1 → 4.11.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/dist/app-sidecar.d.ts +2 -0
- package/dist/app-sidecar.d.ts.map +1 -0
- package/dist/app-sidecar.js +1201 -0
- package/dist/app-sidecar.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -22
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session.d.ts +94 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +313 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-providers.d.ts +18 -0
- package/dist/core/auth-providers.d.ts.map +1 -0
- package/dist/core/auth-providers.js +71 -0
- package/dist/core/auth-providers.js.map +1 -0
- package/dist/core/event-bus.d.ts +5 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +1 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/project-discovery.d.ts +41 -0
- package/dist/core/project-discovery.d.ts.map +1 -0
- package/dist/core/project-discovery.js +441 -0
- package/dist/core/project-discovery.js.map +1 -0
- package/dist/core/radio.d.ts +44 -0
- package/dist/core/radio.d.ts.map +1 -0
- package/dist/core/radio.js +250 -0
- package/dist/core/radio.js.map +1 -0
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/shell-path.d.ts +12 -0
- package/dist/core/shell-path.d.ts.map +1 -0
- package/dist/core/shell-path.js +166 -0
- package/dist/core/shell-path.js.map +1 -0
- package/dist/core/telegram-config.d.ts +21 -0
- package/dist/core/telegram-config.d.ts.map +1 -0
- package/dist/core/telegram-config.js +52 -0
- package/dist/core/telegram-config.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/modes/serve-mode.d.ts +24 -0
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +54 -19
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/tools/tasks.d.ts +1 -1
- package/dist/ui/hooks/useTranscriptHistory.d.ts +2 -0
- package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
- package/dist/ui/hooks/useTranscriptHistory.js +3 -2
- package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +60 -8
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/terminal-history-repaint.test.d.ts +2 -0
- package/dist/ui/terminal-history-repaint.test.d.ts.map +1 -0
- package/dist/ui/terminal-history-repaint.test.js +73 -0
- package/dist/ui/terminal-history-repaint.test.js.map +1 -0
- package/dist/ui/terminal-history.d.ts +1 -0
- package/dist/ui/terminal-history.d.ts.map +1 -1
- package/dist/ui/terminal-history.js +31 -2
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/utils/git.d.ts +6 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +12 -0
- package/dist/utils/git.js.map +1 -1
- package/package.json +4 -4
package/dist/core/event-bus.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../src/core/event-bus.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../src/core/event-bus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type ProjectSource = "ggcoder" | "claude-code" | "codex";
|
|
2
|
+
export interface DiscoveredProject {
|
|
3
|
+
name: string;
|
|
4
|
+
path: string;
|
|
5
|
+
lastActiveMs: number;
|
|
6
|
+
lastActiveDisplay: string;
|
|
7
|
+
/** Sorted, deduped list of stores this project showed up in. */
|
|
8
|
+
sources: ProjectSource[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Scan ggcoder + Claude Code + Codex session stores and return one row per
|
|
12
|
+
* project, sorted most-recent first. Duplicates (same cwd) are collapsed; the
|
|
13
|
+
* `sources` field lists every store the project appeared in so the picker can
|
|
14
|
+
* show a combined badge.
|
|
15
|
+
*/
|
|
16
|
+
export declare function discoverProjects(): Promise<DiscoveredProject[]>;
|
|
17
|
+
export interface RecentSession {
|
|
18
|
+
/** Session id. */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Absolute path to the session .jsonl (passed back to reopen it). */
|
|
21
|
+
path: string;
|
|
22
|
+
/** First user message, trimmed to a short preview (may be empty). */
|
|
23
|
+
preview: string;
|
|
24
|
+
/** Relative "3h ago" string from last activity. */
|
|
25
|
+
lastActiveDisplay: string;
|
|
26
|
+
messageCount: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* List the most recent ggcoder sessions for a project cwd, newest first, each
|
|
30
|
+
* with a short preview built from its first user message. Used by the new-window
|
|
31
|
+
* project picker to offer "resume a session" alongside "new session".
|
|
32
|
+
*
|
|
33
|
+
* Fast path: instead of fully reading every session file in the project (what
|
|
34
|
+
* SessionManager.list does to count messages), sort files by mtime and read
|
|
35
|
+
* only the newest `limit`. Each chosen file is parsed in ONE pass for its
|
|
36
|
+
* header id, message count, last activity, and first user preview. For projects
|
|
37
|
+
* with many/large sessions this is the difference between scanning everything
|
|
38
|
+
* and scanning ~5 files.
|
|
39
|
+
*/
|
|
40
|
+
export declare function listRecentSessions(cwd: string, limit?: number): Promise<RecentSession[]>;
|
|
41
|
+
//# sourceMappingURL=project-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-discovery.d.ts","sourceRoot":"","sources":["../../src/core/project-discovery.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,aAAa,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA6BrE;AAsSD,MAAM,WAAW,aAAa;IAC5B,kBAAkB;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAczF"}
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import readline from "node:readline";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { getAppPaths } from "../config.js";
|
|
7
|
+
/**
|
|
8
|
+
* Scan ggcoder + Claude Code + Codex session stores and return one row per
|
|
9
|
+
* project, sorted most-recent first. Duplicates (same cwd) are collapsed; the
|
|
10
|
+
* `sources` field lists every store the project appeared in so the picker can
|
|
11
|
+
* show a combined badge.
|
|
12
|
+
*/
|
|
13
|
+
export async function discoverProjects() {
|
|
14
|
+
const [gg, cc, cx] = await Promise.all([
|
|
15
|
+
discoverGgcoderProjects(),
|
|
16
|
+
discoverClaudeProjects(),
|
|
17
|
+
discoverCodexProjects(),
|
|
18
|
+
]);
|
|
19
|
+
const byPath = new Map();
|
|
20
|
+
for (const p of [...gg, ...cc, ...cx]) {
|
|
21
|
+
const existing = byPath.get(p.path);
|
|
22
|
+
if (!existing) {
|
|
23
|
+
byPath.set(p.path, p);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
byPath.set(p.path, {
|
|
27
|
+
name: existing.name,
|
|
28
|
+
path: existing.path,
|
|
29
|
+
lastActiveMs: Math.max(existing.lastActiveMs, p.lastActiveMs),
|
|
30
|
+
lastActiveDisplay: "", // recomputed below
|
|
31
|
+
sources: mergeSources(existing.sources, p.sources),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const merged = Array.from(byPath.values()).map((p) => ({
|
|
35
|
+
...p,
|
|
36
|
+
lastActiveDisplay: formatRelativeTime(p.lastActiveMs),
|
|
37
|
+
}));
|
|
38
|
+
merged.sort((a, b) => b.lastActiveMs - a.lastActiveMs);
|
|
39
|
+
return merged;
|
|
40
|
+
}
|
|
41
|
+
const SOURCE_ORDER = {
|
|
42
|
+
ggcoder: 0,
|
|
43
|
+
"claude-code": 1,
|
|
44
|
+
codex: 2,
|
|
45
|
+
};
|
|
46
|
+
function mergeSources(a, b) {
|
|
47
|
+
const set = new Set([...a, ...b]);
|
|
48
|
+
return Array.from(set).sort((x, y) => SOURCE_ORDER[x] - SOURCE_ORDER[y]);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Scan ~/.gg/sessions/. Each session directory's name is the encoded cwd
|
|
52
|
+
* (slashes → underscores); we decode it back and verify the directory still
|
|
53
|
+
* exists on disk.
|
|
54
|
+
*/
|
|
55
|
+
async function discoverGgcoderProjects() {
|
|
56
|
+
const sessionsDir = getAppPaths().sessionsDir;
|
|
57
|
+
let entries;
|
|
58
|
+
try {
|
|
59
|
+
entries = await fs.readdir(sessionsDir);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const results = [];
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const dir = path.join(sessionsDir, entry);
|
|
67
|
+
const mtime = await maxJsonlMtime(dir);
|
|
68
|
+
if (mtime === null)
|
|
69
|
+
continue;
|
|
70
|
+
// Normalize so paths containing traversal segments (e.g. an agent launched
|
|
71
|
+
// with cwd `.../src-tauri/../..`) collapse to their real directory and the
|
|
72
|
+
// basename isn't a stray "..". Deduped against other entries downstream.
|
|
73
|
+
const decoded = path.resolve("/" + entry.replace(/_/g, "/"));
|
|
74
|
+
if (!(await isDirectory(decoded)))
|
|
75
|
+
continue;
|
|
76
|
+
results.push({
|
|
77
|
+
name: path.basename(decoded),
|
|
78
|
+
path: decoded,
|
|
79
|
+
lastActiveMs: mtime,
|
|
80
|
+
lastActiveDisplay: formatRelativeTime(mtime),
|
|
81
|
+
sources: ["ggcoder"],
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return results;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Scan ~/.claude/projects/. Claude Code's directory encoding replaces every
|
|
88
|
+
* "/" with "-", which is genuinely ambiguous — a real dash in a path component
|
|
89
|
+
* (e.g. "gg-coder") collides with the separator. So we extract the cwd from
|
|
90
|
+
* the JSONL events themselves; Claude writes it into user/assistant records.
|
|
91
|
+
* Falls back to a best-effort dash decode only if no event carries a cwd.
|
|
92
|
+
*/
|
|
93
|
+
async function discoverClaudeProjects() {
|
|
94
|
+
const projectsDir = path.join(os.homedir(), ".claude", "projects");
|
|
95
|
+
let entries;
|
|
96
|
+
try {
|
|
97
|
+
entries = await fs.readdir(projectsDir);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const results = await Promise.all(entries.map(async (entry) => {
|
|
103
|
+
const dir = path.join(projectsDir, entry);
|
|
104
|
+
const mtime = await maxJsonlMtime(dir);
|
|
105
|
+
if (mtime === null)
|
|
106
|
+
return null;
|
|
107
|
+
const cwd = (await readFirstFromJsonlDir(dir, claudeCwdExtractor)) ?? fallbackDashDecode(entry);
|
|
108
|
+
if (!cwd)
|
|
109
|
+
return null;
|
|
110
|
+
if (!(await isDirectory(cwd)))
|
|
111
|
+
return null;
|
|
112
|
+
return {
|
|
113
|
+
name: path.basename(cwd),
|
|
114
|
+
path: cwd,
|
|
115
|
+
lastActiveMs: mtime,
|
|
116
|
+
lastActiveDisplay: formatRelativeTime(mtime),
|
|
117
|
+
sources: ["claude-code"],
|
|
118
|
+
};
|
|
119
|
+
}));
|
|
120
|
+
return results.filter((p) => p !== null);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Scan ~/.codex/sessions/. Codex stores sessions flat by date
|
|
124
|
+
* (`YYYY/MM/DD/rollout-*.jsonl`) with the cwd embedded in the first user
|
|
125
|
+
* message as `<environment_context><cwd>/abs/path</cwd>...</environment_context>`.
|
|
126
|
+
* We group sessions by extracted cwd and take max mtime per group.
|
|
127
|
+
*/
|
|
128
|
+
async function discoverCodexProjects() {
|
|
129
|
+
const sessionsDir = path.join(os.homedir(), ".codex", "sessions");
|
|
130
|
+
if (!(await isDirectory(sessionsDir)))
|
|
131
|
+
return [];
|
|
132
|
+
// Layout is YYYY/MM/DD/*.jsonl — depth 4 covers it.
|
|
133
|
+
const files = await collectJsonlFiles(sessionsDir, 4);
|
|
134
|
+
if (files.length === 0)
|
|
135
|
+
return [];
|
|
136
|
+
// Process newest first so per-cwd we always start with the latest mtime.
|
|
137
|
+
files.sort((a, b) => b.mtime - a.mtime);
|
|
138
|
+
const byCwd = new Map();
|
|
139
|
+
for (const f of files) {
|
|
140
|
+
const cwd = await readFirstFromFile(f.path, codexCwdExtractor);
|
|
141
|
+
if (!cwd)
|
|
142
|
+
continue;
|
|
143
|
+
const prev = byCwd.get(cwd);
|
|
144
|
+
if (prev === undefined || f.mtime > prev)
|
|
145
|
+
byCwd.set(cwd, f.mtime);
|
|
146
|
+
}
|
|
147
|
+
const results = [];
|
|
148
|
+
for (const [cwd, mtime] of byCwd) {
|
|
149
|
+
if (!(await isDirectory(cwd)))
|
|
150
|
+
continue;
|
|
151
|
+
results.push({
|
|
152
|
+
name: path.basename(cwd),
|
|
153
|
+
path: cwd,
|
|
154
|
+
lastActiveMs: mtime,
|
|
155
|
+
lastActiveDisplay: formatRelativeTime(mtime),
|
|
156
|
+
sources: ["codex"],
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
async function isDirectory(p) {
|
|
162
|
+
try {
|
|
163
|
+
const s = await fs.stat(p);
|
|
164
|
+
return s.isDirectory();
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function maxJsonlMtime(dir) {
|
|
171
|
+
if (!(await isDirectory(dir)))
|
|
172
|
+
return null;
|
|
173
|
+
const files = await collectJsonlFiles(dir, 2);
|
|
174
|
+
if (files.length === 0)
|
|
175
|
+
return null;
|
|
176
|
+
let max = 0;
|
|
177
|
+
for (const f of files)
|
|
178
|
+
if (f.mtime > max)
|
|
179
|
+
max = f.mtime;
|
|
180
|
+
return max > 0 ? max : null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Walk `dir` up to `maxDepth` levels deep collecting every .jsonl file. Used
|
|
184
|
+
* for both Claude Code (top-level + `<uuid>/subagents/`) and Codex
|
|
185
|
+
* (`YYYY/MM/DD/`) layouts.
|
|
186
|
+
*/
|
|
187
|
+
async function collectJsonlFiles(dir, maxDepth) {
|
|
188
|
+
const out = [];
|
|
189
|
+
await walk(dir, 0);
|
|
190
|
+
return out;
|
|
191
|
+
async function walk(current, depth) {
|
|
192
|
+
let entries;
|
|
193
|
+
try {
|
|
194
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
for (const e of entries) {
|
|
200
|
+
const full = path.join(current, e.name);
|
|
201
|
+
if (e.isFile() && e.name.endsWith(".jsonl")) {
|
|
202
|
+
try {
|
|
203
|
+
const s = await fs.stat(full);
|
|
204
|
+
out.push({ path: full, mtime: s.mtimeMs });
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// skip unreadable
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else if (e.isDirectory() && depth < maxDepth) {
|
|
211
|
+
await walk(full, depth + 1);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const claudeCwdExtractor = (line) => {
|
|
217
|
+
try {
|
|
218
|
+
const parsed = JSON.parse(line);
|
|
219
|
+
if (typeof parsed.cwd === "string" && parsed.cwd.startsWith("/"))
|
|
220
|
+
return parsed.cwd;
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// skip malformed
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
};
|
|
227
|
+
const CODEX_CWD_RE = /<cwd>([^<]+)<\/cwd>/;
|
|
228
|
+
const codexCwdExtractor = (line) => {
|
|
229
|
+
// Current format (openai/codex protocol.rs, late-2025+): RolloutLine wraps
|
|
230
|
+
// SessionMeta / TurnContext items with `{ type, payload: { cwd, ... } }`.
|
|
231
|
+
// First line is always SessionMeta, so this hits on read 1.
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(line);
|
|
234
|
+
const cwd = parsed.payload?.cwd;
|
|
235
|
+
if (typeof cwd === "string" && cwd.startsWith("/"))
|
|
236
|
+
return cwd;
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// not JSON or unexpected shape; fall through to legacy regex
|
|
240
|
+
}
|
|
241
|
+
// Legacy format (pre-late-2025): cwd embedded as <cwd>...</cwd> inside an
|
|
242
|
+
// <environment_context> user-message string.
|
|
243
|
+
const m = CODEX_CWD_RE.exec(line);
|
|
244
|
+
if (m && m[1] && m[1].startsWith("/"))
|
|
245
|
+
return m[1];
|
|
246
|
+
return null;
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* Walk all .jsonl files under `dir` newest-first, returning the first non-null
|
|
250
|
+
* extractor result. Walks two levels deep (matches Claude Code's nested
|
|
251
|
+
* layout).
|
|
252
|
+
*/
|
|
253
|
+
async function readFirstFromJsonlDir(dir, extractor) {
|
|
254
|
+
const files = await collectJsonlFiles(dir, 2);
|
|
255
|
+
if (files.length === 0)
|
|
256
|
+
return null;
|
|
257
|
+
files.sort((a, b) => b.mtime - a.mtime);
|
|
258
|
+
for (const f of files) {
|
|
259
|
+
const v = await readFirstFromFile(f.path, extractor);
|
|
260
|
+
if (v)
|
|
261
|
+
return v;
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Stream `file` line-by-line and return the first non-null extractor result.
|
|
267
|
+
* Caps lines so a giant transcript can't stall discovery.
|
|
268
|
+
*/
|
|
269
|
+
async function readFirstFromFile(file, extractor) {
|
|
270
|
+
return new Promise((resolve) => {
|
|
271
|
+
const stream = createReadStream(file, { encoding: "utf-8" });
|
|
272
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
273
|
+
let lines = 0;
|
|
274
|
+
let done = false;
|
|
275
|
+
const MAX_LINES = 200;
|
|
276
|
+
// Resolve before tearing down. rl.close() synchronously emits 'close',
|
|
277
|
+
// and if the close handler resolves first our real value gets swallowed.
|
|
278
|
+
const finish = (value) => {
|
|
279
|
+
if (done)
|
|
280
|
+
return;
|
|
281
|
+
done = true;
|
|
282
|
+
resolve(value);
|
|
283
|
+
rl.close();
|
|
284
|
+
stream.destroy();
|
|
285
|
+
};
|
|
286
|
+
rl.on("line", (line) => {
|
|
287
|
+
if (done)
|
|
288
|
+
return;
|
|
289
|
+
lines++;
|
|
290
|
+
if (lines > MAX_LINES) {
|
|
291
|
+
finish(null);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const v = extractor(line);
|
|
295
|
+
if (v)
|
|
296
|
+
finish(v);
|
|
297
|
+
});
|
|
298
|
+
rl.on("close", () => finish(null));
|
|
299
|
+
rl.on("error", () => finish(null));
|
|
300
|
+
stream.on("error", () => finish(null));
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
function fallbackDashDecode(entry) {
|
|
304
|
+
// Strip leading "-" then turn remaining "-" into "/". Lossy by design — only
|
|
305
|
+
// used when the JSONLs have no cwd events; the caller still verifies the
|
|
306
|
+
// result is an existing directory.
|
|
307
|
+
if (!entry.startsWith("-"))
|
|
308
|
+
return null;
|
|
309
|
+
return "/" + entry.slice(1).replace(/-/g, "/");
|
|
310
|
+
}
|
|
311
|
+
function formatRelativeTime(ms) {
|
|
312
|
+
if (ms === 0)
|
|
313
|
+
return "—";
|
|
314
|
+
const diff = Date.now() - ms;
|
|
315
|
+
if (diff < 60_000)
|
|
316
|
+
return "just now";
|
|
317
|
+
const min = 60_000;
|
|
318
|
+
const hour = 60 * min;
|
|
319
|
+
const day = 24 * hour;
|
|
320
|
+
const week = 7 * day;
|
|
321
|
+
const month = 30 * day;
|
|
322
|
+
if (diff < hour)
|
|
323
|
+
return `${Math.floor(diff / min)}m ago`;
|
|
324
|
+
if (diff < day)
|
|
325
|
+
return `${Math.floor(diff / hour)}h ago`;
|
|
326
|
+
if (diff < week)
|
|
327
|
+
return `${Math.floor(diff / day)}d ago`;
|
|
328
|
+
if (diff < month)
|
|
329
|
+
return `${Math.floor(diff / week)}w ago`;
|
|
330
|
+
return `${Math.floor(diff / month)}mo ago`;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* List the most recent ggcoder sessions for a project cwd, newest first, each
|
|
334
|
+
* with a short preview built from its first user message. Used by the new-window
|
|
335
|
+
* project picker to offer "resume a session" alongside "new session".
|
|
336
|
+
*
|
|
337
|
+
* Fast path: instead of fully reading every session file in the project (what
|
|
338
|
+
* SessionManager.list does to count messages), sort files by mtime and read
|
|
339
|
+
* only the newest `limit`. Each chosen file is parsed in ONE pass for its
|
|
340
|
+
* header id, message count, last activity, and first user preview. For projects
|
|
341
|
+
* with many/large sessions this is the difference between scanning everything
|
|
342
|
+
* and scanning ~5 files.
|
|
343
|
+
*/
|
|
344
|
+
export async function listRecentSessions(cwd, limit = 5) {
|
|
345
|
+
const sessionsDir = getAppPaths().sessionsDir;
|
|
346
|
+
const dir = path.join(sessionsDir, encodeCwdForSessions(cwd));
|
|
347
|
+
const files = await collectJsonlFiles(dir, 1);
|
|
348
|
+
if (files.length === 0)
|
|
349
|
+
return [];
|
|
350
|
+
files.sort((a, b) => b.mtime - a.mtime);
|
|
351
|
+
const out = [];
|
|
352
|
+
for (const f of files) {
|
|
353
|
+
if (out.length >= limit)
|
|
354
|
+
break;
|
|
355
|
+
const parsed = await readSessionSummary(f.path);
|
|
356
|
+
if (parsed && parsed.messageCount > 0)
|
|
357
|
+
out.push(parsed);
|
|
358
|
+
}
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Encode a cwd to its session directory name. Mirrors SessionManager's encoding
|
|
363
|
+
* (leading slash dropped, remaining slashes → underscores) so we resolve the
|
|
364
|
+
* same per-project folder without instantiating the manager.
|
|
365
|
+
*/
|
|
366
|
+
function encodeCwdForSessions(cwd) {
|
|
367
|
+
return cwd.replace(/[\\/]/g, "_").replace(/:/g, "").replace(/^_/, "");
|
|
368
|
+
}
|
|
369
|
+
/** Single-pass parse of one session file: header id + count + activity + preview. */
|
|
370
|
+
async function readSessionSummary(file) {
|
|
371
|
+
return new Promise((resolve) => {
|
|
372
|
+
const stream = createReadStream(file, { encoding: "utf-8" });
|
|
373
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
374
|
+
let id = "";
|
|
375
|
+
let messageCount = 0;
|
|
376
|
+
let lastActivity = "";
|
|
377
|
+
let preview = "";
|
|
378
|
+
let valid = false;
|
|
379
|
+
let done = false;
|
|
380
|
+
const finish = () => {
|
|
381
|
+
if (done)
|
|
382
|
+
return;
|
|
383
|
+
done = true;
|
|
384
|
+
resolve(valid
|
|
385
|
+
? { id, path: file, preview, lastActiveDisplay: rel(lastActivity), messageCount }
|
|
386
|
+
: null);
|
|
387
|
+
rl.close();
|
|
388
|
+
stream.destroy();
|
|
389
|
+
};
|
|
390
|
+
rl.on("line", (line) => {
|
|
391
|
+
if (done || !line)
|
|
392
|
+
return;
|
|
393
|
+
try {
|
|
394
|
+
const p = JSON.parse(line);
|
|
395
|
+
if (!valid) {
|
|
396
|
+
if (p.type !== "session")
|
|
397
|
+
return finish(); // not a session file
|
|
398
|
+
valid = true;
|
|
399
|
+
id = p.id ?? "";
|
|
400
|
+
if (p.timestamp)
|
|
401
|
+
lastActivity = p.timestamp;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (p.type === "message") {
|
|
405
|
+
messageCount++;
|
|
406
|
+
if (p.timestamp)
|
|
407
|
+
lastActivity = p.timestamp;
|
|
408
|
+
if (!preview && p.message?.role === "user") {
|
|
409
|
+
const text = extractText(p.message.content);
|
|
410
|
+
if (text)
|
|
411
|
+
preview = text.replace(/\s+/g, " ").trim().slice(0, 80);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
// skip malformed line
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
rl.on("close", finish);
|
|
420
|
+
rl.on("error", finish);
|
|
421
|
+
stream.on("error", finish);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
function rel(timestamp) {
|
|
425
|
+
return formatRelativeTime(Date.parse(timestamp) || 0);
|
|
426
|
+
}
|
|
427
|
+
function extractText(content) {
|
|
428
|
+
if (typeof content === "string")
|
|
429
|
+
return content;
|
|
430
|
+
if (Array.isArray(content)) {
|
|
431
|
+
for (const block of content) {
|
|
432
|
+
if (block && typeof block === "object" && "text" in block) {
|
|
433
|
+
const t = block.text;
|
|
434
|
+
if (typeof t === "string")
|
|
435
|
+
return t;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return "";
|
|
440
|
+
}
|
|
441
|
+
//# sourceMappingURL=project-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-discovery.js","sourceRoot":"","sources":["../../src/core/project-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internet radio — stream a free station while you work. Ported from gg-boss's
|
|
3
|
+
* radio so the gg-app windows offer the same curated, royalty-free, no-API-key
|
|
4
|
+
* streams (SomaFM started in 2000, Radio Paradise in 2006).
|
|
5
|
+
*
|
|
6
|
+
* Playback runs inside the per-window app sidecar process, so each window's
|
|
7
|
+
* radio is fully independent — opening more windows never duplicates audio.
|
|
8
|
+
*
|
|
9
|
+
* Player binary detection: mpv > ffplay > mpg123 > cvlc. macOS's built-in
|
|
10
|
+
* afplay isn't a streaming player, so users without any of those get a one-line
|
|
11
|
+
* "install mpv" hint and the request no-ops gracefully.
|
|
12
|
+
*
|
|
13
|
+
* One station at a time — switching stations or selecting "off" kills the
|
|
14
|
+
* existing player process before spawning a new one.
|
|
15
|
+
*/
|
|
16
|
+
export interface RadioStation {
|
|
17
|
+
/** Stable identifier used by the picker + state. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** Display name in the picker. */
|
|
20
|
+
name: string;
|
|
21
|
+
/** Short subtitle shown next to the name. */
|
|
22
|
+
description: string;
|
|
23
|
+
/** Direct stream URL — must be MP3/AAC/Ogg, anything mpv handles. */
|
|
24
|
+
url: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const RADIO_STATIONS: readonly RadioStation[];
|
|
27
|
+
export declare function getCurrentStation(): string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Stop whatever's currently playing. Idempotent — safe to call when nothing
|
|
30
|
+
* is playing. Sends SIGTERM (graceful), child cleans up the audio device.
|
|
31
|
+
*/
|
|
32
|
+
export declare function stopRadio(): void;
|
|
33
|
+
export interface PlayResult {
|
|
34
|
+
ok: boolean;
|
|
35
|
+
/** Friendly error to surface to the user when ok=false. */
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Spawn a streaming player for the given station. If one is already playing,
|
|
40
|
+
* it's killed first. Returns ok=false with a hint if no compatible player is
|
|
41
|
+
* installed — caller should surface the error to the user.
|
|
42
|
+
*/
|
|
43
|
+
export declare function playRadio(stationId: string): PlayResult;
|
|
44
|
+
//# sourceMappingURL=radio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radio.d.ts","sourceRoot":"","sources":["../../src/core/radio.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAyBjD,CAAC;AAqEF,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjD;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAqBhC;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA6CD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CA2DvD"}
|