panopticon-cli 0.5.1 → 0.5.4
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-5OPQKM5K.js → agents-HNMF52RM.js} +7 -6
- package/dist/{chunk-F5555J3A.js → chunk-4HST45MO.js} +13 -27
- package/dist/chunk-4HST45MO.js.map +1 -0
- package/dist/{chunk-YLPSQAM2.js → chunk-565HZ6VV.js} +2 -2
- package/dist/chunk-6N2KBSJA.js +452 -0
- package/dist/chunk-6N2KBSJA.js.map +1 -0
- package/dist/{chunk-4YSYJ4HM.js → chunk-DFNVHK3N.js} +2 -2
- package/dist/{chunk-7SN4L4PH.js → chunk-HOGYHJ2G.js} +2 -2
- package/dist/{chunk-FTCPTHIJ.js → chunk-HRU7S4TA.js} +24 -7
- package/dist/chunk-HRU7S4TA.js.map +1 -0
- package/dist/{chunk-OWHXCGVO.js → chunk-ID4OYXVH.js} +378 -101
- package/dist/chunk-ID4OYXVH.js.map +1 -0
- package/dist/{chunk-NLQRED36.js → chunk-KBHRXV5T.js} +3 -3
- package/dist/chunk-KBHRXV5T.js.map +1 -0
- package/dist/{chunk-VHKSS7QX.js → chunk-KY2E2Q3T.js} +25 -19
- package/dist/chunk-KY2E2Q3T.js.map +1 -0
- package/dist/{chunk-CWELWPWQ.js → chunk-MOPGR3CL.js} +1 -1
- package/dist/chunk-MOPGR3CL.js.map +1 -0
- package/dist/{chunk-2V4NF7J2.js → chunk-RLZQB7HS.js} +2 -2
- package/dist/chunk-RLZQB7HS.js.map +1 -0
- package/dist/{chunk-76F6DSVS.js → chunk-T7BBPDEJ.js} +11 -7
- package/dist/chunk-T7BBPDEJ.js.map +1 -0
- package/dist/chunk-USYP2SBE.js +317 -0
- package/dist/chunk-USYP2SBE.js.map +1 -0
- package/dist/{chunk-JM6V62LT.js → chunk-ZDNQFWR5.js} +2 -2
- package/dist/{chunk-JM6V62LT.js.map → chunk-ZDNQFWR5.js.map} +1 -1
- package/dist/{chunk-HJSM6E6U.js → chunk-ZP6EWSZV.js} +29 -322
- package/dist/chunk-ZP6EWSZV.js.map +1 -0
- package/dist/{chunk-PELXV435.js → chunk-ZTYHZMEC.js} +2 -2
- package/dist/chunk-ZTYHZMEC.js.map +1 -0
- package/dist/cli/index.js +1390 -777
- package/dist/cli/index.js.map +1 -1
- package/dist/config-yaml-OVZLKFMA.js +18 -0
- package/dist/dashboard/prompts/merge-agent.md +7 -5
- package/dist/dashboard/prompts/review-agent.md +12 -1
- package/dist/dashboard/prompts/test-agent.md +3 -1
- package/dist/dashboard/public/assets/index-DA6pnizT.js +767 -0
- package/dist/dashboard/public/assets/index-DSvt5pPn.css +32 -0
- package/dist/dashboard/public/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/dashboard/public/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/dashboard/public/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/dashboard/public/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/dashboard/public/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/dashboard/public/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/dashboard/public/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/dashboard/public/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/dashboard/public/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-400-normal-BnQMeOim.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-600-normal-BflQw4A9.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-600-normal-DjKNqYRj.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-ext-400-normal-CfP_5XZW.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-ext-400-normal-DRPE3kg4.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-ext-600-normal-DxxdqCpr.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-ext-600-normal-VcznFIpX.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-vietnamese-400-normal-B7xT_GF5.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-vietnamese-400-normal-BIWiOVfw.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-vietnamese-600-normal-D6zpsUhD.woff +0 -0
- package/dist/dashboard/public/assets/space-grotesk-vietnamese-600-normal-DUi7WF5p.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
- package/dist/dashboard/public/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
- package/dist/dashboard/public/index.html +5 -3
- package/dist/dashboard/server.js +4728 -2767
- package/dist/{feedback-writer-VRMMWWTW.js → feedback-writer-T43PI5S2.js} +2 -2
- package/dist/{hume-WMAUBBV2.js → hume-CKJJ3OUU.js} +3 -3
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/{projects-CFX3RTDL.js → projects-KVM3MN3Y.js} +2 -2
- package/dist/{remote-agents-TFSMW7GN.js → remote-agents-ULPD6C5U.js} +3 -3
- package/dist/{remote-workspace-7FPGF2RM.js → remote-workspace-XX6ARE6I.js} +3 -3
- package/dist/{review-status-TDPSOU5J.js → review-status-XKUKZF6J.js} +3 -2
- package/dist/{specialist-context-WGUUYDWY.js → specialist-context-C66TEMXS.js} +6 -5
- package/dist/{specialist-context-WGUUYDWY.js.map → specialist-context-C66TEMXS.js.map} +1 -1
- package/dist/{specialist-logs-XJB5TCKJ.js → specialist-logs-CJKXM3SR.js} +6 -5
- package/dist/{specialists-5LBRHYFA.js → specialists-NXYD4Z62.js} +6 -5
- package/dist/{traefik-WFMQX2LY.js → traefik-5GL3Q7DJ.js} +3 -3
- package/dist/{tunnel-W2GZBLEV.js → tunnel-BKC7KLBX.js} +3 -3
- package/dist/{workspace-manager-E434Z45T.js → workspace-manager-ALBR62AS.js} +5 -5
- package/dist/workspace-manager-ALBR62AS.js.map +1 -0
- package/package.json +1 -1
- package/scripts/record-cost-event.js +8424 -42
- package/scripts/recover-costs-deep.mjs +209 -0
- package/scripts/recover-costs-proportional.mjs +206 -0
- package/scripts/recover-costs.mjs +169 -0
- package/scripts/work-agent-stop-hook +221 -24
- package/dist/chunk-2V4NF7J2.js.map +0 -1
- package/dist/chunk-76F6DSVS.js.map +0 -1
- package/dist/chunk-CWELWPWQ.js.map +0 -1
- package/dist/chunk-F5555J3A.js.map +0 -1
- package/dist/chunk-FTCPTHIJ.js.map +0 -1
- package/dist/chunk-HJSM6E6U.js.map +0 -1
- package/dist/chunk-NLQRED36.js.map +0 -1
- package/dist/chunk-OWHXCGVO.js.map +0 -1
- package/dist/chunk-PELXV435.js.map +0 -1
- package/dist/chunk-VHKSS7QX.js.map +0 -1
- package/dist/chunk-YGJ54GW2.js +0 -96
- package/dist/chunk-YGJ54GW2.js.map +0 -1
- package/dist/dashboard/public/assets/index-Ce6q21Fm.js +0 -743
- package/dist/dashboard/public/assets/index-NzpI0ItZ.css +0 -32
- package/dist/git-utils-I2UDKNZH.js +0 -131
- package/dist/git-utils-I2UDKNZH.js.map +0 -1
- /package/dist/{agents-5OPQKM5K.js.map → agents-HNMF52RM.js.map} +0 -0
- /package/dist/{chunk-YLPSQAM2.js.map → chunk-565HZ6VV.js.map} +0 -0
- /package/dist/{chunk-4YSYJ4HM.js.map → chunk-DFNVHK3N.js.map} +0 -0
- /package/dist/{chunk-7SN4L4PH.js.map → chunk-HOGYHJ2G.js.map} +0 -0
- /package/dist/{hume-WMAUBBV2.js.map → config-yaml-OVZLKFMA.js.map} +0 -0
- /package/dist/{feedback-writer-VRMMWWTW.js.map → feedback-writer-T43PI5S2.js.map} +0 -0
- /package/dist/{projects-CFX3RTDL.js.map → hume-CKJJ3OUU.js.map} +0 -0
- /package/dist/{remote-agents-TFSMW7GN.js.map → projects-KVM3MN3Y.js.map} +0 -0
- /package/dist/{review-status-TDPSOU5J.js.map → remote-agents-ULPD6C5U.js.map} +0 -0
- /package/dist/{remote-workspace-7FPGF2RM.js.map → remote-workspace-XX6ARE6I.js.map} +0 -0
- /package/dist/{specialist-logs-XJB5TCKJ.js.map → review-status-XKUKZF6J.js.map} +0 -0
- /package/dist/{specialists-5LBRHYFA.js.map → specialist-logs-CJKXM3SR.js.map} +0 -0
- /package/dist/{traefik-WFMQX2LY.js.map → specialists-NXYD4Z62.js.map} +0 -0
- /package/dist/{tunnel-W2GZBLEV.js.map → traefik-5GL3Q7DJ.js.map} +0 -0
- /package/dist/{workspace-manager-E434Z45T.js.map → tunnel-BKC7KLBX.js.map} +0 -0
|
@@ -1,298 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SETTINGS_FILE,
|
|
3
|
-
init_paths
|
|
4
|
-
} from "./chunk-ZTFNYOC7.js";
|
|
5
1
|
import {
|
|
6
2
|
__esm,
|
|
7
3
|
init_esm_shims
|
|
8
4
|
} from "./chunk-ZHC57RCV.js";
|
|
9
5
|
|
|
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.complexity) {
|
|
67
|
-
return "Missing complexity configuration";
|
|
68
|
-
}
|
|
69
|
-
const complexity = settings.models.complexity;
|
|
70
|
-
const requiredLevels = ["trivial", "simple", "medium", "complex", "expert"];
|
|
71
|
-
for (const level of requiredLevels) {
|
|
72
|
-
if (!complexity[level]) {
|
|
73
|
-
return `Missing complexity level: ${level}`;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (!settings.api_keys) {
|
|
77
|
-
return "Missing api_keys configuration";
|
|
78
|
-
}
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
function getDefaultSettings() {
|
|
82
|
-
return JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
|
|
83
|
-
}
|
|
84
|
-
function getAvailableModels(settings) {
|
|
85
|
-
const anthropicModels = [
|
|
86
|
-
"claude-opus-4-6",
|
|
87
|
-
"claude-sonnet-4-6",
|
|
88
|
-
"claude-haiku-4-5"
|
|
89
|
-
];
|
|
90
|
-
const openaiModels = settings.api_keys.openai ? ["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"] : [];
|
|
91
|
-
const googleModels = settings.api_keys.google ? ["gemini-3-pro-preview", "gemini-3-flash-preview"] : [];
|
|
92
|
-
const zaiModels = settings.api_keys.zai ? ["glm-4.7", "glm-4.7-flash"] : [];
|
|
93
|
-
const kimiModels = settings.api_keys.kimi ? ["kimi-k2", "kimi-k2.5"] : [];
|
|
94
|
-
return {
|
|
95
|
-
anthropic: anthropicModels,
|
|
96
|
-
openai: openaiModels,
|
|
97
|
-
google: googleModels,
|
|
98
|
-
zai: zaiModels,
|
|
99
|
-
kimi: kimiModels
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
function isAnthropicModel(modelId) {
|
|
103
|
-
return modelId.startsWith("claude-");
|
|
104
|
-
}
|
|
105
|
-
function getClaudeModelFlag(modelId) {
|
|
106
|
-
const modelMap = {
|
|
107
|
-
"claude-opus-4-6": "opus",
|
|
108
|
-
"claude-sonnet-4-6": "sonnet",
|
|
109
|
-
"claude-sonnet-4-5": "sonnet",
|
|
110
|
-
"claude-haiku-4-5": "haiku"
|
|
111
|
-
};
|
|
112
|
-
return modelMap[modelId] || "sonnet";
|
|
113
|
-
}
|
|
114
|
-
function getAgentCommand(modelId) {
|
|
115
|
-
if (isAnthropicModel(modelId)) {
|
|
116
|
-
return {
|
|
117
|
-
command: "claude",
|
|
118
|
-
args: ["--model", getClaudeModelFlag(modelId)]
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
command: "claude-code-router",
|
|
123
|
-
args: []
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
var DEFAULT_SETTINGS;
|
|
127
|
-
var init_settings = __esm({
|
|
128
|
-
"src/lib/settings.ts"() {
|
|
129
|
-
"use strict";
|
|
130
|
-
init_esm_shims();
|
|
131
|
-
init_paths();
|
|
132
|
-
DEFAULT_SETTINGS = {
|
|
133
|
-
models: {
|
|
134
|
-
specialists: {
|
|
135
|
-
review_agent: "claude-opus-4-6",
|
|
136
|
-
test_agent: "claude-sonnet-4-6",
|
|
137
|
-
merge_agent: "claude-sonnet-4-6"
|
|
138
|
-
},
|
|
139
|
-
status_review: "claude-opus-4-6",
|
|
140
|
-
complexity: {
|
|
141
|
-
trivial: "claude-haiku-4-5",
|
|
142
|
-
simple: "claude-haiku-4-5",
|
|
143
|
-
medium: "kimi-k2.5",
|
|
144
|
-
complex: "kimi-k2.5",
|
|
145
|
-
expert: "claude-opus-4-6"
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
api_keys: {}
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// src/lib/providers.ts
|
|
154
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
155
|
-
import { join } from "path";
|
|
156
|
-
function getProviderForModel(modelId) {
|
|
157
|
-
if (["claude-opus-4-6", "claude-sonnet-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"].includes(modelId)) {
|
|
158
|
-
return PROVIDERS.anthropic;
|
|
159
|
-
}
|
|
160
|
-
if (["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"].includes(modelId)) {
|
|
161
|
-
return PROVIDERS.openai;
|
|
162
|
-
}
|
|
163
|
-
if (["gemini-3-pro-preview", "gemini-3-flash-preview"].includes(modelId)) {
|
|
164
|
-
return PROVIDERS.google;
|
|
165
|
-
}
|
|
166
|
-
if (["glm-4.7", "glm-4.7-flash"].includes(modelId)) {
|
|
167
|
-
return PROVIDERS.zai;
|
|
168
|
-
}
|
|
169
|
-
if (["kimi-k2", "kimi-k2.5"].includes(modelId)) {
|
|
170
|
-
return PROVIDERS.kimi;
|
|
171
|
-
}
|
|
172
|
-
return PROVIDERS.anthropic;
|
|
173
|
-
}
|
|
174
|
-
function requiresRouter(provider) {
|
|
175
|
-
return PROVIDERS[provider].compatibility === "router";
|
|
176
|
-
}
|
|
177
|
-
function getRouterProviders() {
|
|
178
|
-
return Object.values(PROVIDERS).filter((p) => p.compatibility === "router");
|
|
179
|
-
}
|
|
180
|
-
function getDirectProviders() {
|
|
181
|
-
return Object.values(PROVIDERS).filter((p) => p.compatibility === "direct");
|
|
182
|
-
}
|
|
183
|
-
function needsRouter(apiKeys) {
|
|
184
|
-
return !!(apiKeys.openai || apiKeys.google);
|
|
185
|
-
}
|
|
186
|
-
function getProviderEnv(provider, apiKey) {
|
|
187
|
-
if (provider.compatibility === "direct") {
|
|
188
|
-
const env = {};
|
|
189
|
-
if (provider.baseUrl) {
|
|
190
|
-
env.ANTHROPIC_BASE_URL = provider.baseUrl;
|
|
191
|
-
}
|
|
192
|
-
if (provider.name !== "anthropic") {
|
|
193
|
-
if (provider.authType === "credential-file") {
|
|
194
|
-
env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
195
|
-
env.CLAUDE_CODE_API_KEY_HELPER_TTL_MS = "60000";
|
|
196
|
-
} else {
|
|
197
|
-
env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (provider.name === "zai") {
|
|
201
|
-
env.API_TIMEOUT_MS = "300000";
|
|
202
|
-
}
|
|
203
|
-
return env;
|
|
204
|
-
} else {
|
|
205
|
-
return {
|
|
206
|
-
ANTHROPIC_BASE_URL: "http://localhost:3456",
|
|
207
|
-
ANTHROPIC_AUTH_TOKEN: "router-managed"
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
function setupCredentialFileAuth(provider, workspacePath) {
|
|
212
|
-
if (provider.authType !== "credential-file" || !provider.credentialHelper) return;
|
|
213
|
-
const helperPath = provider.credentialHelper.replace("~", process.env.HOME || "");
|
|
214
|
-
const claudeDir = join(workspacePath, ".claude");
|
|
215
|
-
const settingsPath = join(claudeDir, "settings.local.json");
|
|
216
|
-
if (!existsSync2(claudeDir)) {
|
|
217
|
-
mkdirSync(claudeDir, { recursive: true });
|
|
218
|
-
}
|
|
219
|
-
let settings = {};
|
|
220
|
-
if (existsSync2(settingsPath)) {
|
|
221
|
-
try {
|
|
222
|
-
settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
|
|
223
|
-
} catch {
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
settings.apiKeyHelper = helperPath;
|
|
227
|
-
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
228
|
-
}
|
|
229
|
-
function clearCredentialFileAuth(workspacePath) {
|
|
230
|
-
const settingsPath = join(workspacePath, ".claude", "settings.local.json");
|
|
231
|
-
if (!existsSync2(settingsPath)) return;
|
|
232
|
-
try {
|
|
233
|
-
const settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
|
|
234
|
-
if (!settings.apiKeyHelper) return;
|
|
235
|
-
delete settings.apiKeyHelper;
|
|
236
|
-
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
237
|
-
} catch {
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
var PROVIDERS;
|
|
241
|
-
var init_providers = __esm({
|
|
242
|
-
"src/lib/providers.ts"() {
|
|
243
|
-
"use strict";
|
|
244
|
-
init_esm_shims();
|
|
245
|
-
PROVIDERS = {
|
|
246
|
-
anthropic: {
|
|
247
|
-
name: "anthropic",
|
|
248
|
-
displayName: "Anthropic",
|
|
249
|
-
compatibility: "direct",
|
|
250
|
-
models: ["claude-opus-4-6", "claude-sonnet-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"],
|
|
251
|
-
tested: true,
|
|
252
|
-
description: "Native Claude API"
|
|
253
|
-
},
|
|
254
|
-
kimi: {
|
|
255
|
-
name: "kimi",
|
|
256
|
-
displayName: "Kimi (Moonshot AI)",
|
|
257
|
-
compatibility: "direct",
|
|
258
|
-
baseUrl: "https://api.kimi.com/coding/",
|
|
259
|
-
authType: "credential-file",
|
|
260
|
-
credentialFile: "~/.kimi/credentials/kimi-code.json",
|
|
261
|
-
credentialHelper: "~/.panopticon/bin/kimi-token-helper.sh",
|
|
262
|
-
models: [],
|
|
263
|
-
// Kimi uses same model names as Anthropic
|
|
264
|
-
tested: true,
|
|
265
|
-
description: "Anthropic-compatible API via Kimi Code Plan (OAuth token refresh)"
|
|
266
|
-
},
|
|
267
|
-
zai: {
|
|
268
|
-
name: "zai",
|
|
269
|
-
displayName: "Z.AI (GLM)",
|
|
270
|
-
compatibility: "direct",
|
|
271
|
-
baseUrl: "https://api.z.ai/api/anthropic",
|
|
272
|
-
models: ["glm-4.7", "glm-4.7-flash"],
|
|
273
|
-
tested: true,
|
|
274
|
-
description: "Anthropic-compatible API, tested 2026-01-28"
|
|
275
|
-
},
|
|
276
|
-
openai: {
|
|
277
|
-
name: "openai",
|
|
278
|
-
displayName: "OpenAI",
|
|
279
|
-
compatibility: "router",
|
|
280
|
-
models: ["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"],
|
|
281
|
-
tested: false,
|
|
282
|
-
description: "Requires claude-code-router for API translation"
|
|
283
|
-
},
|
|
284
|
-
google: {
|
|
285
|
-
name: "google",
|
|
286
|
-
displayName: "Google (Gemini)",
|
|
287
|
-
compatibility: "router",
|
|
288
|
-
models: ["gemini-3-pro-preview", "gemini-3-flash-preview"],
|
|
289
|
-
tested: false,
|
|
290
|
-
description: "Requires claude-code-router for API translation"
|
|
291
|
-
}
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
|
|
296
6
|
// src/lib/model-capabilities.ts
|
|
297
7
|
function getModelCapability(model) {
|
|
298
8
|
return MODEL_CAPABILITIES[model];
|
|
@@ -714,8 +424,8 @@ var init_model_capabilities = __esm({
|
|
|
714
424
|
});
|
|
715
425
|
|
|
716
426
|
// src/lib/config-yaml.ts
|
|
717
|
-
import { readFileSync
|
|
718
|
-
import { join
|
|
427
|
+
import { readFileSync, existsSync, writeFileSync, copyFileSync } from "fs";
|
|
428
|
+
import { join } from "path";
|
|
719
429
|
import { homedir } from "os";
|
|
720
430
|
import yaml from "js-yaml";
|
|
721
431
|
function normalizeProviderConfig(providerConfig, fallbackKey) {
|
|
@@ -738,11 +448,11 @@ function resolveEnvVar(value) {
|
|
|
738
448
|
});
|
|
739
449
|
}
|
|
740
450
|
function loadYamlFile(filePath) {
|
|
741
|
-
if (!
|
|
451
|
+
if (!existsSync(filePath)) {
|
|
742
452
|
return null;
|
|
743
453
|
}
|
|
744
454
|
try {
|
|
745
|
-
const content =
|
|
455
|
+
const content = readFileSync(filePath, "utf-8");
|
|
746
456
|
const parsed = yaml.load(content);
|
|
747
457
|
return parsed || {};
|
|
748
458
|
} catch (error) {
|
|
@@ -753,10 +463,10 @@ function loadYamlFile(filePath) {
|
|
|
753
463
|
function findProjectRoot(startDir = process.cwd()) {
|
|
754
464
|
let currentDir = startDir;
|
|
755
465
|
while (currentDir !== "/") {
|
|
756
|
-
if (
|
|
466
|
+
if (existsSync(join(currentDir, ".git"))) {
|
|
757
467
|
return currentDir;
|
|
758
468
|
}
|
|
759
|
-
currentDir =
|
|
469
|
+
currentDir = join(currentDir, "..");
|
|
760
470
|
}
|
|
761
471
|
return null;
|
|
762
472
|
}
|
|
@@ -765,7 +475,7 @@ function loadProjectConfig() {
|
|
|
765
475
|
if (!projectRoot) {
|
|
766
476
|
return null;
|
|
767
477
|
}
|
|
768
|
-
const projectConfigPath =
|
|
478
|
+
const projectConfigPath = join(projectRoot, ".panopticon.yaml");
|
|
769
479
|
return loadYamlFile(projectConfigPath);
|
|
770
480
|
}
|
|
771
481
|
function loadGlobalConfig() {
|
|
@@ -924,7 +634,7 @@ function writeGlobalConfig(config) {
|
|
|
924
634
|
lineWidth: 100,
|
|
925
635
|
noRefs: true
|
|
926
636
|
});
|
|
927
|
-
|
|
637
|
+
writeFileSync(GLOBAL_CONFIG_PATH, yamlContent, "utf-8");
|
|
928
638
|
}
|
|
929
639
|
function loadConfig() {
|
|
930
640
|
let globalConfig = loadGlobalConfig();
|
|
@@ -979,13 +689,25 @@ function loadConfig() {
|
|
|
979
689
|
}
|
|
980
690
|
return { config, migration: migrationResult };
|
|
981
691
|
}
|
|
692
|
+
function hasProjectConfig() {
|
|
693
|
+
const projectRoot = findProjectRoot();
|
|
694
|
+
if (!projectRoot) return false;
|
|
695
|
+
return existsSync(join(projectRoot, ".panopticon.yaml"));
|
|
696
|
+
}
|
|
982
697
|
function hasGlobalConfig() {
|
|
983
|
-
return
|
|
698
|
+
return existsSync(GLOBAL_CONFIG_PATH);
|
|
699
|
+
}
|
|
700
|
+
function getGlobalConfigPath() {
|
|
701
|
+
return GLOBAL_CONFIG_PATH;
|
|
702
|
+
}
|
|
703
|
+
function getProjectConfigPath() {
|
|
704
|
+
const projectRoot = findProjectRoot();
|
|
705
|
+
if (!projectRoot) return null;
|
|
706
|
+
return join(projectRoot, ".panopticon.yaml");
|
|
984
707
|
}
|
|
985
708
|
var DEFAULT_CONFIG, GLOBAL_CONFIG_PATH;
|
|
986
709
|
var init_config_yaml = __esm({
|
|
987
710
|
"src/lib/config-yaml.ts"() {
|
|
988
|
-
"use strict";
|
|
989
711
|
init_esm_shims();
|
|
990
712
|
init_model_capabilities();
|
|
991
713
|
DEFAULT_CONFIG = {
|
|
@@ -1005,34 +727,19 @@ var init_config_yaml = __esm({
|
|
|
1005
727
|
}
|
|
1006
728
|
}
|
|
1007
729
|
};
|
|
1008
|
-
GLOBAL_CONFIG_PATH =
|
|
730
|
+
GLOBAL_CONFIG_PATH = join(homedir(), ".panopticon", "config.yaml");
|
|
1009
731
|
}
|
|
1010
732
|
});
|
|
1011
733
|
|
|
1012
734
|
export {
|
|
1013
|
-
loadSettings,
|
|
1014
|
-
saveSettings,
|
|
1015
|
-
validateSettings,
|
|
1016
|
-
getDefaultSettings,
|
|
1017
|
-
getAvailableModels,
|
|
1018
|
-
isAnthropicModel,
|
|
1019
|
-
getClaudeModelFlag,
|
|
1020
|
-
getAgentCommand,
|
|
1021
|
-
init_settings,
|
|
1022
735
|
MODEL_CAPABILITIES,
|
|
1023
736
|
getModelCapability,
|
|
1024
737
|
init_model_capabilities,
|
|
1025
738
|
loadConfig,
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
getDirectProviders,
|
|
1032
|
-
needsRouter,
|
|
1033
|
-
getProviderEnv,
|
|
1034
|
-
setupCredentialFileAuth,
|
|
1035
|
-
clearCredentialFileAuth,
|
|
1036
|
-
init_providers
|
|
739
|
+
hasProjectConfig,
|
|
740
|
+
hasGlobalConfig,
|
|
741
|
+
getGlobalConfigPath,
|
|
742
|
+
getProjectConfigPath,
|
|
743
|
+
init_config_yaml
|
|
1037
744
|
};
|
|
1038
|
-
//# sourceMappingURL=chunk-
|
|
745
|
+
//# sourceMappingURL=chunk-ZP6EWSZV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/model-capabilities.ts","../src/lib/config-yaml.ts"],"sourcesContent":["/**\n * Model Capability Matrix\n *\n * Defines capability scores for each model across different skill dimensions.\n * This enables intelligent model selection based on what the user has enabled\n * rather than static presets.\n *\n * Scores: 0-100 where 100 = best in class\n * Cost: $/1M tokens (input + output average)\n *\n * Last updated: 2026-01-29\n * Sources:\n * - SWE-bench Verified leaderboard (vals.ai)\n * - LiveCodeBench v6\n * - LMSYS Chatbot Arena\n * - Artificial Analysis\n * - Official provider pricing pages\n */\n\nimport { ModelId } from './settings.js';\n\n/**\n * Model ID deprecation mapping\n *\n * Maps deprecated model IDs to their current replacements.\n * When a model ID changes (e.g., claude-opus-4-5 → claude-opus-4-6),\n * add the mapping here to enable automatic migration.\n *\n * Strategy: Single-hop only. When a newer version arrives (e.g., 4-7),\n * add both old→new mappings (4-5→4-7 and 4-6→4-7).\n */\nexport const MODEL_DEPRECATIONS: Record<string, ModelId> = {\n 'claude-opus-4-5': 'claude-opus-4-6',\n 'claude-sonnet-4-5': 'claude-sonnet-4-6',\n};\n\n/**\n * Resolve a model ID to its current version\n *\n * If the model ID is deprecated, returns the replacement.\n * Otherwise, returns the model ID unchanged.\n *\n * @param modelId - Model ID to resolve (may be deprecated)\n * @returns Current model ID\n */\nexport function resolveModelId(modelId: string): ModelId {\n return (MODEL_DEPRECATIONS[modelId] as ModelId) || (modelId as ModelId);\n}\n\n/**\n * Skill dimensions that models are evaluated on\n */\nexport type SkillDimension =\n | 'code-generation' // Writing new code\n | 'code-review' // Finding issues in code\n | 'debugging' // Root cause analysis\n | 'planning' // Architecture and strategy\n | 'documentation' // Writing docs, PRDs\n | 'testing' // Test generation and analysis\n | 'security' // Security analysis\n | 'performance' // Performance optimization\n | 'synthesis' // Combining information\n | 'speed' // Response latency\n | 'context-length'; // Max context window\n\n/**\n * Capability profile for a single model\n */\nexport interface ModelCapability {\n /** Model identifier */\n model: ModelId;\n /** Provider for this model */\n provider: 'anthropic' | 'openai' | 'google' | 'zai' | 'kimi';\n /** Display name */\n displayName: string;\n /** Cost per 1M tokens (average of input/output) in USD */\n costPer1MTokens: number;\n /** Capability scores (0-100) for each skill dimension */\n skills: Record<SkillDimension, number>;\n /** Context window size in tokens */\n contextWindow: number;\n /** Additional notes about this model's strengths */\n notes?: string;\n}\n\n/**\n * Master capability database\n *\n * Scores are based on:\n * - Public benchmarks (HumanEval, SWE-bench, MBPP)\n * - Community consensus\n * - Practical experience\n *\n * These are baseline scores - run Kimi 2.5 research to refine.\n */\nexport const MODEL_CAPABILITIES: Record<ModelId, ModelCapability> = {\n // ═══════════════════════════════════════════════════════════════════════════\n // ANTHROPIC MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'claude-opus-4-6': {\n model: 'claude-opus-4-6',\n provider: 'anthropic',\n displayName: 'Claude Opus 4.6',\n costPer1MTokens: 45.0, // $5 in / $25 out → same pricing as 4.5\n contextWindow: 200000, // 1M available via opt-in beta, but we use 200K\n skills: {\n 'code-generation': 96, // 80.9% SWE-bench (first >80%), 89.4% Aider Polyglot\n 'code-review': 98,\n debugging: 97,\n planning: 99, // User confirms: \"Opus 4.6 planning for sure\"\n documentation: 95,\n testing: 92,\n security: 98, // Best for security review\n performance: 90,\n synthesis: 98, // Best for combining info across domains\n speed: 40, // Slower but 76% more token efficient\n 'context-length': 95,\n },\n notes: 'Successor to Opus 4.5. Same pricing, 1M context available (opt-in beta). Best for planning, security, complex reasoning.',\n },\n\n 'claude-sonnet-4-6': {\n model: 'claude-sonnet-4-6',\n provider: 'anthropic',\n displayName: 'Claude Sonnet 4.6',\n costPer1MTokens: 9.0, // $3 in / $15 out → avg ~$9\n contextWindow: 200000,\n skills: {\n 'code-generation': 94,\n 'code-review': 94,\n debugging: 92,\n planning: 90,\n documentation: 92,\n testing: 92,\n security: 88,\n performance: 88,\n synthesis: 90,\n speed: 70,\n 'context-length': 95,\n },\n notes: 'Successor to Sonnet 4.5. Same pricing tier. Improved coding and reasoning.',\n },\n\n 'claude-sonnet-4-5': {\n model: 'claude-sonnet-4-5',\n provider: 'anthropic',\n displayName: 'Claude Sonnet 4.5',\n costPer1MTokens: 9.0, // $3 in / $15 out → avg ~$9\n contextWindow: 200000,\n skills: {\n 'code-generation': 92, // 77.2% SWE-bench (82% parallel), beats GPT-5 Codex (74.5%)\n 'code-review': 92,\n debugging: 90,\n planning: 88,\n documentation: 90, // 100% AIME with Python\n testing: 90, // 50% Terminal-Bench, 61.4% OSWorld\n security: 85,\n performance: 85,\n synthesis: 88,\n speed: 70,\n 'context-length': 95,\n },\n notes: 'Best value: 77.2% SWE-bench at 1/5th Opus cost. Beats GPT-5 Codex.',\n },\n\n 'claude-haiku-4-5': {\n model: 'claude-haiku-4-5',\n provider: 'anthropic',\n displayName: 'Claude Haiku 4.5',\n costPer1MTokens: 4.0, // $0.80 in / $4 out → avg ~$2.4\n contextWindow: 200000,\n skills: {\n 'code-generation': 75,\n 'code-review': 72,\n debugging: 70,\n planning: 65,\n documentation: 75,\n testing: 70,\n security: 60,\n performance: 65,\n synthesis: 68,\n speed: 95, // Fastest Anthropic\n 'context-length': 95,\n },\n notes: 'Fast and cheap, good for simple tasks and exploration',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // OPENAI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'gpt-5.2-codex': {\n model: 'gpt-5.2-codex',\n provider: 'openai',\n displayName: 'GPT-5.2 Codex',\n costPer1MTokens: 75.0, // Premium tier ~$75/M\n contextWindow: 128000,\n skills: {\n 'code-generation': 95, // 80% SWE-bench Verified, 55.6% SWE-bench Pro\n 'code-review': 90,\n debugging: 92, // 92.4% GPQA Diamond\n planning: 88,\n documentation: 85,\n testing: 90,\n security: 85,\n performance: 88, // 52.9% ARC-AGI-2 (best reasoning)\n synthesis: 88, // 100% AIME 2025 without tools\n speed: 55,\n 'context-length': 75,\n },\n notes: 'Premium coding: 80% SWE-bench. Best raw reasoning (52.9% ARC-AGI-2). Expensive.',\n },\n\n 'o3-deep-research': {\n model: 'o3-deep-research',\n provider: 'openai',\n displayName: 'O3 Deep Research',\n costPer1MTokens: 100.0, // Expensive reasoning model\n contextWindow: 200000,\n skills: {\n 'code-generation': 85,\n 'code-review': 95,\n debugging: 98, // Best for debugging\n planning: 95,\n documentation: 88,\n testing: 85,\n security: 92,\n performance: 92,\n synthesis: 95,\n speed: 20, // Very slow (reasoning chains)\n 'context-length': 95,\n },\n notes: 'Deep reasoning model, excellent for complex debugging and analysis',\n },\n\n 'gpt-4o': {\n model: 'gpt-4o',\n provider: 'openai',\n displayName: 'GPT-4o',\n costPer1MTokens: 15.0, // $5 in / $15 out\n contextWindow: 128000,\n skills: {\n 'code-generation': 88,\n 'code-review': 85,\n debugging: 85,\n planning: 82,\n documentation: 88,\n testing: 82,\n security: 78,\n performance: 80,\n synthesis: 85,\n speed: 75,\n 'context-length': 75,\n },\n notes: 'Good all-rounder, competitive with Sonnet',\n },\n\n 'gpt-4o-mini': {\n model: 'gpt-4o-mini',\n provider: 'openai',\n displayName: 'GPT-4o Mini',\n costPer1MTokens: 1.0, // Very cheap\n contextWindow: 128000,\n skills: {\n 'code-generation': 72,\n 'code-review': 68,\n debugging: 65,\n planning: 60,\n documentation: 70,\n testing: 65,\n security: 55,\n performance: 60,\n synthesis: 62,\n speed: 92,\n 'context-length': 75,\n },\n notes: 'Budget option, good for simple tasks',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // GOOGLE MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'gemini-3-pro-preview': {\n model: 'gemini-3-pro-preview',\n provider: 'google',\n displayName: 'Gemini 3 Pro',\n costPer1MTokens: 12.0, // $4.2 in / $18.9 out\n contextWindow: 1000000, // 1M context!\n skills: {\n 'code-generation': 90, // 2439 Elo LiveCodeBench Pro (first >1500 on LMArena)\n 'code-review': 88,\n debugging: 85,\n planning: 85,\n documentation: 88,\n testing: 85, // ~95% AIME 2025\n security: 78,\n performance: 85, // Strong multimodal\n synthesis: 90, // Best for combining large codebases\n speed: 80,\n 'context-length': 100, // Best context - 1M tokens\n },\n notes: 'First to exceed 1500 Elo on LMArena. Best for large codebase analysis with 1M context.',\n },\n\n 'gemini-3-flash-preview': {\n model: 'gemini-3-flash-preview',\n provider: 'google',\n displayName: 'Gemini 3 Flash',\n costPer1MTokens: 0.5, // Very cheap\n contextWindow: 1000000,\n skills: {\n 'code-generation': 75,\n 'code-review': 70,\n debugging: 68,\n planning: 62,\n documentation: 72,\n testing: 68,\n security: 55,\n performance: 65,\n synthesis: 70,\n speed: 98, // Fastest overall\n 'context-length': 100,\n },\n notes: 'Extremely fast and cheap, huge context, great for exploration',\n },\n\n 'gemini-2.5-pro': {\n model: 'gemini-2.5-pro',\n provider: 'google',\n displayName: 'Gemini 2.5 Pro',\n costPer1MTokens: 12.0,\n contextWindow: 1000000,\n skills: {\n 'code-generation': 92,\n 'code-review': 90,\n debugging: 88,\n planning: 88,\n documentation: 90,\n testing: 87,\n security: 82,\n performance: 88,\n synthesis: 92,\n speed: 75,\n 'context-length': 100,\n },\n notes: 'Advanced reasoning and code capabilities with 1M context',\n },\n\n 'gemini-2.5-flash': {\n model: 'gemini-2.5-flash',\n provider: 'google',\n displayName: 'Gemini 2.5 Flash',\n costPer1MTokens: 0.6,\n contextWindow: 1000000,\n skills: {\n 'code-generation': 78,\n 'code-review': 73,\n debugging: 70,\n planning: 65,\n documentation: 75,\n testing: 70,\n security: 58,\n performance: 68,\n synthesis: 73,\n speed: 95,\n 'context-length': 100,\n },\n notes: 'Fast and efficient with large context support',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Z.AI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'glm-4.7': {\n model: 'glm-4.7',\n provider: 'zai',\n displayName: 'GLM 4.7',\n costPer1MTokens: 5.0,\n contextWindow: 200000, // 200K context, 128K output\n skills: {\n 'code-generation': 88, // 73.8% SWE-bench, 84.9 LiveCodeBench v6 (open-source SOTA)\n 'code-review': 85,\n debugging: 85, // Strong debugging with Interleaved Thinking\n planning: 82, // 95.7% AIME 2025 (beats Gemini 3 & GPT-5.1)\n documentation: 80,\n testing: 82, // 87.4 τ²-Bench (SOTA for tool use)\n security: 72,\n performance: 78,\n synthesis: 85, // Preserved Thinking retains context across turns\n speed: 80,\n 'context-length': 95, // 200K context\n },\n notes: 'Top open-source for agentic coding. 73.8% SWE-bench, best tool use. 400B params with Interleaved Thinking.',\n },\n\n 'glm-4.7-flash': {\n model: 'glm-4.7-flash',\n provider: 'zai',\n displayName: 'GLM 4.7 Flash',\n costPer1MTokens: 1.5,\n contextWindow: 128000,\n skills: {\n 'code-generation': 72,\n 'code-review': 68,\n debugging: 65,\n planning: 62,\n documentation: 70,\n testing: 65,\n security: 55,\n performance: 62,\n synthesis: 65,\n speed: 92, // Fast inference\n 'context-length': 75,\n },\n notes: 'Fast and affordable. Good for quick iterations and exploration.',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // KIMI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'kimi-k2': {\n model: 'kimi-k2',\n provider: 'kimi',\n displayName: 'Kimi K2',\n costPer1MTokens: 1.4, // $0.16 in / $2.63 out → very cheap\n contextWindow: 131000,\n skills: {\n 'code-generation': 82, // 65.8% SWE-bench (beats GPT-4.1 at 54.6%)\n 'code-review': 80,\n debugging: 78,\n planning: 75,\n documentation: 80,\n testing: 75,\n security: 70,\n performance: 72,\n synthesis: 78,\n speed: 80,\n 'context-length': 75,\n },\n notes: 'Strong value: 65.8% SWE-bench at very low cost. Good for routine tasks.',\n },\n\n 'kimi-k2.5': {\n model: 'kimi-k2.5',\n provider: 'kimi',\n displayName: 'Kimi K2.5',\n costPer1MTokens: 8.0, // ~5.1x cheaper than GPT-5.2\n contextWindow: 256000,\n skills: {\n 'code-generation': 92, // 76.8% SWE-bench, 85 LiveCodeBench v6\n 'code-review': 90,\n debugging: 90, // Strong analytical capabilities\n planning: 88, // User confirms \"highly capable\"\n documentation: 88,\n testing: 88, // 92% coding accuracy\n security: 82,\n performance: 85,\n synthesis: 92, // Can coordinate 100 sub-agents, 1500 tool calls\n speed: 75, // MoE: 1T total params, 32B active\n 'context-length': 98, // 256K context\n },\n notes: 'Best open-source coding model. 5x cheaper than GPT-5.2. Excellent for frontend dev and multi-agent orchestration.',\n },\n};\n\n/**\n * Get capability profile for a model\n */\nexport function getModelCapability(model: ModelId): ModelCapability {\n return MODEL_CAPABILITIES[model];\n}\n\n/**\n * Get all models sorted by a specific skill (descending)\n */\nexport function getModelsBySkill(skill: SkillDimension): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).sort(\n (a, b) => MODEL_CAPABILITIES[b].skills[skill] - MODEL_CAPABILITIES[a].skills[skill]\n );\n}\n\n/**\n * Get all models for a provider\n */\nexport function getModelsForProvider(\n provider: ModelCapability['provider']\n): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).filter(\n (model) => MODEL_CAPABILITIES[model].provider === provider\n );\n}\n\n/**\n * Get cheapest models (sorted by cost ascending)\n */\nexport function getCheapestModels(): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).sort(\n (a, b) => MODEL_CAPABILITIES[a].costPer1MTokens - MODEL_CAPABILITIES[b].costPer1MTokens\n );\n}\n\n/**\n * Calculate cost efficiency score for a skill\n * Higher = better value (skill score / cost)\n */\nexport function getValueScore(model: ModelId, skill: SkillDimension): number {\n const cap = MODEL_CAPABILITIES[model];\n return cap.skills[skill] / Math.log10(cap.costPer1MTokens + 1);\n}\n\n/**\n * Get all skill dimensions\n */\nexport function getAllSkillDimensions(): SkillDimension[] {\n return [\n 'code-generation',\n 'code-review',\n 'debugging',\n 'planning',\n 'documentation',\n 'testing',\n 'security',\n 'performance',\n 'synthesis',\n 'speed',\n 'context-length',\n ];\n}\n","/**\n * YAML Configuration Loader\n *\n * Loads and merges configuration from:\n * 1. Global config: ~/.panopticon/config.yaml\n * 2. Per-project config: .panopticon.yaml (project root)\n *\n * Uses smart (capability-based) model selection - no legacy presets.\n */\n\nimport { readFileSync, existsSync, writeFileSync, copyFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\nimport { WorkTypeId } from './work-types.js';\nimport { ModelId } from './settings.js';\nimport { ModelProvider } from './model-fallback.js';\nimport { MODEL_DEPRECATIONS, resolveModelId } from './model-capabilities.js';\n\n/**\n * Provider configuration (enable/disable + API keys)\n */\nexport interface ProviderConfig {\n /** Whether this provider is enabled */\n enabled: boolean;\n /** API key (optional, can use env var) */\n api_key?: string;\n}\n\n/**\n * Shadow mode configuration\n */\nexport interface ShadowConfig {\n /** Global shadow mode default */\n enabled?: boolean;\n\n /** Per-tracker overrides */\n trackers?: {\n linear?: boolean;\n github?: boolean;\n gitlab?: boolean;\n rally?: boolean;\n };\n}\n\n/**\n * Complete configuration structure (YAML schema)\n */\nexport interface YamlConfig {\n /** Model configuration */\n models?: {\n /** Provider enable/disable and API keys */\n providers?: {\n anthropic?: ProviderConfig | boolean;\n openai?: ProviderConfig | boolean;\n google?: ProviderConfig | boolean;\n zai?: ProviderConfig | boolean;\n kimi?: ProviderConfig | boolean;\n };\n\n /** Per-work-type overrides (explicit model for specific tasks) */\n overrides?: Partial<Record<WorkTypeId, ModelId>>;\n\n /** Gemini thinking level (1-4) */\n gemini_thinking_level?: 1 | 2 | 3 | 4;\n };\n\n /** Legacy API keys (for backward compatibility) */\n api_keys?: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n };\n\n /** Tracker API keys (override environment variables) */\n tracker_keys?: {\n linear?: string;\n github?: string;\n gitlab?: string;\n rally?: string;\n };\n\n /** Shadow mode configuration */\n shadow?: ShadowConfig;\n}\n\n/**\n * Normalized shadow configuration\n */\nexport interface NormalizedShadowConfig {\n /** Global shadow mode enabled */\n enabled: boolean;\n\n /** Per-tracker overrides */\n trackers: {\n linear: boolean;\n github: boolean;\n gitlab: boolean;\n rally: boolean;\n };\n}\n\n/**\n * Normalized configuration (after loading and merging)\n */\nexport interface NormalizedConfig {\n /** Enabled providers */\n enabledProviders: Set<ModelProvider>;\n\n /** API keys by provider */\n apiKeys: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n };\n\n /** Per-work-type overrides */\n overrides: Partial<Record<WorkTypeId, ModelId>>;\n\n /** Gemini thinking level */\n geminiThinkingLevel: 1 | 2 | 3 | 4;\n\n /** Tracker API keys */\n trackerKeys: {\n linear?: string;\n github?: string;\n gitlab?: string;\n rally?: string;\n };\n\n /** Shadow mode configuration */\n shadow: NormalizedShadowConfig;\n}\n\n/**\n * Model ID migration result\n *\n * Returned when deprecated model IDs are automatically migrated\n * during config load.\n */\nexport interface MigrationResult {\n /** List of migrated model IDs */\n migrated: Array<{\n /** Work type that was migrated */\n workType: WorkTypeId;\n /** Old (deprecated) model ID */\n from: string;\n /** New (current) model ID */\n to: string;\n }>;\n /** Whether config.yaml was backed up before migration */\n backedUp: boolean;\n}\n\n/**\n * Config load result (config + optional migration info)\n */\nexport interface ConfigLoadResult {\n /** Normalized configuration */\n config: NormalizedConfig;\n /** Migration result (if any deprecated models were migrated) */\n migration?: MigrationResult;\n}\n\n/**\n * Default configuration (used when no config files exist)\n */\nconst DEFAULT_CONFIG: NormalizedConfig = {\n enabledProviders: new Set(['anthropic']), // Only Anthropic by default\n apiKeys: {},\n overrides: {},\n geminiThinkingLevel: 3,\n trackerKeys: {},\n shadow: {\n enabled: false,\n trackers: {\n linear: false,\n github: false,\n gitlab: false,\n rally: false,\n },\n },\n};\n\n/**\n * Path to global config file\n */\nconst GLOBAL_CONFIG_PATH = join(homedir(), '.panopticon', 'config.yaml');\n\n/**\n * Normalize a provider config (handle both boolean and object forms)\n */\nfunction normalizeProviderConfig(\n providerConfig: ProviderConfig | boolean | undefined,\n fallbackKey?: string\n): { enabled: boolean; api_key?: string } {\n if (providerConfig === undefined) {\n return { enabled: false };\n }\n\n if (typeof providerConfig === 'boolean') {\n return { enabled: providerConfig, api_key: fallbackKey };\n }\n\n return {\n enabled: providerConfig.enabled,\n api_key: providerConfig.api_key || fallbackKey,\n };\n}\n\n/**\n * Resolve environment variables in config values.\n * If the env var is not set, returns the original reference (e.g., \"$OPENAI_API_KEY\")\n * so the UI can show that it's configured via env var but not resolved.\n */\nfunction resolveEnvVar(value: string | undefined): string | undefined {\n if (!value) return undefined;\n\n // Replace $VAR_NAME or ${VAR_NAME} with environment variable\n // If env var is not set, keep the original reference\n return value.replace(/\\$\\{?([A-Z_][A-Z0-9_]*)\\}?/g, (match, varName) => {\n const envValue = process.env[varName];\n return envValue !== undefined ? envValue : match; // Keep $VAR_NAME if not set\n });\n}\n\n/**\n * Load and parse a YAML config file\n */\nfunction loadYamlFile(filePath: string): YamlConfig | null {\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const parsed = yaml.load(content) as YamlConfig;\n return parsed || {};\n } catch (error) {\n console.error(`Error loading YAML config from ${filePath}:`, error);\n return null;\n }\n}\n\n/**\n * Find project root by looking for .git directory\n */\nfunction findProjectRoot(startDir: string = process.cwd()): string | null {\n let currentDir = startDir;\n\n while (currentDir !== '/') {\n if (existsSync(join(currentDir, '.git'))) {\n return currentDir;\n }\n currentDir = join(currentDir, '..');\n }\n\n return null;\n}\n\n/**\n * Load per-project config (.panopticon.yaml in project root)\n */\nfunction loadProjectConfig(): YamlConfig | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) {\n return null;\n }\n\n const projectConfigPath = join(projectRoot, '.panopticon.yaml');\n return loadYamlFile(projectConfigPath);\n}\n\n/**\n * Load global config (~/.panopticon/config.yaml)\n */\nfunction loadGlobalConfig(): YamlConfig | null {\n return loadYamlFile(GLOBAL_CONFIG_PATH);\n}\n\n/**\n * Merge shadow configuration from multiple sources\n */\nfunction mergeShadowConfig(\n result: NormalizedShadowConfig,\n config: YamlConfig | null\n): void {\n if (!config?.shadow) return;\n\n // Merge global enabled flag\n if (config.shadow.enabled !== undefined) {\n result.enabled = config.shadow.enabled;\n }\n\n // Merge per-tracker overrides\n if (config.shadow.trackers) {\n if (config.shadow.trackers.linear !== undefined) {\n result.trackers.linear = config.shadow.trackers.linear;\n }\n if (config.shadow.trackers.github !== undefined) {\n result.trackers.github = config.shadow.trackers.github;\n }\n if (config.shadow.trackers.gitlab !== undefined) {\n result.trackers.gitlab = config.shadow.trackers.gitlab;\n }\n if (config.shadow.trackers.rally !== undefined) {\n result.trackers.rally = config.shadow.trackers.rally;\n }\n }\n}\n\n/**\n * Merge multiple configs with precedence: project > global > defaults\n */\nfunction mergeConfigs(...configs: (YamlConfig | null)[]): NormalizedConfig {\n const result: NormalizedConfig = {\n ...DEFAULT_CONFIG,\n enabledProviders: new Set(DEFAULT_CONFIG.enabledProviders),\n shadow: {\n enabled: DEFAULT_CONFIG.shadow.enabled,\n trackers: { ...DEFAULT_CONFIG.shadow.trackers },\n },\n };\n\n // Filter out null configs\n const validConfigs = configs.filter((c): c is YamlConfig => c !== null);\n\n // Merge in reverse order (lowest precedence first)\n for (const config of validConfigs.reverse()) {\n // Merge providers\n if (config.models?.providers) {\n const providers = config.models.providers;\n const legacyKeys = config.api_keys || {};\n\n // Anthropic (always enabled)\n result.enabledProviders.add('anthropic');\n\n // OpenAI\n const openai = normalizeProviderConfig(providers.openai, legacyKeys.openai);\n if (openai.enabled) {\n result.enabledProviders.add('openai');\n if (openai.api_key) {\n result.apiKeys.openai = resolveEnvVar(openai.api_key);\n }\n }\n\n // Google\n const google = normalizeProviderConfig(providers.google, legacyKeys.google);\n if (google.enabled) {\n result.enabledProviders.add('google');\n if (google.api_key) {\n result.apiKeys.google = resolveEnvVar(google.api_key);\n }\n }\n\n // Z.AI\n const zai = normalizeProviderConfig(providers.zai, legacyKeys.zai);\n if (zai.enabled) {\n result.enabledProviders.add('zai');\n if (zai.api_key) {\n result.apiKeys.zai = resolveEnvVar(zai.api_key);\n }\n }\n\n // Kimi\n const kimi = normalizeProviderConfig(providers.kimi, legacyKeys.kimi);\n if (kimi.enabled) {\n result.enabledProviders.add('kimi');\n if (kimi.api_key) {\n result.apiKeys.kimi = resolveEnvVar(kimi.api_key);\n }\n }\n }\n\n // Merge legacy API keys (for backward compatibility)\n if (config.api_keys) {\n if (config.api_keys.openai) {\n result.apiKeys.openai = resolveEnvVar(config.api_keys.openai);\n result.enabledProviders.add('openai');\n }\n if (config.api_keys.google) {\n result.apiKeys.google = resolveEnvVar(config.api_keys.google);\n result.enabledProviders.add('google');\n }\n if (config.api_keys.zai) {\n result.apiKeys.zai = resolveEnvVar(config.api_keys.zai);\n result.enabledProviders.add('zai');\n }\n if (config.api_keys.kimi) {\n result.apiKeys.kimi = resolveEnvVar(config.api_keys.kimi);\n result.enabledProviders.add('kimi');\n }\n }\n\n // Merge overrides\n if (config.models?.overrides) {\n result.overrides = {\n ...result.overrides,\n ...config.models.overrides,\n };\n }\n\n // Merge Gemini thinking level\n if (config.models?.gemini_thinking_level) {\n result.geminiThinkingLevel = config.models.gemini_thinking_level;\n }\n\n // Merge tracker keys\n if (config.tracker_keys) {\n if (config.tracker_keys.linear) {\n result.trackerKeys.linear = resolveEnvVar(config.tracker_keys.linear);\n }\n if (config.tracker_keys.github) {\n result.trackerKeys.github = resolveEnvVar(config.tracker_keys.github);\n }\n if (config.tracker_keys.gitlab) {\n result.trackerKeys.gitlab = resolveEnvVar(config.tracker_keys.gitlab);\n }\n if (config.tracker_keys.rally) {\n result.trackerKeys.rally = resolveEnvVar(config.tracker_keys.rally);\n }\n }\n\n // Merge shadow configuration\n mergeShadowConfig(result.shadow, config);\n }\n\n return result;\n}\n\n/**\n * Detect deprecated model IDs in config overrides\n *\n * Returns array of migrations to perform, or empty array if none found.\n */\nfunction detectDeprecatedModels(config: YamlConfig | null): Array<{\n workType: WorkTypeId;\n from: string;\n to: string;\n}> {\n if (!config?.models?.overrides) {\n return [];\n }\n\n const migrations: Array<{ workType: WorkTypeId; from: string; to: string }> = [];\n\n for (const [workType, modelId] of Object.entries(config.models.overrides)) {\n if (modelId && MODEL_DEPRECATIONS[modelId]) {\n migrations.push({\n workType: workType as WorkTypeId,\n from: modelId,\n to: MODEL_DEPRECATIONS[modelId],\n });\n }\n }\n\n return migrations;\n}\n\n/**\n * Apply deprecation migrations to a YamlConfig (in-place)\n */\nfunction applyMigrations(\n config: YamlConfig,\n migrations: Array<{ workType: WorkTypeId; from: string; to: string }>\n): void {\n if (!config.models) {\n config.models = {};\n }\n if (!config.models.overrides) {\n config.models.overrides = {};\n }\n\n for (const { workType, to } of migrations) {\n config.models.overrides[workType] = to as ModelId;\n }\n}\n\n/**\n * Create backup of global config file\n */\nfunction backupGlobalConfig(): boolean {\n try {\n const backupPath = `${GLOBAL_CONFIG_PATH}.bak`;\n copyFileSync(GLOBAL_CONFIG_PATH, backupPath);\n console.log(`✓ Backed up config.yaml → config.yaml.bak`);\n return true;\n } catch (error) {\n console.error(`Failed to create config backup:`, error);\n return false;\n }\n}\n\n/**\n * Write YamlConfig back to global config file\n */\nfunction writeGlobalConfig(config: YamlConfig): void {\n const yamlContent = yaml.dump(config, {\n indent: 2,\n lineWidth: 100,\n noRefs: true,\n });\n\n writeFileSync(GLOBAL_CONFIG_PATH, yamlContent, 'utf-8');\n}\n\n/**\n * Load complete configuration (global + project + defaults)\n * Also loads API keys from environment variables as fallback\n *\n * IMPORTANT: This function may modify config.yaml if deprecated model IDs\n * are detected. A backup is created before any modifications.\n */\nexport function loadConfig(): ConfigLoadResult {\n let globalConfig = loadGlobalConfig();\n const projectConfig = loadProjectConfig();\n\n // Check for deprecated models in global config\n let migrationResult: MigrationResult | undefined;\n if (globalConfig && hasGlobalConfig()) {\n const migrations = detectDeprecatedModels(globalConfig);\n\n if (migrations.length > 0) {\n // Create backup\n const backedUp = backupGlobalConfig();\n\n // Apply migrations to global config\n applyMigrations(globalConfig, migrations);\n\n // Write migrated config back to disk\n writeGlobalConfig(globalConfig);\n\n // Log migrations\n console.log('\\n🔄 Model ID Migration:');\n for (const { workType, from, to } of migrations) {\n console.log(` ${workType}: ${from} → ${to}`);\n }\n console.log('');\n\n migrationResult = { migrated: migrations, backedUp };\n }\n }\n\n const config = mergeConfigs(projectConfig, globalConfig);\n\n // Load API keys from environment variables as fallback\n // This allows using ~/.panopticon.env for API keys\n if (process.env.OPENAI_API_KEY && !config.apiKeys.openai) {\n config.apiKeys.openai = process.env.OPENAI_API_KEY;\n config.enabledProviders.add('openai');\n }\n if (process.env.GOOGLE_API_KEY && !config.apiKeys.google) {\n config.apiKeys.google = process.env.GOOGLE_API_KEY;\n config.enabledProviders.add('google');\n }\n if (process.env.ZAI_API_KEY && !config.apiKeys.zai) {\n config.apiKeys.zai = process.env.ZAI_API_KEY;\n config.enabledProviders.add('zai');\n }\n if (process.env.KIMI_API_KEY && !config.apiKeys.kimi) {\n config.apiKeys.kimi = process.env.KIMI_API_KEY;\n config.enabledProviders.add('kimi');\n }\n\n // Load tracker API keys from environment variables as fallback\n if (process.env.LINEAR_API_KEY && !config.trackerKeys.linear) {\n config.trackerKeys.linear = process.env.LINEAR_API_KEY;\n }\n if (process.env.GITHUB_TOKEN && !config.trackerKeys.github) {\n config.trackerKeys.github = process.env.GITHUB_TOKEN;\n }\n if (process.env.GITLAB_TOKEN && !config.trackerKeys.gitlab) {\n config.trackerKeys.gitlab = process.env.GITLAB_TOKEN;\n }\n if (process.env.RALLY_API_KEY && !config.trackerKeys.rally) {\n config.trackerKeys.rally = process.env.RALLY_API_KEY;\n }\n\n // Load shadow mode from environment as fallback\n // Environment variable takes precedence over config file\n if (process.env.SHADOW_MODE !== undefined) {\n const envShadowMode = ['true', '1', 'yes'].includes(process.env.SHADOW_MODE.toLowerCase());\n config.shadow.enabled = envShadowMode;\n }\n\n return { config, migration: migrationResult };\n}\n\n/**\n * Check if a project-level config exists\n */\nexport function hasProjectConfig(): boolean {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return false;\n return existsSync(join(projectRoot, '.panopticon.yaml'));\n}\n\n/**\n * Check if global config exists\n */\nexport function hasGlobalConfig(): boolean {\n return existsSync(GLOBAL_CONFIG_PATH);\n}\n\n/**\n * Get path to global config file\n */\nexport function getGlobalConfigPath(): string {\n return GLOBAL_CONFIG_PATH;\n}\n\n/**\n * Get path to project config file (null if not in a project)\n */\nexport function getProjectConfigPath(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n return join(projectRoot, '.panopticon.yaml');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
init_workspace_config,
|
|
3
3
|
replacePlaceholders
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MOPGR3CL.js";
|
|
5
5
|
import {
|
|
6
6
|
__esm,
|
|
7
7
|
init_esm_shims
|
|
@@ -212,4 +212,4 @@ export {
|
|
|
212
212
|
removeTunnelIngress,
|
|
213
213
|
init_tunnel
|
|
214
214
|
};
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-ZTYHZMEC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/tunnel.ts"],"sourcesContent":["/**\n * Cloudflare Tunnel Management\n *\n * Manages tunnel ingress rules and DNS CNAME records for workspace lifecycle.\n * Called during workspace create (addTunnelIngress) and workspace remove/deep-wipe (removeTunnelIngress).\n */\n\nimport { readFileSync } from 'fs';\nimport { resolve } from 'path';\nimport { homedir } from 'os';\nimport { TunnelConfig, TunnelHostname, TemplatePlaceholders, replacePlaceholders } from './workspace-config.js';\n\nexport interface TunnelResult {\n success: boolean;\n steps: string[];\n}\n\ninterface CloudflareCredentials {\n apiToken: string;\n accountId: string;\n zoneId: string;\n}\n\ninterface CloudflareIngressRule {\n service: string;\n hostname?: string;\n originRequest?: Record<string, unknown>;\n}\n\ninterface CloudflareTunnelConfig {\n config: {\n ingress: CloudflareIngressRule[];\n };\n}\n\nconst CF_API = 'https://api.cloudflare.com/client/v4';\nconst FETCH_TIMEOUT = 10_000;\n\n/**\n * Read API token from Cloudflare cert.pem file.\n * The cert.pem contains a PEM-wrapped base64 JSON blob with { zoneID, accountID, apiToken }.\n */\nfunction readCloudflareCredentials(certPath: string): CloudflareCredentials | null {\n try {\n const resolvedPath = certPath.replace(/^~/, homedir());\n const pem = readFileSync(resolve(resolvedPath), 'utf-8');\n // Strip PEM headers/trailers and decode\n const b64 = pem\n .split('\\n')\n .filter(line => !line.startsWith('-----'))\n .join('');\n const json = JSON.parse(Buffer.from(b64, 'base64').toString('utf-8'));\n return {\n apiToken: json.apiToken,\n accountId: json.accountID,\n zoneId: json.zoneID,\n };\n } catch (err) {\n return null;\n }\n}\n\n/**\n * Make an authenticated Cloudflare API request.\n */\nasync function cfFetch(\n path: string,\n apiToken: string,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',\n body?: unknown,\n): Promise<{ ok: boolean; data: any; errors?: any[] }> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT);\n\n try {\n const resp = await fetch(`${CF_API}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${apiToken}`,\n 'Content-Type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n const json = await resp.json() as any;\n return { ok: json.success !== false, data: json.result, errors: json.errors };\n } catch (err: any) {\n return { ok: false, data: null, errors: [{ message: err.message }] };\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * Resolve hostnames from config, replacing template placeholders.\n */\nfunction resolveHostnames(\n hostnames: TunnelHostname[],\n placeholders: TemplatePlaceholders,\n): Array<{ hostname: string; httpHostHeader?: string; noTlsVerify: boolean }> {\n return hostnames.map(h => ({\n hostname: replacePlaceholders(h.pattern, placeholders),\n httpHostHeader: h.http_host_header ? replacePlaceholders(h.http_host_header, placeholders) : undefined,\n noTlsVerify: h.no_tls_verify !== false, // default true\n }));\n}\n\n/**\n * Add tunnel ingress rules and DNS CNAME records for a workspace.\n * Called during workspace creation.\n */\nexport async function addTunnelIngress(\n config: TunnelConfig,\n placeholders: TemplatePlaceholders,\n): Promise<TunnelResult> {\n const steps: string[] = [];\n let allOk = true;\n\n // Read credentials\n const creds = readCloudflareCredentials(config.credentials_file);\n if (!creds) {\n return { success: false, steps: ['[tunnel] Failed to read Cloudflare credentials from ' + config.credentials_file] };\n }\n steps.push('[tunnel] Read Cloudflare credentials');\n\n const resolved = resolveHostnames(config.hostnames, placeholders);\n\n // Get current tunnel configuration\n const tunnelPath = `/accounts/${creds.accountId}/cfd_tunnel/${config.tunnel_id}/configurations`;\n const current = await cfFetch(tunnelPath, creds.apiToken);\n if (!current.ok) {\n return { success: false, steps: [...steps, `[tunnel] Failed to get tunnel config: ${JSON.stringify(current.errors)}`] };\n }\n\n const tunnelConfig: CloudflareTunnelConfig = current.data;\n const ingress: CloudflareIngressRule[] = tunnelConfig.config?.ingress || [];\n steps.push(`[tunnel] Current tunnel config has ${ingress.length} ingress rules`);\n\n // Add new ingress rules (before the catch-all)\n let modified = false;\n for (const h of resolved) {\n // Skip if rule already exists\n if (ingress.some(r => r.hostname === h.hostname)) {\n steps.push(`[tunnel] Ingress rule for ${h.hostname} already exists, skipping`);\n continue;\n }\n\n const originRequest: Record<string, unknown> = {};\n if (h.noTlsVerify) originRequest.noTLSVerify = true;\n if (h.httpHostHeader) originRequest.httpHostHeader = h.httpHostHeader;\n\n const newRule: CloudflareIngressRule = {\n service: config.service_target,\n hostname: h.hostname,\n originRequest: Object.keys(originRequest).length > 0 ? originRequest : undefined,\n };\n\n // Insert before the last rule (catch-all has no hostname)\n const catchAllIdx = ingress.findIndex(r => !r.hostname);\n if (catchAllIdx >= 0) {\n ingress.splice(catchAllIdx, 0, newRule);\n } else {\n ingress.push(newRule);\n }\n modified = true;\n steps.push(`[tunnel] Added ingress rule for ${h.hostname}`);\n }\n\n // Push updated tunnel config\n if (modified) {\n const putResult = await cfFetch(tunnelPath, creds.apiToken, 'PUT', {\n config: { ingress },\n });\n if (!putResult.ok) {\n steps.push(`[tunnel] Failed to update tunnel config: ${JSON.stringify(putResult.errors)}`);\n allOk = false;\n } else {\n steps.push('[tunnel] Updated tunnel ingress configuration');\n }\n }\n\n // Create DNS CNAME records\n for (const h of resolved) {\n const dnsResult = await cfFetch(\n `/zones/${creds.zoneId}/dns_records`,\n creds.apiToken,\n 'POST',\n {\n type: 'CNAME',\n name: h.hostname,\n content: `${config.tunnel_id}.cfargotunnel.com`,\n proxied: true,\n },\n );\n if (!dnsResult.ok) {\n const errMsg = dnsResult.errors?.map((e: any) => e.message).join(', ') || 'unknown error';\n // Record already exists is not a failure\n if (errMsg.includes('already exists') || errMsg.includes('already been taken')) {\n steps.push(`[tunnel] DNS CNAME for ${h.hostname} already exists`);\n } else {\n steps.push(`[tunnel] Failed to create DNS CNAME for ${h.hostname}: ${errMsg}`);\n allOk = false;\n }\n } else {\n steps.push(`[tunnel] Created DNS CNAME: ${h.hostname} → ${config.tunnel_id}.cfargotunnel.com`);\n }\n }\n\n return { success: allOk, steps };\n}\n\n/**\n * Remove tunnel ingress rules and DNS CNAME records for a workspace.\n * Called during workspace removal and deep-wipe.\n */\nexport async function removeTunnelIngress(\n config: TunnelConfig,\n placeholders: TemplatePlaceholders,\n): Promise<TunnelResult> {\n const steps: string[] = [];\n let allOk = true;\n\n // Read credentials\n const creds = readCloudflareCredentials(config.credentials_file);\n if (!creds) {\n return { success: false, steps: ['[tunnel] Failed to read Cloudflare credentials from ' + config.credentials_file] };\n }\n steps.push('[tunnel] Read Cloudflare credentials');\n\n const resolved = resolveHostnames(config.hostnames, placeholders);\n const hostnameSet = new Set(resolved.map(h => h.hostname));\n\n // Get current tunnel configuration\n const tunnelPath = `/accounts/${creds.accountId}/cfd_tunnel/${config.tunnel_id}/configurations`;\n const current = await cfFetch(tunnelPath, creds.apiToken);\n if (!current.ok) {\n steps.push(`[tunnel] Failed to get tunnel config: ${JSON.stringify(current.errors)}`);\n // Continue to attempt DNS cleanup even if tunnel config read fails\n allOk = false;\n } else {\n const tunnelConfig: CloudflareTunnelConfig = current.data;\n const ingress: CloudflareIngressRule[] = tunnelConfig.config?.ingress || [];\n const originalCount = ingress.length;\n\n // Filter out matching ingress rules\n const filtered = ingress.filter(r => !r.hostname || !hostnameSet.has(r.hostname));\n\n if (filtered.length < originalCount) {\n const putResult = await cfFetch(tunnelPath, creds.apiToken, 'PUT', {\n config: { ingress: filtered },\n });\n if (!putResult.ok) {\n steps.push(`[tunnel] Failed to update tunnel config: ${JSON.stringify(putResult.errors)}`);\n allOk = false;\n } else {\n steps.push(`[tunnel] Removed ${originalCount - filtered.length} ingress rule(s)`);\n }\n } else {\n steps.push('[tunnel] No matching ingress rules found to remove');\n }\n }\n\n // Remove DNS CNAME records\n for (const h of resolved) {\n // Find the DNS record\n const listResult = await cfFetch(\n `/zones/${creds.zoneId}/dns_records?name=${encodeURIComponent(h.hostname)}&type=CNAME`,\n creds.apiToken,\n );\n if (!listResult.ok) {\n steps.push(`[tunnel] Failed to look up DNS record for ${h.hostname}: ${JSON.stringify(listResult.errors)}`);\n allOk = false;\n continue;\n }\n\n const records = Array.isArray(listResult.data) ? listResult.data : [];\n if (records.length === 0) {\n steps.push(`[tunnel] No DNS CNAME record found for ${h.hostname}`);\n continue;\n }\n\n for (const record of records) {\n const delResult = await cfFetch(\n `/zones/${creds.zoneId}/dns_records/${record.id}`,\n creds.apiToken,\n 'DELETE',\n );\n if (!delResult.ok) {\n steps.push(`[tunnel] Failed to delete DNS record ${record.id} for ${h.hostname}: ${JSON.stringify(delResult.errors)}`);\n allOk = false;\n } else {\n steps.push(`[tunnel] Deleted DNS CNAME for ${h.hostname}`);\n }\n }\n }\n\n return { success: allOk, steps };\n}\n"],"mappings":"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","names":[]}
|