@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.
Files changed (65) hide show
  1. package/dist/app-sidecar.d.ts +2 -0
  2. package/dist/app-sidecar.d.ts.map +1 -0
  3. package/dist/app-sidecar.js +1201 -0
  4. package/dist/app-sidecar.js.map +1 -0
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +5 -22
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/agent-session.d.ts +94 -0
  9. package/dist/core/agent-session.d.ts.map +1 -1
  10. package/dist/core/agent-session.js +313 -1
  11. package/dist/core/agent-session.js.map +1 -1
  12. package/dist/core/auth-providers.d.ts +18 -0
  13. package/dist/core/auth-providers.d.ts.map +1 -0
  14. package/dist/core/auth-providers.js +71 -0
  15. package/dist/core/auth-providers.js.map +1 -0
  16. package/dist/core/event-bus.d.ts +5 -0
  17. package/dist/core/event-bus.d.ts.map +1 -1
  18. package/dist/core/event-bus.js +1 -0
  19. package/dist/core/event-bus.js.map +1 -1
  20. package/dist/core/project-discovery.d.ts +41 -0
  21. package/dist/core/project-discovery.d.ts.map +1 -0
  22. package/dist/core/project-discovery.js +441 -0
  23. package/dist/core/project-discovery.js.map +1 -0
  24. package/dist/core/radio.d.ts +44 -0
  25. package/dist/core/radio.d.ts.map +1 -0
  26. package/dist/core/radio.js +250 -0
  27. package/dist/core/radio.js.map +1 -0
  28. package/dist/core/settings-manager.d.ts +1 -1
  29. package/dist/core/shell-path.d.ts +12 -0
  30. package/dist/core/shell-path.d.ts.map +1 -0
  31. package/dist/core/shell-path.js +166 -0
  32. package/dist/core/shell-path.js.map +1 -0
  33. package/dist/core/telegram-config.d.ts +21 -0
  34. package/dist/core/telegram-config.d.ts.map +1 -0
  35. package/dist/core/telegram-config.js +52 -0
  36. package/dist/core/telegram-config.js.map +1 -0
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +2 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/modes/serve-mode.d.ts +24 -0
  42. package/dist/modes/serve-mode.d.ts.map +1 -1
  43. package/dist/modes/serve-mode.js +54 -19
  44. package/dist/modes/serve-mode.js.map +1 -1
  45. package/dist/tools/tasks.d.ts +1 -1
  46. package/dist/ui/hooks/useTranscriptHistory.d.ts +2 -0
  47. package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
  48. package/dist/ui/hooks/useTranscriptHistory.js +3 -2
  49. package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
  50. package/dist/ui/render.d.ts.map +1 -1
  51. package/dist/ui/render.js +60 -8
  52. package/dist/ui/render.js.map +1 -1
  53. package/dist/ui/terminal-history-repaint.test.d.ts +2 -0
  54. package/dist/ui/terminal-history-repaint.test.d.ts.map +1 -0
  55. package/dist/ui/terminal-history-repaint.test.js +73 -0
  56. package/dist/ui/terminal-history-repaint.test.js.map +1 -0
  57. package/dist/ui/terminal-history.d.ts +1 -0
  58. package/dist/ui/terminal-history.d.ts.map +1 -1
  59. package/dist/ui/terminal-history.js +31 -2
  60. package/dist/ui/terminal-history.js.map +1 -1
  61. package/dist/utils/git.d.ts +6 -0
  62. package/dist/utils/git.d.ts.map +1 -1
  63. package/dist/utils/git.js +12 -0
  64. package/dist/utils/git.js.map +1 -1
  65. package/package.json +4 -4
@@ -61,6 +61,7 @@ export class EventBus {
61
61
  result: event.result,
62
62
  isError: event.isError,
63
63
  durationMs: event.durationMs,
64
+ details: event.details,
64
65
  });
65
66
  break;
66
67
  case "turn_end":
@@ -1 +1 @@
1
- {"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../src/core/event-bus.ts"],"names":[],"mappings":"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"}
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"}