panopticon-cli 0.4.30 → 0.4.31
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/{agents-ZG4JIPO2.js → agents-GQDAKTEQ.js} +4 -3
- package/dist/{chunk-SIAUVHVO.js → chunk-3XAB4IXF.js} +4 -2
- package/dist/{chunk-SIAUVHVO.js.map → chunk-3XAB4IXF.js.map} +1 -1
- package/dist/chunk-ELK6Q7QI.js +545 -0
- package/dist/chunk-ELK6Q7QI.js.map +1 -0
- package/dist/{chunk-VH27COUW.js → chunk-HNEWTIR3.js} +37 -5
- package/dist/chunk-HNEWTIR3.js.map +1 -0
- package/dist/chunk-LYSBSZYV.js +1523 -0
- package/dist/chunk-LYSBSZYV.js.map +1 -0
- package/dist/{chunk-42O6XJWZ.js → chunk-TMXN7THF.js} +14 -14
- package/dist/{chunk-VTMXR7JF.js → chunk-VU4FLXV5.js} +47 -40
- package/dist/{chunk-VTMXR7JF.js.map → chunk-VU4FLXV5.js.map} +1 -1
- package/dist/cli/index.js +46 -28
- package/dist/cli/index.js.map +1 -1
- package/dist/{config-QWTS63TU.js → config-BOAMSKTF.js} +4 -2
- package/dist/dashboard/public/assets/{index-Dhwz2I7n.js → index-izWbAt7V.js} +65 -65
- package/dist/dashboard/public/index.html +1 -1
- package/dist/dashboard/server.js +94155 -23826
- package/dist/index.js +16 -14
- package/dist/index.js.map +1 -1
- package/dist/{remote-workspace-FNXLMNBG.js → remote-workspace-2G6V2KNP.js} +7 -5
- package/dist/{remote-workspace-FNXLMNBG.js.map → remote-workspace-2G6V2KNP.js.map} +1 -1
- package/dist/{specialist-context-QVRSHPYN.js → specialist-context-6SE5VRRC.js} +2 -2
- package/dist/{specialist-logs-7TQMHCUN.js → specialist-logs-EXLOQHQ2.js} +2 -2
- package/dist/{specialists-UUXB5KFV.js → specialists-BRUHPAXE.js} +2 -2
- package/dist/{traefik-7OLLXUD7.js → traefik-CUJM6K5Z.js} +3 -3
- package/package.json +3 -2
- package/scripts/record-cost-event.js +243 -79
- package/scripts/record-cost-event.ts +128 -68
- package/dist/chunk-J3J32DIR.js +0 -279
- package/dist/chunk-J3J32DIR.js.map +0 -1
- package/dist/chunk-SUMIHS2B.js +0 -1714
- package/dist/chunk-SUMIHS2B.js.map +0 -1
- package/dist/chunk-VH27COUW.js.map +0 -1
- /package/dist/{agents-ZG4JIPO2.js.map → agents-GQDAKTEQ.js.map} +0 -0
- /package/dist/{chunk-42O6XJWZ.js.map → chunk-TMXN7THF.js.map} +0 -0
- /package/dist/{config-QWTS63TU.js.map → config-BOAMSKTF.js.map} +0 -0
- /package/dist/{specialist-context-QVRSHPYN.js.map → specialist-context-6SE5VRRC.js.map} +0 -0
- /package/dist/{specialist-logs-7TQMHCUN.js.map → specialist-logs-EXLOQHQ2.js.map} +0 -0
- /package/dist/{specialists-UUXB5KFV.js.map → specialists-BRUHPAXE.js.map} +0 -0
- /package/dist/{traefik-7OLLXUD7.js.map → traefik-CUJM6K5Z.js.map} +0 -0
|
@@ -1,113 +1,173 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Record
|
|
4
|
-
* Called by heartbeat-hook with JSON
|
|
3
|
+
* Record cost events from Claude Code transcript data
|
|
4
|
+
* Called by heartbeat-hook with PostToolUse JSON on stdin
|
|
5
|
+
*
|
|
6
|
+
* Claude Code's PostToolUse events do NOT include token usage.
|
|
7
|
+
* Instead, we read usage from the transcript JSONL file
|
|
8
|
+
* (available via the transcript_path field in the event).
|
|
9
|
+
*
|
|
10
|
+
* Uses byte-offset tracking to efficiently process only new
|
|
11
|
+
* transcript entries on each invocation.
|
|
5
12
|
*/
|
|
6
13
|
|
|
7
|
-
import { readFileSync } from 'fs';
|
|
14
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync, openSync, readSync, fstatSync, closeSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { homedir } from 'os';
|
|
8
17
|
import { calculateCost, getPricing, AIProvider } from '../src/lib/cost.js';
|
|
9
18
|
import { appendCostEvent } from '../src/lib/costs/events.js';
|
|
10
19
|
|
|
11
20
|
// ============== Types ==============
|
|
12
21
|
|
|
13
|
-
interface
|
|
22
|
+
interface PostToolUseEvent {
|
|
23
|
+
session_id?: string;
|
|
24
|
+
transcript_path?: string;
|
|
25
|
+
tool_name?: string;
|
|
26
|
+
tool_use_id?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface TranscriptUsage {
|
|
14
30
|
input_tokens?: number;
|
|
15
31
|
output_tokens?: number;
|
|
16
32
|
cache_read_input_tokens?: number;
|
|
17
33
|
cache_creation_input_tokens?: number;
|
|
18
34
|
}
|
|
19
35
|
|
|
20
|
-
interface
|
|
21
|
-
|
|
22
|
-
usage?: UsageData;
|
|
36
|
+
interface TranscriptEntry {
|
|
37
|
+
type?: string;
|
|
23
38
|
message?: {
|
|
24
39
|
model?: string;
|
|
25
|
-
usage?:
|
|
40
|
+
usage?: TranscriptUsage;
|
|
26
41
|
};
|
|
42
|
+
requestId?: string;
|
|
27
43
|
}
|
|
28
44
|
|
|
29
45
|
// ============== Main ==============
|
|
30
46
|
|
|
31
|
-
// Read
|
|
32
|
-
let
|
|
47
|
+
// Read PostToolUse event from stdin
|
|
48
|
+
let event: PostToolUseEvent;
|
|
33
49
|
try {
|
|
34
50
|
const input = readFileSync(0, 'utf-8');
|
|
35
|
-
|
|
36
|
-
} catch
|
|
37
|
-
// Silent failure - don't break Claude Code execution
|
|
51
|
+
event = JSON.parse(input) as PostToolUseEvent;
|
|
52
|
+
} catch {
|
|
38
53
|
process.exit(0);
|
|
39
54
|
}
|
|
40
55
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
-
if (!
|
|
44
|
-
// No usage data - not a Claude API call
|
|
56
|
+
// Need transcript path to read usage data
|
|
57
|
+
const transcriptPath = event?.transcript_path;
|
|
58
|
+
if (!transcriptPath || !existsSync(transcriptPath)) {
|
|
45
59
|
process.exit(0);
|
|
46
60
|
}
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
const inputTokens = usage.input_tokens || 0;
|
|
50
|
-
const outputTokens = usage.output_tokens || 0;
|
|
51
|
-
const cacheReadTokens = usage.cache_read_input_tokens || 0;
|
|
52
|
-
const cacheWriteTokens = usage.cache_creation_input_tokens || 0;
|
|
62
|
+
const sessionId = event?.session_id || 'unknown';
|
|
53
63
|
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
64
|
+
// State tracking: byte offset per session to avoid re-processing
|
|
65
|
+
const stateDir = join(process.env.HOME || homedir(), '.panopticon', 'costs', 'state');
|
|
66
|
+
mkdirSync(stateDir, { recursive: true });
|
|
67
|
+
const stateFile = join(stateDir, `${sessionId}.offset`);
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
let lastOffset = 0;
|
|
70
|
+
if (existsSync(stateFile)) {
|
|
71
|
+
try {
|
|
72
|
+
lastOffset = parseInt(readFileSync(stateFile, 'utf-8').trim(), 10) || 0;
|
|
73
|
+
} catch { /* start from 0 */ }
|
|
74
|
+
}
|
|
61
75
|
|
|
62
|
-
//
|
|
63
|
-
let
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
76
|
+
// Read only NEW content from the transcript (efficient for large files)
|
|
77
|
+
let fd: number;
|
|
78
|
+
try {
|
|
79
|
+
fd = openSync(transcriptPath, 'r');
|
|
80
|
+
} catch {
|
|
81
|
+
process.exit(0);
|
|
68
82
|
}
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
const stat = fstatSync(fd);
|
|
85
|
+
if (stat.size <= lastOffset) {
|
|
86
|
+
closeSync(fd);
|
|
87
|
+
// Save current size even if no new content (handles file truncation)
|
|
88
|
+
writeFileSync(stateFile, String(stat.size), 'utf-8');
|
|
74
89
|
process.exit(0);
|
|
75
90
|
}
|
|
76
91
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// Get agent
|
|
86
|
-
// PANOPTICON_AGENT_ID should always be set by pan work or heartbeat-hook
|
|
87
|
-
// If not set, use a fallback that makes it clear costs are unattributed
|
|
92
|
+
const bytesToRead = stat.size - lastOffset;
|
|
93
|
+
const buffer = Buffer.alloc(bytesToRead);
|
|
94
|
+
readSync(fd, buffer, 0, bytesToRead, lastOffset);
|
|
95
|
+
closeSync(fd);
|
|
96
|
+
|
|
97
|
+
const newContent = buffer.toString('utf-8');
|
|
98
|
+
const lines = newContent.split('\n');
|
|
99
|
+
|
|
100
|
+
// Get agent/issue context from environment
|
|
88
101
|
const agentId: string = process.env.PANOPTICON_AGENT_ID || 'unattributed';
|
|
89
102
|
const issueId: string = process.env.PANOPTICON_ISSUE_ID || 'UNKNOWN';
|
|
90
103
|
const sessionType: string = process.env.PANOPTICON_SESSION_TYPE || 'implementation';
|
|
91
104
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
// Process new transcript lines looking for assistant messages with usage
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (!line.trim()) continue;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const entry = JSON.parse(line) as TranscriptEntry;
|
|
111
|
+
|
|
112
|
+
// Only process assistant messages that have usage data
|
|
113
|
+
if (entry.type !== 'assistant' || !entry.message?.usage) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const usage = entry.message.usage;
|
|
118
|
+
const model: string = entry.message.model || 'claude-sonnet-4';
|
|
119
|
+
|
|
120
|
+
const inputTokens = usage.input_tokens || 0;
|
|
121
|
+
const outputTokens = usage.output_tokens || 0;
|
|
122
|
+
const cacheReadTokens = usage.cache_read_input_tokens || 0;
|
|
123
|
+
const cacheWriteTokens = usage.cache_creation_input_tokens || 0;
|
|
124
|
+
|
|
125
|
+
// Skip entries with zero tokens
|
|
126
|
+
if (inputTokens === 0 && outputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Determine provider from model name
|
|
131
|
+
let provider: AIProvider = 'anthropic';
|
|
132
|
+
if (model.includes('gpt')) {
|
|
133
|
+
provider = 'openai';
|
|
134
|
+
} else if (model.includes('gemini')) {
|
|
135
|
+
provider = 'google';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Get pricing and calculate cost
|
|
139
|
+
const pricing = getPricing(provider, model);
|
|
140
|
+
if (!pricing) continue;
|
|
141
|
+
|
|
142
|
+
const cost = calculateCost({
|
|
143
|
+
inputTokens,
|
|
144
|
+
outputTokens,
|
|
145
|
+
cacheReadTokens,
|
|
146
|
+
cacheWriteTokens,
|
|
147
|
+
cacheTTL: '5m',
|
|
148
|
+
}, pricing);
|
|
149
|
+
|
|
150
|
+
// Record the cost event
|
|
151
|
+
appendCostEvent({
|
|
152
|
+
ts: new Date().toISOString(),
|
|
153
|
+
type: 'cost',
|
|
154
|
+
agentId,
|
|
155
|
+
issueId,
|
|
156
|
+
sessionType,
|
|
157
|
+
provider,
|
|
158
|
+
model,
|
|
159
|
+
input: inputTokens,
|
|
160
|
+
output: outputTokens,
|
|
161
|
+
cacheRead: cacheReadTokens,
|
|
162
|
+
cacheWrite: cacheWriteTokens,
|
|
163
|
+
cost,
|
|
164
|
+
});
|
|
165
|
+
} catch {
|
|
166
|
+
// Skip malformed lines silently
|
|
167
|
+
}
|
|
111
168
|
}
|
|
112
169
|
|
|
170
|
+
// Save new byte offset for next invocation
|
|
171
|
+
writeFileSync(stateFile, String(stat.size), 'utf-8');
|
|
172
|
+
|
|
113
173
|
process.exit(0);
|
package/dist/chunk-J3J32DIR.js
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SETTINGS_FILE,
|
|
3
|
-
init_paths
|
|
4
|
-
} from "./chunk-6HXKTOD7.js";
|
|
5
|
-
import {
|
|
6
|
-
__esm,
|
|
7
|
-
init_esm_shims
|
|
8
|
-
} from "./chunk-ZHC57RCV.js";
|
|
9
|
-
|
|
10
|
-
// src/lib/settings.ts
|
|
11
|
-
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
12
|
-
function deepMerge(defaults, overrides) {
|
|
13
|
-
const result = { ...defaults };
|
|
14
|
-
for (const key of Object.keys(overrides)) {
|
|
15
|
-
const defaultVal = defaults[key];
|
|
16
|
-
const overrideVal = overrides[key];
|
|
17
|
-
if (overrideVal === void 0) continue;
|
|
18
|
-
if (typeof defaultVal === "object" && defaultVal !== null && !Array.isArray(defaultVal) && typeof overrideVal === "object" && overrideVal !== null && !Array.isArray(overrideVal)) {
|
|
19
|
-
result[key] = deepMerge(defaultVal, overrideVal);
|
|
20
|
-
} else {
|
|
21
|
-
result[key] = overrideVal;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
function loadSettings() {
|
|
27
|
-
let settings;
|
|
28
|
-
if (!existsSync(SETTINGS_FILE)) {
|
|
29
|
-
settings = getDefaultSettings();
|
|
30
|
-
} else {
|
|
31
|
-
try {
|
|
32
|
-
const content = readFileSync(SETTINGS_FILE, "utf8");
|
|
33
|
-
const parsed = JSON.parse(content);
|
|
34
|
-
settings = deepMerge(DEFAULT_SETTINGS, parsed);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.error("Warning: Failed to parse settings.json, using defaults");
|
|
37
|
-
settings = getDefaultSettings();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const envApiKeys = {};
|
|
41
|
-
if (process.env.OPENAI_API_KEY) envApiKeys.openai = process.env.OPENAI_API_KEY;
|
|
42
|
-
if (process.env.GOOGLE_API_KEY) envApiKeys.google = process.env.GOOGLE_API_KEY;
|
|
43
|
-
if (process.env.ZAI_API_KEY) envApiKeys.zai = process.env.ZAI_API_KEY;
|
|
44
|
-
if (process.env.KIMI_API_KEY) envApiKeys.kimi = process.env.KIMI_API_KEY;
|
|
45
|
-
settings.api_keys = {
|
|
46
|
-
...envApiKeys,
|
|
47
|
-
...settings.api_keys
|
|
48
|
-
};
|
|
49
|
-
return settings;
|
|
50
|
-
}
|
|
51
|
-
function saveSettings(settings) {
|
|
52
|
-
const content = JSON.stringify(settings, null, 2);
|
|
53
|
-
writeFileSync(SETTINGS_FILE, content, "utf8");
|
|
54
|
-
}
|
|
55
|
-
function validateSettings(settings) {
|
|
56
|
-
if (!settings.models) {
|
|
57
|
-
return "Missing models configuration";
|
|
58
|
-
}
|
|
59
|
-
if (!settings.models.specialists) {
|
|
60
|
-
return "Missing specialists configuration";
|
|
61
|
-
}
|
|
62
|
-
const specialists = settings.models.specialists;
|
|
63
|
-
if (!specialists.review_agent || !specialists.test_agent || !specialists.merge_agent) {
|
|
64
|
-
return "Missing specialist agent model configuration";
|
|
65
|
-
}
|
|
66
|
-
if (!settings.models.planning_agent) {
|
|
67
|
-
return "Missing planning_agent configuration";
|
|
68
|
-
}
|
|
69
|
-
if (!settings.models.complexity) {
|
|
70
|
-
return "Missing complexity configuration";
|
|
71
|
-
}
|
|
72
|
-
const complexity = settings.models.complexity;
|
|
73
|
-
const requiredLevels = ["trivial", "simple", "medium", "complex", "expert"];
|
|
74
|
-
for (const level of requiredLevels) {
|
|
75
|
-
if (!complexity[level]) {
|
|
76
|
-
return `Missing complexity level: ${level}`;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
if (!settings.api_keys) {
|
|
80
|
-
return "Missing api_keys configuration";
|
|
81
|
-
}
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
function getDefaultSettings() {
|
|
85
|
-
return JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
|
|
86
|
-
}
|
|
87
|
-
function getAvailableModels(settings) {
|
|
88
|
-
const anthropicModels = [
|
|
89
|
-
"claude-opus-4-6",
|
|
90
|
-
"claude-sonnet-4-5",
|
|
91
|
-
"claude-haiku-4-5"
|
|
92
|
-
];
|
|
93
|
-
const openaiModels = settings.api_keys.openai ? ["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"] : [];
|
|
94
|
-
const googleModels = settings.api_keys.google ? ["gemini-3-pro-preview", "gemini-3-flash-preview"] : [];
|
|
95
|
-
const zaiModels = settings.api_keys.zai ? ["glm-4.7", "glm-4.7-flash"] : [];
|
|
96
|
-
const kimiModels = settings.api_keys.kimi ? ["kimi-k2", "kimi-k2.5"] : [];
|
|
97
|
-
return {
|
|
98
|
-
anthropic: anthropicModels,
|
|
99
|
-
openai: openaiModels,
|
|
100
|
-
google: googleModels,
|
|
101
|
-
zai: zaiModels,
|
|
102
|
-
kimi: kimiModels
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
function isAnthropicModel(modelId) {
|
|
106
|
-
return modelId.startsWith("claude-");
|
|
107
|
-
}
|
|
108
|
-
function getClaudeModelFlag(modelId) {
|
|
109
|
-
const modelMap = {
|
|
110
|
-
"claude-opus-4-6": "opus",
|
|
111
|
-
"claude-sonnet-4-5": "sonnet",
|
|
112
|
-
"claude-haiku-4-5": "haiku"
|
|
113
|
-
};
|
|
114
|
-
return modelMap[modelId] || "sonnet";
|
|
115
|
-
}
|
|
116
|
-
function getAgentCommand(modelId) {
|
|
117
|
-
if (isAnthropicModel(modelId)) {
|
|
118
|
-
return {
|
|
119
|
-
command: "claude",
|
|
120
|
-
args: ["--model", getClaudeModelFlag(modelId)]
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
command: "claude-code-router",
|
|
125
|
-
args: []
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
var DEFAULT_SETTINGS;
|
|
129
|
-
var init_settings = __esm({
|
|
130
|
-
"src/lib/settings.ts"() {
|
|
131
|
-
"use strict";
|
|
132
|
-
init_esm_shims();
|
|
133
|
-
init_paths();
|
|
134
|
-
DEFAULT_SETTINGS = {
|
|
135
|
-
models: {
|
|
136
|
-
specialists: {
|
|
137
|
-
review_agent: "claude-opus-4-6",
|
|
138
|
-
test_agent: "claude-sonnet-4-5",
|
|
139
|
-
merge_agent: "claude-sonnet-4-5"
|
|
140
|
-
},
|
|
141
|
-
planning_agent: "claude-opus-4-6",
|
|
142
|
-
status_review: "claude-opus-4-6",
|
|
143
|
-
complexity: {
|
|
144
|
-
trivial: "claude-haiku-4-5",
|
|
145
|
-
simple: "claude-haiku-4-5",
|
|
146
|
-
medium: "kimi-k2.5",
|
|
147
|
-
complex: "kimi-k2.5",
|
|
148
|
-
expert: "claude-opus-4-6"
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
api_keys: {}
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// src/lib/providers.ts
|
|
157
|
-
function getProviderForModel(modelId) {
|
|
158
|
-
if (["claude-opus-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"].includes(modelId)) {
|
|
159
|
-
return PROVIDERS.anthropic;
|
|
160
|
-
}
|
|
161
|
-
if (["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"].includes(modelId)) {
|
|
162
|
-
return PROVIDERS.openai;
|
|
163
|
-
}
|
|
164
|
-
if (["gemini-3-pro-preview", "gemini-3-flash-preview"].includes(modelId)) {
|
|
165
|
-
return PROVIDERS.google;
|
|
166
|
-
}
|
|
167
|
-
if (["glm-4.7", "glm-4.7-flash"].includes(modelId)) {
|
|
168
|
-
return PROVIDERS.zai;
|
|
169
|
-
}
|
|
170
|
-
if (["kimi-k2", "kimi-k2.5"].includes(modelId)) {
|
|
171
|
-
return PROVIDERS.kimi;
|
|
172
|
-
}
|
|
173
|
-
return PROVIDERS.anthropic;
|
|
174
|
-
}
|
|
175
|
-
function requiresRouter(provider) {
|
|
176
|
-
return PROVIDERS[provider].compatibility === "router";
|
|
177
|
-
}
|
|
178
|
-
function getRouterProviders() {
|
|
179
|
-
return Object.values(PROVIDERS).filter((p) => p.compatibility === "router");
|
|
180
|
-
}
|
|
181
|
-
function getDirectProviders() {
|
|
182
|
-
return Object.values(PROVIDERS).filter((p) => p.compatibility === "direct");
|
|
183
|
-
}
|
|
184
|
-
function needsRouter(apiKeys) {
|
|
185
|
-
return !!(apiKeys.openai || apiKeys.google);
|
|
186
|
-
}
|
|
187
|
-
function getProviderEnv(provider, apiKey) {
|
|
188
|
-
if (provider.compatibility === "direct") {
|
|
189
|
-
const env = {};
|
|
190
|
-
if (provider.baseUrl) {
|
|
191
|
-
env.ANTHROPIC_BASE_URL = provider.baseUrl;
|
|
192
|
-
}
|
|
193
|
-
if (provider.name !== "anthropic") {
|
|
194
|
-
env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
195
|
-
}
|
|
196
|
-
if (provider.name === "zai") {
|
|
197
|
-
env.API_TIMEOUT_MS = "300000";
|
|
198
|
-
}
|
|
199
|
-
return env;
|
|
200
|
-
} else {
|
|
201
|
-
return {
|
|
202
|
-
ANTHROPIC_BASE_URL: "http://localhost:3456",
|
|
203
|
-
ANTHROPIC_AUTH_TOKEN: "router-managed"
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
var PROVIDERS;
|
|
208
|
-
var init_providers = __esm({
|
|
209
|
-
"src/lib/providers.ts"() {
|
|
210
|
-
"use strict";
|
|
211
|
-
init_esm_shims();
|
|
212
|
-
PROVIDERS = {
|
|
213
|
-
anthropic: {
|
|
214
|
-
name: "anthropic",
|
|
215
|
-
displayName: "Anthropic",
|
|
216
|
-
compatibility: "direct",
|
|
217
|
-
models: ["claude-opus-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"],
|
|
218
|
-
tested: true,
|
|
219
|
-
description: "Native Claude API"
|
|
220
|
-
},
|
|
221
|
-
kimi: {
|
|
222
|
-
name: "kimi",
|
|
223
|
-
displayName: "Kimi (Moonshot AI)",
|
|
224
|
-
compatibility: "direct",
|
|
225
|
-
baseUrl: "https://api.kimi.com/coding/",
|
|
226
|
-
models: [],
|
|
227
|
-
// Kimi uses same model names as Anthropic
|
|
228
|
-
tested: true,
|
|
229
|
-
description: "Anthropic-compatible API, tested 2026-01-28"
|
|
230
|
-
},
|
|
231
|
-
zai: {
|
|
232
|
-
name: "zai",
|
|
233
|
-
displayName: "Z.AI (GLM)",
|
|
234
|
-
compatibility: "direct",
|
|
235
|
-
baseUrl: "https://api.z.ai/api/anthropic",
|
|
236
|
-
models: ["glm-4.7", "glm-4.7-flash"],
|
|
237
|
-
tested: true,
|
|
238
|
-
description: "Anthropic-compatible API, tested 2026-01-28"
|
|
239
|
-
},
|
|
240
|
-
openai: {
|
|
241
|
-
name: "openai",
|
|
242
|
-
displayName: "OpenAI",
|
|
243
|
-
compatibility: "router",
|
|
244
|
-
models: ["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"],
|
|
245
|
-
tested: false,
|
|
246
|
-
description: "Requires claude-code-router for API translation"
|
|
247
|
-
},
|
|
248
|
-
google: {
|
|
249
|
-
name: "google",
|
|
250
|
-
displayName: "Google (Gemini)",
|
|
251
|
-
compatibility: "router",
|
|
252
|
-
models: ["gemini-3-pro-preview", "gemini-3-flash-preview"],
|
|
253
|
-
tested: false,
|
|
254
|
-
description: "Requires claude-code-router for API translation"
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
export {
|
|
261
|
-
loadSettings,
|
|
262
|
-
saveSettings,
|
|
263
|
-
validateSettings,
|
|
264
|
-
getDefaultSettings,
|
|
265
|
-
getAvailableModels,
|
|
266
|
-
isAnthropicModel,
|
|
267
|
-
getClaudeModelFlag,
|
|
268
|
-
getAgentCommand,
|
|
269
|
-
init_settings,
|
|
270
|
-
PROVIDERS,
|
|
271
|
-
getProviderForModel,
|
|
272
|
-
requiresRouter,
|
|
273
|
-
getRouterProviders,
|
|
274
|
-
getDirectProviders,
|
|
275
|
-
needsRouter,
|
|
276
|
-
getProviderEnv,
|
|
277
|
-
init_providers
|
|
278
|
-
};
|
|
279
|
-
//# sourceMappingURL=chunk-J3J32DIR.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/settings.ts","../src/lib/providers.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from 'fs';\nimport { SETTINGS_FILE } from './paths.js';\n\n// Model identifiers\nexport type AnthropicModel = 'claude-opus-4-6' | 'claude-sonnet-4-5' | 'claude-haiku-4-5';\nexport type OpenAIModel = 'gpt-5.2-codex' | 'o3-deep-research' | 'gpt-4o' | 'gpt-4o-mini';\nexport type GoogleModel = 'gemini-3-pro-preview' | 'gemini-3-flash-preview' | 'gemini-2.5-pro' | 'gemini-2.5-flash';\nexport type ZAIModel = 'glm-4.7' | 'glm-4.7-flash';\nexport type KimiModel = 'kimi-k2' | 'kimi-k2.5';\nexport type ModelId = AnthropicModel | OpenAIModel | GoogleModel | ZAIModel | KimiModel;\n\n// Task complexity levels\nexport type ComplexityLevel = 'trivial' | 'simple' | 'medium' | 'complex' | 'expert';\n\n// Specialist agent types\nexport interface SpecialistModels {\n review_agent: ModelId;\n test_agent: ModelId;\n merge_agent: ModelId;\n}\n\n// Complexity-based model mapping\nexport type ComplexityModels = {\n [K in ComplexityLevel]: ModelId;\n};\n\n// All model configuration\nexport interface ModelsConfig {\n specialists: SpecialistModels;\n planning_agent: ModelId;\n status_review: ModelId;\n complexity: ComplexityModels;\n}\n\n// API keys for external providers\nexport interface ApiKeysConfig {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n}\n\n// Complete settings structure\nexport interface SettingsConfig {\n models: ModelsConfig;\n api_keys: ApiKeysConfig;\n}\n\n// Default settings - match optimal defaults from settings-api.ts\nconst DEFAULT_SETTINGS: SettingsConfig = {\n models: {\n specialists: {\n review_agent: 'claude-opus-4-6',\n test_agent: 'claude-sonnet-4-5',\n merge_agent: 'claude-sonnet-4-5',\n },\n planning_agent: 'claude-opus-4-6',\n status_review: 'claude-opus-4-6',\n complexity: {\n trivial: 'claude-haiku-4-5',\n simple: 'claude-haiku-4-5',\n medium: 'kimi-k2.5',\n complex: 'kimi-k2.5',\n expert: 'claude-opus-4-6',\n },\n },\n api_keys: {},\n};\n\n/**\n * Deep merge utility that recursively merges objects.\n * - Recursively merges nested objects\n * - User values take precedence over defaults\n */\nfunction deepMerge<T extends object>(defaults: T, overrides: Partial<T>): T {\n const result = { ...defaults };\n\n for (const key of Object.keys(overrides) as (keyof T)[]) {\n const defaultVal = defaults[key];\n const overrideVal = overrides[key];\n\n // Skip undefined values in overrides\n if (overrideVal === undefined) continue;\n\n // Deep merge if both values are non-array objects\n if (\n typeof defaultVal === 'object' &&\n defaultVal !== null &&\n !Array.isArray(defaultVal) &&\n typeof overrideVal === 'object' &&\n overrideVal !== null &&\n !Array.isArray(overrideVal)\n ) {\n result[key] = deepMerge(defaultVal, overrideVal as any);\n } else {\n // For primitives or null - override wins\n result[key] = overrideVal as T[keyof T];\n }\n }\n\n return result;\n}\n\n/**\n * Load settings from ~/.panopticon/settings.json\n * Returns default settings if file doesn't exist or is invalid\n * Also loads API keys from environment variables as fallback\n */\nexport function loadSettings(): SettingsConfig {\n let settings: SettingsConfig;\n\n if (!existsSync(SETTINGS_FILE)) {\n settings = getDefaultSettings();\n } else {\n try {\n const content = readFileSync(SETTINGS_FILE, 'utf8');\n const parsed = JSON.parse(content) as Partial<SettingsConfig>;\n settings = deepMerge(DEFAULT_SETTINGS, parsed);\n } catch (error) {\n console.error('Warning: Failed to parse settings.json, using defaults');\n settings = getDefaultSettings();\n }\n }\n\n // Load API keys from environment variables as fallback\n // This allows using ~/.panopticon.env for API keys\n const envApiKeys: ApiKeysConfig = {};\n if (process.env.OPENAI_API_KEY) envApiKeys.openai = process.env.OPENAI_API_KEY;\n if (process.env.GOOGLE_API_KEY) envApiKeys.google = process.env.GOOGLE_API_KEY;\n if (process.env.ZAI_API_KEY) envApiKeys.zai = process.env.ZAI_API_KEY;\n if (process.env.KIMI_API_KEY) envApiKeys.kimi = process.env.KIMI_API_KEY;\n\n // Merge env vars as fallback (settings.json takes precedence)\n settings.api_keys = {\n ...envApiKeys,\n ...settings.api_keys,\n };\n\n return settings;\n}\n\n/**\n * Save settings to ~/.panopticon/settings.json\n * Writes with pretty formatting (2-space indent)\n */\nexport function saveSettings(settings: SettingsConfig): void {\n const content = JSON.stringify(settings, null, 2);\n writeFileSync(SETTINGS_FILE, content, 'utf8');\n}\n\n/**\n * Validate settings structure and model IDs\n * Returns error message if invalid, null if valid\n */\nexport function validateSettings(settings: SettingsConfig): string | null {\n // Validate models structure\n if (!settings.models) {\n return 'Missing models configuration';\n }\n\n // Validate specialists\n if (!settings.models.specialists) {\n return 'Missing specialists configuration';\n }\n const specialists = settings.models.specialists;\n if (!specialists.review_agent || !specialists.test_agent || !specialists.merge_agent) {\n return 'Missing specialist agent model configuration';\n }\n\n // Validate planning agent\n if (!settings.models.planning_agent) {\n return 'Missing planning_agent configuration';\n }\n\n // Validate complexity levels\n if (!settings.models.complexity) {\n return 'Missing complexity configuration';\n }\n const complexity = settings.models.complexity;\n const requiredLevels: ComplexityLevel[] = ['trivial', 'simple', 'medium', 'complex', 'expert'];\n for (const level of requiredLevels) {\n if (!complexity[level]) {\n return `Missing complexity level: ${level}`;\n }\n }\n\n // Validate api_keys structure (optional keys)\n if (!settings.api_keys) {\n return 'Missing api_keys configuration';\n }\n\n return null;\n}\n\n/**\n * Get a deep copy of the default settings\n */\nexport function getDefaultSettings(): SettingsConfig {\n return JSON.parse(JSON.stringify(DEFAULT_SETTINGS));\n}\n\n/**\n * Get available models for a provider based on configured API keys\n * Returns empty array if provider API key is not configured\n */\nexport function getAvailableModels(settings: SettingsConfig): {\n anthropic: AnthropicModel[];\n openai: OpenAIModel[];\n google: GoogleModel[];\n zai: ZAIModel[];\n kimi: KimiModel[];\n} {\n const anthropicModels: AnthropicModel[] = [\n 'claude-opus-4-6',\n 'claude-sonnet-4-5',\n 'claude-haiku-4-5',\n ];\n\n const openaiModels: OpenAIModel[] = settings.api_keys.openai\n ? ['gpt-5.2-codex', 'o3-deep-research', 'gpt-4o', 'gpt-4o-mini']\n : [];\n\n const googleModels: GoogleModel[] = settings.api_keys.google\n ? ['gemini-3-pro-preview', 'gemini-3-flash-preview']\n : [];\n\n const zaiModels: ZAIModel[] = settings.api_keys.zai\n ? ['glm-4.7', 'glm-4.7-flash']\n : [];\n\n const kimiModels: KimiModel[] = settings.api_keys.kimi\n ? ['kimi-k2', 'kimi-k2.5']\n : [];\n\n return {\n anthropic: anthropicModels,\n openai: openaiModels,\n google: googleModels,\n zai: zaiModels,\n kimi: kimiModels,\n };\n}\n\n/**\n * Check if a model ID is an Anthropic model\n * Anthropic models can be run directly with `claude` CLI\n */\nexport function isAnthropicModel(modelId: ModelId | string): boolean {\n return modelId.startsWith('claude-');\n}\n\n/**\n * Get the Claude CLI model flag for an Anthropic model\n * Maps our model IDs to Claude's expected format\n */\nexport function getClaudeModelFlag(modelId: ModelId | string): string {\n const modelMap: Record<string, string> = {\n 'claude-opus-4-6': 'opus',\n 'claude-sonnet-4-5': 'sonnet',\n 'claude-haiku-4-5': 'haiku',\n };\n return modelMap[modelId] || 'sonnet';\n}\n\n/**\n * Get the command to run an agent with a specific model\n * Returns 'claude' for Anthropic models, 'claude-code-router' for others\n */\nexport function getAgentCommand(modelId: ModelId | string): { command: string; args: string[] } {\n if (isAnthropicModel(modelId)) {\n return {\n command: 'claude',\n args: ['--model', getClaudeModelFlag(modelId)],\n };\n }\n // Non-Anthropic models require the router\n return {\n command: 'claude-code-router',\n args: [],\n };\n}\n","/**\r\n * Provider Configuration and Compatibility\r\n *\r\n * Defines which LLM providers are compatible with Claude Code's API format.\r\n * - Direct providers: Implement Anthropic-compatible API (no router needed)\r\n * - Router providers: Require claude-code-router for API translation\r\n */\r\n\r\nimport type { ModelId, AnthropicModel, OpenAIModel, GoogleModel, ZAIModel } from './settings.js';\r\n\r\nexport type ProviderName = 'anthropic' | 'kimi' | 'openai' | 'google' | 'zai';\r\n\r\n/**\r\n * Provider compatibility types\r\n * - direct: Anthropic-compatible API, use ANTHROPIC_BASE_URL directly\r\n * - router: Incompatible API, requires claude-code-router for translation\r\n */\r\nexport type ProviderCompatibility = 'direct' | 'router';\r\n\r\n/**\r\n * Provider configuration\r\n */\r\nexport interface ProviderConfig {\r\n name: ProviderName;\r\n displayName: string;\r\n compatibility: ProviderCompatibility;\r\n baseUrl?: string; // For direct providers\r\n models: ModelId[];\r\n tested: boolean; // Whether compatibility has been verified\r\n description: string;\r\n}\r\n\r\n/**\r\n * All provider configurations\r\n */\r\nexport const PROVIDERS: Record<ProviderName, ProviderConfig> = {\r\n anthropic: {\r\n name: 'anthropic',\r\n displayName: 'Anthropic',\r\n compatibility: 'direct',\r\n models: ['claude-opus-4-6', 'claude-sonnet-4-5', 'claude-haiku-4-5'],\r\n tested: true,\r\n description: 'Native Claude API',\r\n },\r\n\r\n kimi: {\r\n name: 'kimi',\r\n displayName: 'Kimi (Moonshot AI)',\r\n compatibility: 'direct',\r\n baseUrl: 'https://api.kimi.com/coding/',\r\n models: [], // Kimi uses same model names as Anthropic\r\n tested: true,\r\n description: 'Anthropic-compatible API, tested 2026-01-28',\r\n },\r\n\r\n zai: {\r\n name: 'zai',\r\n displayName: 'Z.AI (GLM)',\r\n compatibility: 'direct',\r\n baseUrl: 'https://api.z.ai/api/anthropic',\r\n models: ['glm-4.7', 'glm-4.7-flash'],\r\n tested: true,\r\n description: 'Anthropic-compatible API, tested 2026-01-28',\r\n },\r\n\r\n openai: {\r\n name: 'openai',\r\n displayName: 'OpenAI',\r\n compatibility: 'router',\r\n models: ['gpt-5.2-codex', 'o3-deep-research', 'gpt-4o', 'gpt-4o-mini'],\r\n tested: false,\r\n description: 'Requires claude-code-router for API translation',\r\n },\r\n\r\n google: {\r\n name: 'google',\r\n displayName: 'Google (Gemini)',\r\n compatibility: 'router',\r\n models: ['gemini-3-pro-preview', 'gemini-3-flash-preview'],\r\n tested: false,\r\n description: 'Requires claude-code-router for API translation',\r\n },\r\n};\r\n\r\n/**\r\n * Get provider for a given model ID\r\n */\r\nexport function getProviderForModel(modelId: ModelId): ProviderConfig {\r\n // Check Anthropic models\r\n if (['claude-opus-4-6', 'claude-sonnet-4-5', 'claude-haiku-4-5'].includes(modelId)) {\r\n return PROVIDERS.anthropic;\r\n }\r\n\r\n // Check OpenAI models\r\n if (['gpt-5.2-codex', 'o3-deep-research', 'gpt-4o', 'gpt-4o-mini'].includes(modelId)) {\r\n return PROVIDERS.openai;\r\n }\r\n\r\n // Check Google models\r\n if (['gemini-3-pro-preview', 'gemini-3-flash-preview'].includes(modelId)) {\r\n return PROVIDERS.google;\r\n }\r\n\r\n // Check Z.AI models\r\n if (['glm-4.7', 'glm-4.7-flash'].includes(modelId)) {\r\n return PROVIDERS.zai;\r\n }\r\n\r\n // Check Kimi models\r\n if (['kimi-k2', 'kimi-k2.5'].includes(modelId)) {\r\n return PROVIDERS.kimi;\r\n }\r\n\r\n // Default to Anthropic if unknown\r\n return PROVIDERS.anthropic;\r\n}\r\n\r\n/**\r\n * Check if a provider requires claude-code-router\r\n */\r\nexport function requiresRouter(provider: ProviderName): boolean {\r\n return PROVIDERS[provider].compatibility === 'router';\r\n}\r\n\r\n/**\r\n * Get all providers that require router (have router compatibility)\r\n */\r\nexport function getRouterProviders(): ProviderConfig[] {\r\n return Object.values(PROVIDERS).filter(p => p.compatibility === 'router');\r\n}\r\n\r\n/**\r\n * Get all direct-compatible providers\r\n */\r\nexport function getDirectProviders(): ProviderConfig[] {\r\n return Object.values(PROVIDERS).filter(p => p.compatibility === 'direct');\r\n}\r\n\r\n/**\r\n * Check if any configured providers require router\r\n * Used to determine if router installation is needed\r\n */\r\nexport function needsRouter(apiKeys: { openai?: string; google?: string; zai?: string }): boolean {\r\n return !!(apiKeys.openai || apiKeys.google);\r\n}\r\n\r\n/**\r\n * Get environment variables for spawning agent with specific provider\r\n */\r\nexport function getProviderEnv(\r\n provider: ProviderConfig,\r\n apiKey: string\r\n): Record<string, string> {\r\n if (provider.compatibility === 'direct') {\r\n // Direct providers use ANTHROPIC_BASE_URL\r\n const env: Record<string, string> = {};\r\n\r\n if (provider.baseUrl) {\r\n env.ANTHROPIC_BASE_URL = provider.baseUrl;\r\n }\r\n\r\n if (provider.name !== 'anthropic') {\r\n // Non-Anthropic providers need auth token\r\n env.ANTHROPIC_AUTH_TOKEN = apiKey;\r\n }\r\n\r\n // Z.AI recommends longer timeout\r\n if (provider.name === 'zai') {\r\n env.API_TIMEOUT_MS = '300000';\r\n }\r\n\r\n return env;\r\n } else {\r\n // Router providers use local router proxy\r\n return {\r\n ANTHROPIC_BASE_URL: 'http://localhost:3456',\r\n ANTHROPIC_AUTH_TOKEN: 'router-managed',\r\n };\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|