@axplusb/kepler 0.0.1 → 1.0.1
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 +82 -0
- package/package.json +36 -4
- package/pulse/app/activity/page.tsx +190 -0
- package/pulse/app/api/activity/route.ts +138 -0
- package/pulse/app/api/costs/route.ts +88 -0
- package/pulse/app/api/export/route.ts +77 -0
- package/pulse/app/api/history/route.ts +11 -0
- package/pulse/app/api/import/route.ts +31 -0
- package/pulse/app/api/memory/route.ts +52 -0
- package/pulse/app/api/plans/route.ts +9 -0
- package/pulse/app/api/projects/[slug]/route.ts +96 -0
- package/pulse/app/api/projects/route.ts +121 -0
- package/pulse/app/api/sessions/[id]/replay/route.ts +20 -0
- package/pulse/app/api/sessions/[id]/route.ts +31 -0
- package/pulse/app/api/sessions/route.ts +112 -0
- package/pulse/app/api/settings/route.ts +14 -0
- package/pulse/app/api/stats/route.ts +143 -0
- package/pulse/app/api/todos/route.ts +9 -0
- package/pulse/app/api/tools/route.ts +160 -0
- package/pulse/app/costs/page.tsx +179 -0
- package/pulse/app/export/page.tsx +465 -0
- package/pulse/app/favicon.ico +0 -0
- package/pulse/app/globals.css +263 -0
- package/pulse/app/help/page.tsx +142 -0
- package/pulse/app/history/page.tsx +157 -0
- package/pulse/app/layout.tsx +46 -0
- package/pulse/app/memory/page.tsx +365 -0
- package/pulse/app/overview-client.tsx +393 -0
- package/pulse/app/page.tsx +14 -0
- package/pulse/app/plans/page.tsx +308 -0
- package/pulse/app/projects/[slug]/page.tsx +390 -0
- package/pulse/app/projects/page.tsx +110 -0
- package/pulse/app/sessions/[id]/page.tsx +243 -0
- package/pulse/app/sessions/page.tsx +39 -0
- package/pulse/app/settings/page.tsx +188 -0
- package/pulse/app/todos/page.tsx +211 -0
- package/pulse/app/tools/page.tsx +249 -0
- package/pulse/cli.js +159 -0
- package/pulse/components/activity/day-of-week-chart.tsx +35 -0
- package/pulse/components/activity/streak-card.tsx +36 -0
- package/pulse/components/costs/cache-efficiency-panel.tsx +76 -0
- package/pulse/components/costs/cost-by-project-chart.tsx +48 -0
- package/pulse/components/costs/cost-over-time-chart.tsx +95 -0
- package/pulse/components/costs/model-token-table.tsx +60 -0
- package/pulse/components/global-search.tsx +193 -0
- package/pulse/components/keyboard-nav-provider.tsx +23 -0
- package/pulse/components/layout/bottom-nav.tsx +52 -0
- package/pulse/components/layout/client-layout.tsx +31 -0
- package/pulse/components/layout/sidebar-context.tsx +50 -0
- package/pulse/components/layout/sidebar.tsx +182 -0
- package/pulse/components/layout/top-bar.tsx +121 -0
- package/pulse/components/overview/activity-heatmap.tsx +107 -0
- package/pulse/components/overview/conversation-table.tsx +148 -0
- package/pulse/components/overview/model-breakdown-donut.tsx +95 -0
- package/pulse/components/overview/peak-hours-chart.tsx +87 -0
- package/pulse/components/overview/project-activity-donut.tsx +96 -0
- package/pulse/components/overview/stat-card.tsx +102 -0
- package/pulse/components/overview/usage-over-time-chart.tsx +166 -0
- package/pulse/components/projects/project-card.tsx +175 -0
- package/pulse/components/sessions/replay/assistant-markdown.tsx +94 -0
- package/pulse/components/sessions/replay/compaction-card.tsx +25 -0
- package/pulse/components/sessions/replay/session-sidebar.tsx +231 -0
- package/pulse/components/sessions/replay/token-accumulation-chart.tsx +98 -0
- package/pulse/components/sessions/replay/tool-call-badge.tsx +127 -0
- package/pulse/components/sessions/replay/turn-cards.tsx +220 -0
- package/pulse/components/sessions/replay/user-tool-result.tsx +158 -0
- package/pulse/components/sessions/session-badges.tsx +49 -0
- package/pulse/components/sessions/session-table.tsx +299 -0
- package/pulse/components/theme-provider.tsx +44 -0
- package/pulse/components/tools/feature-adoption-table.tsx +58 -0
- package/pulse/components/tools/mcp-server-panel.tsx +45 -0
- package/pulse/components/tools/tool-ranking-chart.tsx +57 -0
- package/pulse/components/tools/version-history-table.tsx +32 -0
- package/pulse/components/ui/alert.tsx +66 -0
- package/pulse/components/ui/badge.tsx +48 -0
- package/pulse/components/ui/breadcrumb.tsx +109 -0
- package/pulse/components/ui/button.tsx +64 -0
- package/pulse/components/ui/calendar.tsx +220 -0
- package/pulse/components/ui/card.tsx +92 -0
- package/pulse/components/ui/command.tsx +158 -0
- package/pulse/components/ui/dialog.tsx +158 -0
- package/pulse/components/ui/input.tsx +21 -0
- package/pulse/components/ui/popover.tsx +89 -0
- package/pulse/components/ui/progress.tsx +31 -0
- package/pulse/components/ui/select.tsx +190 -0
- package/pulse/components/ui/separator.tsx +28 -0
- package/pulse/components/ui/sheet.tsx +143 -0
- package/pulse/components/ui/skeleton.tsx +13 -0
- package/pulse/components/ui/table.tsx +116 -0
- package/pulse/components/ui/tabs.tsx +91 -0
- package/pulse/components/ui/tooltip.tsx +57 -0
- package/pulse/components/use-global-keyboard-nav.ts +79 -0
- package/pulse/components.json +23 -0
- package/pulse/eslint.config.mjs +18 -0
- package/pulse/lib/claude-reader.ts +594 -0
- package/pulse/lib/decode.ts +129 -0
- package/pulse/lib/pricing.ts +102 -0
- package/pulse/lib/replay-parser.ts +165 -0
- package/pulse/lib/tool-categories.ts +127 -0
- package/pulse/lib/utils.ts +6 -0
- package/pulse/next-env.d.ts +6 -0
- package/pulse/next.config.ts +16 -0
- package/pulse/package.json +45 -0
- package/pulse/postcss.config.mjs +7 -0
- package/pulse/public/activity.png +0 -0
- package/pulse/public/cc-lens.png +0 -0
- package/pulse/public/command-k.png +0 -0
- package/pulse/public/costs.png +0 -0
- package/pulse/public/dashboard-dark.png +0 -0
- package/pulse/public/dashboard-white.png +0 -0
- package/pulse/public/export.png +0 -0
- package/pulse/public/file.svg +1 -0
- package/pulse/public/globe.svg +1 -0
- package/pulse/public/next.svg +1 -0
- package/pulse/public/projects.png +0 -0
- package/pulse/public/session-chat.png +0 -0
- package/pulse/public/todos.png +0 -0
- package/pulse/public/tools.png +0 -0
- package/pulse/public/vercel.svg +1 -0
- package/pulse/public/window.svg +1 -0
- package/pulse/tsconfig.json +34 -0
- package/pulse/types/claude.ts +294 -0
- package/src/agents/loader.mjs +89 -0
- package/src/agents/parser.mjs +98 -0
- package/src/agents/teams.mjs +123 -0
- package/src/auth/oauth.mjs +220 -0
- package/src/auth/tarang-auth.mjs +277 -0
- package/src/config/cli-args.mjs +173 -0
- package/src/config/env.mjs +263 -0
- package/src/config/settings.mjs +132 -0
- package/src/context/ast-parser.mjs +298 -0
- package/src/context/bm25.mjs +85 -0
- package/src/context/retriever.mjs +270 -0
- package/src/context/skeleton.mjs +134 -0
- package/src/core/agent-loop.mjs +480 -0
- package/src/core/approval.mjs +273 -0
- package/src/core/backend-url.mjs +57 -0
- package/src/core/cache.mjs +105 -0
- package/src/core/callback-client.mjs +149 -0
- package/src/core/checkpoints.mjs +142 -0
- package/src/core/context-manager.mjs +198 -0
- package/src/core/headless.mjs +168 -0
- package/src/core/hooks-manager.mjs +87 -0
- package/src/core/jsonl-writer.mjs +351 -0
- package/src/core/local-agent.mjs +429 -0
- package/src/core/local-store.mjs +325 -0
- package/src/core/mode-selector.mjs +51 -0
- package/src/core/output-filter.mjs +177 -0
- package/src/core/paths.mjs +101 -0
- package/src/core/pricing.mjs +314 -0
- package/src/core/providers.mjs +219 -0
- package/src/core/rate-limiter.mjs +119 -0
- package/src/core/safety.mjs +200 -0
- package/src/core/scheduler.mjs +173 -0
- package/src/core/session-manager.mjs +317 -0
- package/src/core/session.mjs +143 -0
- package/src/core/settings-sync.mjs +85 -0
- package/src/core/stagnation.mjs +57 -0
- package/src/core/stream-client.mjs +367 -0
- package/src/core/streaming.mjs +182 -0
- package/src/core/system-prompt.mjs +135 -0
- package/src/core/tool-executor.mjs +725 -0
- package/src/hooks/engine.mjs +162 -0
- package/src/index.mjs +370 -0
- package/src/mcp/client.mjs +253 -0
- package/src/mcp/transport-shttp.mjs +130 -0
- package/src/mcp/transport-sse.mjs +131 -0
- package/src/mcp/transport-ws.mjs +134 -0
- package/src/permissions/checker.mjs +57 -0
- package/src/permissions/command-classifier.mjs +573 -0
- package/src/permissions/injection-check.mjs +60 -0
- package/src/permissions/path-check.mjs +102 -0
- package/src/permissions/prompt.mjs +73 -0
- package/src/permissions/sandbox.mjs +112 -0
- package/src/plugins/loader.mjs +138 -0
- package/src/skills/loader.mjs +147 -0
- package/src/skills/runner.mjs +55 -0
- package/src/telemetry/index.mjs +96 -0
- package/src/terminal/agents.mjs +177 -0
- package/src/terminal/analytics.mjs +292 -0
- package/src/terminal/ansi.mjs +421 -0
- package/src/terminal/main.mjs +150 -0
- package/src/terminal/repl.mjs +1484 -0
- package/src/terminal/tool-display.mjs +58 -0
- package/src/tools/agent.mjs +137 -0
- package/src/tools/ask-user.mjs +61 -0
- package/src/tools/bash.mjs +148 -0
- package/src/tools/cron-create.mjs +120 -0
- package/src/tools/cron-delete.mjs +49 -0
- package/src/tools/cron-list.mjs +37 -0
- package/src/tools/edit.mjs +82 -0
- package/src/tools/enter-worktree.mjs +69 -0
- package/src/tools/exit-worktree.mjs +57 -0
- package/src/tools/glob.mjs +117 -0
- package/src/tools/grep.mjs +129 -0
- package/src/tools/lint.mjs +71 -0
- package/src/tools/ls.mjs +58 -0
- package/src/tools/lsp.mjs +115 -0
- package/src/tools/multi-edit.mjs +94 -0
- package/src/tools/notebook-edit.mjs +96 -0
- package/src/tools/read-mcp-resource.mjs +57 -0
- package/src/tools/read.mjs +138 -0
- package/src/tools/registry.mjs +132 -0
- package/src/tools/remote-trigger.mjs +84 -0
- package/src/tools/send-message.mjs +64 -0
- package/src/tools/skill.mjs +52 -0
- package/src/tools/test-runner.mjs +49 -0
- package/src/tools/todo-write.mjs +68 -0
- package/src/tools/tool-search.mjs +77 -0
- package/src/tools/web-fetch.mjs +65 -0
- package/src/tools/web-search.mjs +89 -0
- package/src/tools/write.mjs +55 -0
- package/src/ui/banner.mjs +237 -0
- package/src/ui/commands.mjs +499 -0
- package/src/ui/formatter.mjs +379 -0
- package/src/ui/markdown.mjs +278 -0
- package/src/ui/slash-commands.mjs +258 -0
- package/index.js +0 -1
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL Writer — writes cc-lens compatible session transcripts to ~/.kepler/.
|
|
3
|
+
*
|
|
4
|
+
* Format mirrors Claude Code's ~/.claude/ JSONL structure so that
|
|
5
|
+
* cc-lens (CLAUDE_CONFIG_DIR=~/.orca npx cc-lens) can read Orca sessions.
|
|
6
|
+
*
|
|
7
|
+
* Design:
|
|
8
|
+
* - Non-blocking: buffered writes, flushed every 500ms or on turn end
|
|
9
|
+
* - Accumulates content + tool_use blocks during a turn
|
|
10
|
+
* - Writes single assistant entry on complete event
|
|
11
|
+
* - Tracks UUID chain for cc-lens replay
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as fs from 'node:fs';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import * as os from 'node:os';
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import * as childProcessModule from 'node:child_process';
|
|
19
|
+
|
|
20
|
+
const KEPLER_DIR = process.env.KEPLER_HOME || path.join(os.homedir(), '.kepler');
|
|
21
|
+
const FLUSH_INTERVAL_MS = 500;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Sanitize a cwd path into a project slug for the directory name.
|
|
25
|
+
* /Users/sree/Sites/myproject → -Users-sree-Sites-myproject
|
|
26
|
+
* Mirrors the Claude Code / kepler-cli convention.
|
|
27
|
+
*/
|
|
28
|
+
function sanitizePath(p) {
|
|
29
|
+
return p.replace(/\//g, '-').replace(/^-/, '-');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeToolResultContent(output) {
|
|
33
|
+
if (typeof output === 'string') return output;
|
|
34
|
+
if (output == null) return '';
|
|
35
|
+
try {
|
|
36
|
+
const serialized = JSON.stringify(output);
|
|
37
|
+
return typeof serialized === 'string' ? serialized : String(output);
|
|
38
|
+
} catch {
|
|
39
|
+
return String(output);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class JsonlWriter {
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} cwd — project working directory
|
|
46
|
+
* @param {string} version — CLI version string
|
|
47
|
+
*/
|
|
48
|
+
constructor(cwd, version) {
|
|
49
|
+
this.cwd = cwd;
|
|
50
|
+
this.version = version;
|
|
51
|
+
this.sessionId = null; // set by setSessionId() when backend assigns it
|
|
52
|
+
this.slug = sanitizePath(cwd);
|
|
53
|
+
this.projectDir = path.join(KEPLER_DIR, 'projects', this.slug);
|
|
54
|
+
|
|
55
|
+
// UUID chain for parent linking (cc-lens replay)
|
|
56
|
+
this.lastUuid = null;
|
|
57
|
+
|
|
58
|
+
// Write buffer
|
|
59
|
+
this._buffer = [];
|
|
60
|
+
this._flushTimer = null;
|
|
61
|
+
this._flushPromise = null;
|
|
62
|
+
this._transcriptPath = null; // set when sessionId is known
|
|
63
|
+
this._ready = false;
|
|
64
|
+
|
|
65
|
+
// Turn accumulator (reset per assistant turn)
|
|
66
|
+
this._turnContent = []; // [{type: 'text', text: '...'}, ...]
|
|
67
|
+
this._turnToolCalls = []; // [{id, name, input}, ...]
|
|
68
|
+
this._turnToolResults = []; // [{tool_use_id, content, is_error}, ...]
|
|
69
|
+
this._turnUsage = null;
|
|
70
|
+
this._turnModel = null;
|
|
71
|
+
|
|
72
|
+
// Git branch (captured once at construction)
|
|
73
|
+
this._gitBranch = this._detectGitBranch();
|
|
74
|
+
|
|
75
|
+
this._ensureDir();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Set the session ID (called when backend returns session_info).
|
|
80
|
+
* Until this is called, entries are buffered with a local fallback ID.
|
|
81
|
+
*/
|
|
82
|
+
setSessionId(id) {
|
|
83
|
+
this.sessionId = id;
|
|
84
|
+
this._transcriptPath = path.join(this.projectDir, `${id}.jsonl`);
|
|
85
|
+
this._ready = true;
|
|
86
|
+
// Flush any buffered entries now that we have a path
|
|
87
|
+
if (this._buffer.length > 0) this._flush();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Generate a local session ID (for --local mode or before backend assigns one).
|
|
92
|
+
*/
|
|
93
|
+
ensureSessionId() {
|
|
94
|
+
if (!this.sessionId) {
|
|
95
|
+
this.setSessionId(randomUUID());
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Write Methods (called from REPL) ──
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Write a user turn entry.
|
|
103
|
+
*/
|
|
104
|
+
writeUserTurn(content) {
|
|
105
|
+
this.ensureSessionId();
|
|
106
|
+
const uuid = randomUUID();
|
|
107
|
+
const entry = {
|
|
108
|
+
type: 'user',
|
|
109
|
+
uuid,
|
|
110
|
+
parentUuid: this.lastUuid,
|
|
111
|
+
timestamp: new Date().toISOString(),
|
|
112
|
+
cwd: this.cwd,
|
|
113
|
+
sessionId: this.sessionId,
|
|
114
|
+
version: this.version,
|
|
115
|
+
gitBranch: this._gitBranch,
|
|
116
|
+
message: { role: 'user', content },
|
|
117
|
+
};
|
|
118
|
+
this._appendEntry(entry);
|
|
119
|
+
this.lastUuid = uuid;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Accumulate content during streaming (call on content/content_partial events).
|
|
124
|
+
*/
|
|
125
|
+
accumulateContent(text) {
|
|
126
|
+
if (!text) return;
|
|
127
|
+
// Merge into last text block or create new one
|
|
128
|
+
const last = this._turnContent[this._turnContent.length - 1];
|
|
129
|
+
if (last && last.type === 'text') {
|
|
130
|
+
last.text += text;
|
|
131
|
+
} else {
|
|
132
|
+
this._turnContent.push({ type: 'text', text });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Accumulate a tool call (call on tool_call/tool_request events).
|
|
138
|
+
*/
|
|
139
|
+
accumulateToolCall(callId, toolName, args) {
|
|
140
|
+
this._turnToolCalls.push({
|
|
141
|
+
type: 'tool_use',
|
|
142
|
+
id: callId,
|
|
143
|
+
name: toolName,
|
|
144
|
+
input: args || {},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Record a tool result (call on tool_done/tool_result events).
|
|
150
|
+
*/
|
|
151
|
+
recordToolResult(callId, output, isError) {
|
|
152
|
+
this._turnToolResults.push({
|
|
153
|
+
tool_use_id: callId,
|
|
154
|
+
content: normalizeToolResultContent(output),
|
|
155
|
+
is_error: !!isError,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Set usage and model for the current turn (call on complete event).
|
|
161
|
+
*/
|
|
162
|
+
setTurnUsage(usage, model) {
|
|
163
|
+
this._turnUsage = usage || null;
|
|
164
|
+
this._turnModel = model || null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Finalize and write the assistant turn entry + tool result entries.
|
|
169
|
+
* Call this on the 'complete' event.
|
|
170
|
+
*/
|
|
171
|
+
flushAssistantTurn() {
|
|
172
|
+
this.ensureSessionId();
|
|
173
|
+
|
|
174
|
+
// Build content array: text blocks + tool_use blocks
|
|
175
|
+
const contentBlocks = [...this._turnContent, ...this._turnToolCalls];
|
|
176
|
+
if (contentBlocks.length === 0) {
|
|
177
|
+
this._resetTurn();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Simplify: if only one text block and no tools, use string content
|
|
182
|
+
const content = (contentBlocks.length === 1 && contentBlocks[0].type === 'text')
|
|
183
|
+
? contentBlocks[0].text
|
|
184
|
+
: contentBlocks;
|
|
185
|
+
|
|
186
|
+
const uuid = randomUUID();
|
|
187
|
+
const usage = this._turnUsage || {};
|
|
188
|
+
const entry = {
|
|
189
|
+
type: 'assistant',
|
|
190
|
+
uuid,
|
|
191
|
+
parentUuid: this.lastUuid,
|
|
192
|
+
timestamp: new Date().toISOString(),
|
|
193
|
+
cwd: this.cwd,
|
|
194
|
+
sessionId: this.sessionId,
|
|
195
|
+
version: this.version,
|
|
196
|
+
message: {
|
|
197
|
+
role: 'assistant',
|
|
198
|
+
model: this._turnModel || undefined,
|
|
199
|
+
usage: {
|
|
200
|
+
input_tokens: usage.total_input_tokens || usage.input_tokens || 0,
|
|
201
|
+
output_tokens: usage.total_output_tokens || usage.output_tokens || 0,
|
|
202
|
+
cache_read_input_tokens: usage.cache_read_input_tokens || 0,
|
|
203
|
+
cache_creation_input_tokens: usage.cache_creation_input_tokens || 0,
|
|
204
|
+
},
|
|
205
|
+
content,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
this._appendEntry(entry);
|
|
209
|
+
this.lastUuid = uuid;
|
|
210
|
+
|
|
211
|
+
// Write tool result entries (as user messages with tool_result content)
|
|
212
|
+
if (this._turnToolResults.length > 0) {
|
|
213
|
+
const toolResultUuid = randomUUID();
|
|
214
|
+
const toolResultEntry = {
|
|
215
|
+
type: 'user',
|
|
216
|
+
uuid: toolResultUuid,
|
|
217
|
+
parentUuid: uuid,
|
|
218
|
+
timestamp: new Date().toISOString(),
|
|
219
|
+
cwd: this.cwd,
|
|
220
|
+
sessionId: this.sessionId,
|
|
221
|
+
version: this.version,
|
|
222
|
+
message: {
|
|
223
|
+
role: 'user',
|
|
224
|
+
content: this._turnToolResults.map(r => ({
|
|
225
|
+
type: 'tool_result',
|
|
226
|
+
tool_use_id: r.tool_use_id,
|
|
227
|
+
content: r.content.slice(0, 5000), // truncate large outputs
|
|
228
|
+
is_error: r.is_error,
|
|
229
|
+
})),
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
this._appendEntry(toolResultEntry);
|
|
233
|
+
this.lastUuid = toolResultUuid;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this._resetTurn();
|
|
237
|
+
this._flush(); // force flush at end of turn
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Write a prompt entry to ~/.kepler/history.jsonl.
|
|
242
|
+
*/
|
|
243
|
+
writeHistory(prompt) {
|
|
244
|
+
const entry = {
|
|
245
|
+
display: prompt,
|
|
246
|
+
pastedContents: {},
|
|
247
|
+
timestamp: Date.now(),
|
|
248
|
+
project: this.cwd,
|
|
249
|
+
sessionId: this.sessionId,
|
|
250
|
+
};
|
|
251
|
+
const historyPath = path.join(KEPLER_DIR, 'history.jsonl');
|
|
252
|
+
fs.promises.appendFile(historyPath, JSON.stringify(entry) + '\n', { mode: 0o600 })
|
|
253
|
+
.catch(() => {}); // best effort
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Final flush on session end.
|
|
258
|
+
*/
|
|
259
|
+
async close() {
|
|
260
|
+
if (this._flushTimer) {
|
|
261
|
+
clearTimeout(this._flushTimer);
|
|
262
|
+
this._flushTimer = null;
|
|
263
|
+
}
|
|
264
|
+
await this._flush();
|
|
265
|
+
if (this._flushPromise) {
|
|
266
|
+
await this._flushPromise;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ── Internal ──
|
|
271
|
+
|
|
272
|
+
_resetTurn() {
|
|
273
|
+
this._turnContent = [];
|
|
274
|
+
this._turnToolCalls = [];
|
|
275
|
+
this._turnToolResults = [];
|
|
276
|
+
this._turnUsage = null;
|
|
277
|
+
this._turnModel = null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
_appendEntry(entry) {
|
|
281
|
+
this._buffer.push(JSON.stringify(entry));
|
|
282
|
+
if (!this._flushTimer && this._ready) {
|
|
283
|
+
this._flushTimer = setTimeout(() => this._flush(), FLUSH_INTERVAL_MS);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async _flush() {
|
|
288
|
+
if (this._flushTimer) {
|
|
289
|
+
clearTimeout(this._flushTimer);
|
|
290
|
+
this._flushTimer = null;
|
|
291
|
+
}
|
|
292
|
+
if (this._buffer.length === 0) {
|
|
293
|
+
return this._flushPromise;
|
|
294
|
+
}
|
|
295
|
+
if (!this._transcriptPath) return; // no session ID yet, keep buffered
|
|
296
|
+
|
|
297
|
+
const lines = this._buffer.join('\n') + '\n';
|
|
298
|
+
this._buffer = [];
|
|
299
|
+
|
|
300
|
+
const pending = this._flushPromise || Promise.resolve();
|
|
301
|
+
const writePromise = pending.then(async () => {
|
|
302
|
+
try {
|
|
303
|
+
await fs.promises.appendFile(this._transcriptPath, lines, { mode: 0o600 });
|
|
304
|
+
} catch {
|
|
305
|
+
// If directory was deleted, try re-creating
|
|
306
|
+
try {
|
|
307
|
+
await this._ensureDirAsync();
|
|
308
|
+
await fs.promises.appendFile(this._transcriptPath, lines, { mode: 0o600 });
|
|
309
|
+
} catch {
|
|
310
|
+
// silent — local logging is best-effort
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
this._flushPromise = writePromise;
|
|
316
|
+
try {
|
|
317
|
+
await writePromise;
|
|
318
|
+
} finally {
|
|
319
|
+
if (this._flushPromise === writePromise) {
|
|
320
|
+
this._flushPromise = null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
_ensureDir() {
|
|
326
|
+
try {
|
|
327
|
+
fs.mkdirSync(this.projectDir, { recursive: true, mode: 0o700 });
|
|
328
|
+
} catch { /* ignore */ }
|
|
329
|
+
try {
|
|
330
|
+
fs.mkdirSync(path.join(KEPLER_DIR, 'projects'), { recursive: true, mode: 0o700 });
|
|
331
|
+
} catch { /* ignore */ }
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async _ensureDirAsync() {
|
|
335
|
+
await fs.promises.mkdir(this.projectDir, { recursive: true, mode: 0o700 });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
_detectGitBranch() {
|
|
339
|
+
try {
|
|
340
|
+
const { execSync } = childProcessModule;
|
|
341
|
+
return execSync('git rev-parse --abbrev-ref HEAD', {
|
|
342
|
+
cwd: this.cwd,
|
|
343
|
+
encoding: 'utf-8',
|
|
344
|
+
timeout: 2000,
|
|
345
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
346
|
+
}).trim();
|
|
347
|
+
} catch {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|