@axplusb/kepler 0.0.1 → 1.0.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 +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 +98 -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,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry Stub — basic telemetry interface (no actual reporting).
|
|
3
|
+
*
|
|
4
|
+
* Logs events to debug output when CLAUDE_CODE_DEBUG is set.
|
|
5
|
+
* Designed to be a drop-in interface for the full telemetry system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const events = [];
|
|
9
|
+
let enabled = true;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Track a telemetry event.
|
|
13
|
+
* @param {string} event - event name
|
|
14
|
+
* @param {object} [properties] - event properties
|
|
15
|
+
*/
|
|
16
|
+
export function track(event, properties = {}) {
|
|
17
|
+
if (!enabled) return;
|
|
18
|
+
if (process.env.CLAUDE_CODE_DISABLE_TELEMETRY === '1') return;
|
|
19
|
+
|
|
20
|
+
const entry = {
|
|
21
|
+
event,
|
|
22
|
+
properties,
|
|
23
|
+
timestamp: Date.now(),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
events.push(entry);
|
|
27
|
+
|
|
28
|
+
// Keep max 1000 events in memory
|
|
29
|
+
if (events.length > 1000) {
|
|
30
|
+
events.splice(0, events.length - 1000);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (process.env.CLAUDE_CODE_DEBUG) {
|
|
34
|
+
console.error(`[telemetry] ${event}`, JSON.stringify(properties).slice(0, 200));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Track a timing event.
|
|
40
|
+
* @param {string} event - event name
|
|
41
|
+
* @param {number} durationMs - duration in milliseconds
|
|
42
|
+
* @param {object} [properties] - additional properties
|
|
43
|
+
*/
|
|
44
|
+
export function trackTiming(event, durationMs, properties = {}) {
|
|
45
|
+
track(event, { ...properties, durationMs });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Track an error.
|
|
50
|
+
* @param {string} event - error context
|
|
51
|
+
* @param {Error} error - the error
|
|
52
|
+
*/
|
|
53
|
+
export function trackError(event, error) {
|
|
54
|
+
track(`error.${event}`, {
|
|
55
|
+
message: error.message,
|
|
56
|
+
stack: error.stack?.split('\n').slice(0, 3).join('\n'),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get collected events (for debugging).
|
|
62
|
+
* @returns {Array}
|
|
63
|
+
*/
|
|
64
|
+
export function getEvents() {
|
|
65
|
+
return [...events];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Clear collected events.
|
|
70
|
+
*/
|
|
71
|
+
export function clear() {
|
|
72
|
+
events.length = 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Enable or disable telemetry.
|
|
77
|
+
* @param {boolean} value
|
|
78
|
+
*/
|
|
79
|
+
export function setEnabled(value) {
|
|
80
|
+
enabled = value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get telemetry stats.
|
|
85
|
+
*/
|
|
86
|
+
export function getStats() {
|
|
87
|
+
const counts = {};
|
|
88
|
+
for (const e of events) {
|
|
89
|
+
counts[e.event] = (counts[e.event] || 0) + 1;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
totalEvents: events.length,
|
|
93
|
+
enabled,
|
|
94
|
+
eventCounts: counts,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in Agents — specialized agent modes invoked via slash commands.
|
|
3
|
+
*
|
|
4
|
+
* Each agent wraps the same backend SSE flow but with a specialized
|
|
5
|
+
* system prompt prefix that focuses the AI on a specific task type.
|
|
6
|
+
*
|
|
7
|
+
* Agents:
|
|
8
|
+
* - explore: Code explorer — traces execution paths, maps architecture
|
|
9
|
+
* - review: Code reviewer — finds bugs, security issues, quality problems
|
|
10
|
+
* - architect: Feature architect — designs implementations, file plans
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { c, spinner, inPlace, hr } from './ansi.mjs';
|
|
14
|
+
import { TarangStreamClient } from '../core/stream-client.mjs';
|
|
15
|
+
import { resolveBackendUrl } from '../core/backend-url.mjs';
|
|
16
|
+
|
|
17
|
+
// ── Agent Definitions ────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
export const BUILTIN_AGENTS = [
|
|
20
|
+
{
|
|
21
|
+
command: 'explore',
|
|
22
|
+
name: 'Code Explorer',
|
|
23
|
+
description: 'Deeply analyze codebase features and architecture',
|
|
24
|
+
detail: 'Traces execution paths, maps layers, documents dependencies',
|
|
25
|
+
icon: '🔭',
|
|
26
|
+
systemPrompt: `You are a Code Explorer agent. Your job is to deeply analyze the codebase to answer the user's question.
|
|
27
|
+
|
|
28
|
+
Your approach:
|
|
29
|
+
1. Start by understanding what the user wants to know
|
|
30
|
+
2. Search for relevant files using search_code and list_files
|
|
31
|
+
3. Read the key files to trace the execution path
|
|
32
|
+
4. Map the architecture layers (entry points → business logic → data layer)
|
|
33
|
+
5. Document dependencies between components
|
|
34
|
+
6. Provide a clear, structured answer with file references
|
|
35
|
+
|
|
36
|
+
Rules:
|
|
37
|
+
- ONLY use read-only tools: read_file, search_code, list_files, search_files, get_file_info
|
|
38
|
+
- NEVER modify any files
|
|
39
|
+
- NEVER run shell commands that modify state
|
|
40
|
+
- Include file paths and line numbers in your references
|
|
41
|
+
- Structure your response with clear sections: Overview, Key Files, Execution Flow, Dependencies
|
|
42
|
+
- Be thorough but concise — focus on what matters for the user's question`,
|
|
43
|
+
readOnly: true,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
command: 'review',
|
|
47
|
+
name: 'Code Reviewer',
|
|
48
|
+
description: 'Review code for bugs, security issues, and quality',
|
|
49
|
+
detail: 'Scans for OWASP top 10, logic errors, code smells',
|
|
50
|
+
icon: '🔍',
|
|
51
|
+
systemPrompt: `You are a Code Review agent. Your job is to review code for issues.
|
|
52
|
+
|
|
53
|
+
Your approach:
|
|
54
|
+
1. Read the files or directories the user specifies
|
|
55
|
+
2. Check for:
|
|
56
|
+
- Security vulnerabilities (OWASP top 10: injection, XSS, auth bypass, etc.)
|
|
57
|
+
- Logic errors and edge cases
|
|
58
|
+
- Error handling gaps
|
|
59
|
+
- Performance issues (N+1 queries, memory leaks, blocking operations)
|
|
60
|
+
- Code quality (naming, complexity, duplication)
|
|
61
|
+
3. Rate each finding by severity: CRITICAL / HIGH / MEDIUM / LOW
|
|
62
|
+
4. Provide specific fix suggestions with code
|
|
63
|
+
|
|
64
|
+
Rules:
|
|
65
|
+
- ONLY use read-only tools: read_file, search_code, list_files, search_files
|
|
66
|
+
- NEVER modify any files
|
|
67
|
+
- Focus on HIGH and CRITICAL issues first
|
|
68
|
+
- Include file:line references
|
|
69
|
+
- Structure: Summary → Critical Issues → Other Findings → Recommendations
|
|
70
|
+
- Be specific — "line 42 has SQL injection via string interpolation" not "check for SQL injection"`,
|
|
71
|
+
readOnly: true,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
command: 'architect',
|
|
75
|
+
name: 'Feature Architect',
|
|
76
|
+
description: 'Design feature implementations with file plans',
|
|
77
|
+
detail: 'Analyzes patterns, designs components, maps data flows',
|
|
78
|
+
icon: '📐',
|
|
79
|
+
systemPrompt: `You are a Feature Architect agent. Your job is to design how a feature should be implemented.
|
|
80
|
+
|
|
81
|
+
Your approach:
|
|
82
|
+
1. Understand the feature requirements from the user
|
|
83
|
+
2. Analyze existing codebase patterns and conventions:
|
|
84
|
+
- File organization and naming
|
|
85
|
+
- Import patterns and module structure
|
|
86
|
+
- Error handling patterns
|
|
87
|
+
- Testing patterns
|
|
88
|
+
3. Design the implementation:
|
|
89
|
+
- List all files to create/modify
|
|
90
|
+
- Component/module design with interfaces
|
|
91
|
+
- Data flow (request → processing → response)
|
|
92
|
+
- Database schema changes if needed
|
|
93
|
+
4. Provide implementation order (what to build first)
|
|
94
|
+
|
|
95
|
+
Rules:
|
|
96
|
+
- ONLY use read-only tools: read_file, search_code, list_files, search_files
|
|
97
|
+
- NEVER modify any files — you DESIGN, you don't implement
|
|
98
|
+
- Follow existing project conventions
|
|
99
|
+
- Structure: Requirements → Architecture → File Plan → Implementation Order → Risks
|
|
100
|
+
- Include code sketches for key interfaces
|
|
101
|
+
- Call out edge cases and potential pitfalls`,
|
|
102
|
+
readOnly: true,
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
// ── Agent Runner ─────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Run a built-in agent with the given instruction.
|
|
110
|
+
* @param {string} agentName - e.g. 'explore', 'review', 'architect'
|
|
111
|
+
* @param {string} instruction - User's instruction
|
|
112
|
+
* @param {Object} ctx - { auth, toolExecutor, approval }
|
|
113
|
+
* @param {Object} session - Session state
|
|
114
|
+
* @param {Function} renderEvent - Event renderer function
|
|
115
|
+
*/
|
|
116
|
+
export async function runAgent(agentName, instruction, ctx, session, renderEvent) {
|
|
117
|
+
const agent = BUILTIN_AGENTS.find(a => a.command === agentName);
|
|
118
|
+
if (!agent) {
|
|
119
|
+
process.stderr.write(` ${c.red('Unknown agent: ' + agentName)}\n`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const creds = ctx.auth.loadCredentials();
|
|
124
|
+
if (!creds.token) {
|
|
125
|
+
process.stderr.write(` ${c.red('Not logged in. Run /login first.')}\n`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Header
|
|
130
|
+
process.stderr.write(`\n ${agent.icon} ${c.bold(c.brand(agent.name))}\n`);
|
|
131
|
+
process.stderr.write(` ${c.gray('─'.repeat(40))}\n`);
|
|
132
|
+
process.stderr.write(` ${c.gray(instruction)}\n\n`);
|
|
133
|
+
|
|
134
|
+
// Prepend agent system prompt to instruction
|
|
135
|
+
const fullInstruction = `${agent.systemPrompt}\n\n---\n\nUser request: ${instruction}`;
|
|
136
|
+
|
|
137
|
+
// For read-only agents, use a restricted approval manager
|
|
138
|
+
const { ApprovalManager } = await import('../core/approval.mjs');
|
|
139
|
+
const agentApproval = agent.readOnly
|
|
140
|
+
? new ApprovalManager({ planMode: true }) // planMode blocks all writes
|
|
141
|
+
: ctx.approval;
|
|
142
|
+
|
|
143
|
+
const client = new TarangStreamClient({
|
|
144
|
+
baseUrl: creds.backendUrl,
|
|
145
|
+
token: creds.token,
|
|
146
|
+
toolExecutor: ctx.toolExecutor,
|
|
147
|
+
approvalManager: agentApproval,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
session.turns++;
|
|
151
|
+
session.toolCalls = 0;
|
|
152
|
+
let assistantContent = '';
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
for await (const event of client.execute(fullInstruction, { cwd: process.cwd() })) {
|
|
156
|
+
renderEvent(event);
|
|
157
|
+
|
|
158
|
+
if (event.type === 'content' || event.type === 'content_partial') {
|
|
159
|
+
const text = event.data?.text || '';
|
|
160
|
+
if (text) assistantContent = text;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch (err) {
|
|
164
|
+
inPlace('');
|
|
165
|
+
process.stderr.write(` ${c.red('Agent error: ' + err.message)}\n`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Save to conversation history
|
|
169
|
+
if (assistantContent) {
|
|
170
|
+
session.history.push(
|
|
171
|
+
{ role: 'user', content: `[${agent.name}] ${instruction}` },
|
|
172
|
+
{ role: 'assistant', content: assistantContent }
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
process.stderr.write('\n');
|
|
177
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
getRecentSessions,
|
|
8
|
+
getSessionStats,
|
|
9
|
+
getToolBreakdown,
|
|
10
|
+
getModelBreakdown,
|
|
11
|
+
getHistory,
|
|
12
|
+
getSessionDetail,
|
|
13
|
+
getStorePaths,
|
|
14
|
+
} from '../core/local-store.mjs';
|
|
15
|
+
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
const { version } = require('../../package.json');
|
|
18
|
+
|
|
19
|
+
function parseNumber(value, fallback) {
|
|
20
|
+
const num = Number.parseInt(value, 10);
|
|
21
|
+
return Number.isFinite(num) && num >= 0 ? num : fallback;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readOption(args, name, fallback = null) {
|
|
25
|
+
const idx = args.indexOf(name);
|
|
26
|
+
if (idx === -1 || idx === args.length - 1) return fallback;
|
|
27
|
+
return args[idx + 1];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function hasFlag(args, name) {
|
|
31
|
+
return args.includes(name);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function basename(projectPath) {
|
|
35
|
+
return projectPath ? path.basename(projectPath) : 'unknown';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function formatNumber(num) {
|
|
39
|
+
return new Intl.NumberFormat('en-US').format(num || 0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function formatWhen(value) {
|
|
43
|
+
if (!value) return 'n/a';
|
|
44
|
+
const date = new Date(value);
|
|
45
|
+
return Number.isNaN(date.getTime()) ? 'n/a' : date.toLocaleString();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatDuration(startTime, endTime) {
|
|
49
|
+
if (!startTime || !endTime) return 'n/a';
|
|
50
|
+
const ms = new Date(endTime).getTime() - new Date(startTime).getTime();
|
|
51
|
+
if (!Number.isFinite(ms) || ms < 0) return 'n/a';
|
|
52
|
+
if (ms < 1000) return `${ms}ms`;
|
|
53
|
+
const seconds = Math.round(ms / 100) / 10;
|
|
54
|
+
if (seconds < 60) return `${seconds}s`;
|
|
55
|
+
const minutes = Math.floor(seconds / 60);
|
|
56
|
+
const rem = Math.round((seconds % 60) * 10) / 10;
|
|
57
|
+
return `${minutes}m ${rem}s`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function truncate(text, max = 80) {
|
|
61
|
+
if (!text) return '';
|
|
62
|
+
return text.length > max ? `${text.slice(0, max - 3)}...` : text;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function relativeDaysLabel(days) {
|
|
66
|
+
return days === 0 ? 'all time' : `last ${days} days`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function extractPrompt(entry) {
|
|
70
|
+
return truncate(entry?.display || '', 120);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function formatSessionsReport(sessions, limit) {
|
|
74
|
+
if (!sessions.length) {
|
|
75
|
+
return 'No local sessions found in ~/.orca/projects.\n';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const lines = [];
|
|
79
|
+
lines.push(`ORCA SESSIONS ${sessions.length} shown`);
|
|
80
|
+
lines.push(`Recent local transcripts from ~/.orca/projects (limit ${limit})`);
|
|
81
|
+
lines.push('');
|
|
82
|
+
|
|
83
|
+
for (const session of sessions) {
|
|
84
|
+
const model = session.models[0] || 'backend default';
|
|
85
|
+
const totalTokens = (session.inputTokens || 0) + (session.outputTokens || 0);
|
|
86
|
+
lines.push(`${formatWhen(session.endTime || session.startTime)} ${basename(session.project)}`);
|
|
87
|
+
lines.push(` session ${session.sessionId}`);
|
|
88
|
+
lines.push(` prompt ${truncate(session.firstPrompt || '(no prompt captured)', 96)}`);
|
|
89
|
+
lines.push(` usage ${formatNumber(totalTokens)} tok ${formatNumber(session.toolCalls.reduce((sum, tool) => sum + tool.count, 0))} tools ${session.userMessages} user / ${session.assistantMessages} assistant`);
|
|
90
|
+
lines.push(` model ${model}${session.gitBranch ? ` git:${session.gitBranch}` : ''}`);
|
|
91
|
+
lines.push('');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function formatStatsReport(stats, tools, models, days, paths) {
|
|
98
|
+
const lines = [];
|
|
99
|
+
lines.push(`ORCA STATS ${relativeDaysLabel(days)}`);
|
|
100
|
+
lines.push(`Store: ${paths.orcaDir}`);
|
|
101
|
+
lines.push('');
|
|
102
|
+
lines.push(`Sessions ${formatNumber(stats.totalSessions)}`);
|
|
103
|
+
lines.push(`Messages ${formatNumber(stats.totalUserMessages + stats.totalAssistantMessages)} (${formatNumber(stats.totalUserMessages)} user, ${formatNumber(stats.totalAssistantMessages)} assistant)`);
|
|
104
|
+
lines.push(`Tokens ${formatNumber(stats.totalInputTokens + stats.totalOutputTokens)} (${formatNumber(stats.totalInputTokens)} in, ${formatNumber(stats.totalOutputTokens)} out)`);
|
|
105
|
+
lines.push(`Cache Read ${formatNumber(stats.totalCacheReadTokens)}`);
|
|
106
|
+
lines.push(`Tool Calls ${formatNumber(stats.totalToolCalls)}`);
|
|
107
|
+
lines.push('');
|
|
108
|
+
|
|
109
|
+
lines.push('Top Tools');
|
|
110
|
+
if (tools.length === 0) {
|
|
111
|
+
lines.push(' none');
|
|
112
|
+
} else {
|
|
113
|
+
for (const tool of tools.slice(0, 8)) {
|
|
114
|
+
lines.push(` ${tool.name.padEnd(18)} ${formatNumber(tool.count)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
lines.push('');
|
|
118
|
+
|
|
119
|
+
lines.push('Models');
|
|
120
|
+
if (models.length === 0) {
|
|
121
|
+
lines.push(' none');
|
|
122
|
+
} else {
|
|
123
|
+
for (const model of models.slice(0, 8)) {
|
|
124
|
+
lines.push(` ${truncate(model.model, 42).padEnd(42)} ${formatNumber(model.sessions)} sessions`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
lines.push('');
|
|
128
|
+
|
|
129
|
+
return lines.join('\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function formatHistoryReport(entries, limit) {
|
|
133
|
+
if (!entries.length) {
|
|
134
|
+
return 'No local prompt history found in ~/.orca/history.jsonl.\n';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const lines = [];
|
|
138
|
+
lines.push(`ORCA HISTORY ${entries.length} shown`);
|
|
139
|
+
lines.push(`Recent prompts from ~/.orca/history.jsonl (limit ${limit})`);
|
|
140
|
+
lines.push('');
|
|
141
|
+
|
|
142
|
+
for (const entry of entries) {
|
|
143
|
+
lines.push(`${formatWhen(entry.timestamp)} ${basename(entry.project)}`);
|
|
144
|
+
lines.push(` ${extractPrompt(entry)}`);
|
|
145
|
+
lines.push('');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return lines.join('\n');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function json(res, status, payload) {
|
|
152
|
+
res.writeHead(status, {
|
|
153
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
154
|
+
'Cache-Control': 'no-store',
|
|
155
|
+
});
|
|
156
|
+
res.end(JSON.stringify(payload));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function escapeHtml(value) {
|
|
160
|
+
return String(value)
|
|
161
|
+
.replace(/&/g, '&')
|
|
162
|
+
.replace(/</g, '<')
|
|
163
|
+
.replace(/>/g, '>')
|
|
164
|
+
.replace(/"/g, '"');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function openBrowser(url) {
|
|
168
|
+
try {
|
|
169
|
+
let child;
|
|
170
|
+
if (process.platform === 'darwin') {
|
|
171
|
+
child = spawn('open', [url], { stdio: 'ignore', detached: true });
|
|
172
|
+
} else if (process.platform === 'win32') {
|
|
173
|
+
child = spawn('cmd', ['/c', 'start', '', url], { stdio: 'ignore', detached: true });
|
|
174
|
+
} else {
|
|
175
|
+
child = spawn('xdg-open', [url], { stdio: 'ignore', detached: true });
|
|
176
|
+
}
|
|
177
|
+
child.unref();
|
|
178
|
+
return true;
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export async function runSessionsCommand(args = []) {
|
|
185
|
+
const limit = parseNumber(readOption(args, '--limit', '20'), 20);
|
|
186
|
+
const sessions = getRecentSessions(limit);
|
|
187
|
+
process.stdout.write(formatSessionsReport(sessions, limit));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function runStatsCommand(args = []) {
|
|
191
|
+
const days = parseNumber(readOption(args, '--days', '30'), 30);
|
|
192
|
+
const stats = getSessionStats(days);
|
|
193
|
+
const tools = getToolBreakdown(days);
|
|
194
|
+
const models = getModelBreakdown(days);
|
|
195
|
+
const paths = getStorePaths();
|
|
196
|
+
process.stdout.write(formatStatsReport(stats, tools, models, days, paths));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export async function runHistoryCommand(args = []) {
|
|
200
|
+
const limit = parseNumber(readOption(args, '--limit', '50'), 50);
|
|
201
|
+
const history = getHistory(limit);
|
|
202
|
+
process.stdout.write(formatHistoryReport(history, limit));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Dashboard removed — now using Orca Pulse (pulse/cli.js)
|
|
206
|
+
|
|
207
|
+
export async function _runDashboardCommand_REMOVED() { /* see pulse/cli.js */ }
|
|
208
|
+
|
|
209
|
+
export async function _OLD_runDashboardCommand(args = []) {
|
|
210
|
+
const requestedPort = parseNumber(readOption(args, '--port', '4318'), 4318);
|
|
211
|
+
const host = readOption(args, '--host', '127.0.0.1') || '127.0.0.1';
|
|
212
|
+
const shouldOpen = !hasFlag(args, '--no-open');
|
|
213
|
+
|
|
214
|
+
const server = http.createServer(async (req, res) => {
|
|
215
|
+
try {
|
|
216
|
+
const url = new URL(req.url || '/', `http://${req.headers.host || `${host}:${requestedPort}`}`);
|
|
217
|
+
|
|
218
|
+
if (url.pathname === '/') {
|
|
219
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
220
|
+
res.end(dashboardHtml());
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (url.pathname === '/api/overview') {
|
|
225
|
+
const days = parseNumber(url.searchParams.get('days') || '30', 30);
|
|
226
|
+
const limit = parseNumber(url.searchParams.get('limit') || '18', 18);
|
|
227
|
+
const historyLimit = parseNumber(url.searchParams.get('history_limit') || '40', 40);
|
|
228
|
+
const [stats, tools, models, sessions] = await Promise.all([
|
|
229
|
+
getSessionStats(days),
|
|
230
|
+
getToolBreakdown(days),
|
|
231
|
+
getModelBreakdown(days),
|
|
232
|
+
getRecentSessions(limit),
|
|
233
|
+
]);
|
|
234
|
+
json(res, 200, {
|
|
235
|
+
stats,
|
|
236
|
+
tools,
|
|
237
|
+
models,
|
|
238
|
+
sessions,
|
|
239
|
+
history: getHistory(historyLimit),
|
|
240
|
+
paths: getStorePaths(),
|
|
241
|
+
});
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (url.pathname === '/api/session') {
|
|
246
|
+
const sessionId = url.searchParams.get('session_id') || '';
|
|
247
|
+
if (!sessionId) {
|
|
248
|
+
json(res, 400, { error: 'session_id is required' });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const detail = await getSessionDetail(sessionId);
|
|
252
|
+
if (!detail) {
|
|
253
|
+
json(res, 404, { error: 'Session not found' });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
json(res, 200, detail);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
json(res, 404, { error: 'Not found' });
|
|
261
|
+
} catch (err) {
|
|
262
|
+
json(res, 500, { error: err.message || 'Internal error' });
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await new Promise((resolve, reject) => {
|
|
267
|
+
server.once('error', reject);
|
|
268
|
+
server.listen(requestedPort, host, resolve);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const address = server.address();
|
|
272
|
+
const actualPort = typeof address === 'object' && address ? address.port : requestedPort;
|
|
273
|
+
const url = `http://${host}:${actualPort}`;
|
|
274
|
+
|
|
275
|
+
process.stderr.write(`\x1b[36mOrca dashboard\x1b[0m ${url}\n`);
|
|
276
|
+
process.stderr.write(`\x1b[2mReading local analytics from ${getStorePaths().orcaDir}\x1b[0m\n`);
|
|
277
|
+
process.stderr.write(`\x1b[2mPress Ctrl+C to stop the dashboard server.\x1b[0m\n`);
|
|
278
|
+
|
|
279
|
+
if (shouldOpen) {
|
|
280
|
+
const opened = openBrowser(url);
|
|
281
|
+
if (!opened) {
|
|
282
|
+
process.stderr.write(`\x1b[33mUnable to open a browser automatically. Open ${url} manually.\x1b[0m\n`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const shutdown = () => {
|
|
287
|
+
server.close(() => process.exit(0));
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
process.on('SIGINT', shutdown);
|
|
291
|
+
process.on('SIGTERM', shutdown);
|
|
292
|
+
}
|