panopticon-cli 0.4.32 → 0.4.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/{agents-BDFHF4T3.js → agents-VLK4BMVA.js} +10 -7
  2. package/dist/chunk-7SN4L4PH.js +150 -0
  3. package/dist/chunk-7SN4L4PH.js.map +1 -0
  4. package/dist/chunk-7XNJJBH6.js +538 -0
  5. package/dist/chunk-7XNJJBH6.js.map +1 -0
  6. package/dist/chunk-AQXETQHW.js +113 -0
  7. package/dist/chunk-AQXETQHW.js.map +1 -0
  8. package/dist/{chunk-2NIAOCIC.js → chunk-ASY7T35E.js} +170 -64
  9. package/dist/chunk-ASY7T35E.js.map +1 -0
  10. package/dist/chunk-B3PF6JPQ.js +212 -0
  11. package/dist/chunk-B3PF6JPQ.js.map +1 -0
  12. package/dist/{chunk-XP2DXWYP.js → chunk-BKCWRMUX.js} +88 -35
  13. package/dist/chunk-BKCWRMUX.js.map +1 -0
  14. package/dist/chunk-CFCUOV3Q.js +669 -0
  15. package/dist/chunk-CFCUOV3Q.js.map +1 -0
  16. package/dist/chunk-CWELWPWQ.js +32 -0
  17. package/dist/chunk-CWELWPWQ.js.map +1 -0
  18. package/dist/chunk-DI7ABPNQ.js +352 -0
  19. package/dist/chunk-DI7ABPNQ.js.map +1 -0
  20. package/dist/{chunk-VU4FLXV5.js → chunk-FQ66DECN.js} +31 -4
  21. package/dist/chunk-FQ66DECN.js.map +1 -0
  22. package/dist/{review-status-GWQYY77L.js → chunk-GFP3PIPB.js} +14 -7
  23. package/dist/chunk-GFP3PIPB.js.map +1 -0
  24. package/dist/chunk-JQBV3Q2W.js +29 -0
  25. package/dist/chunk-JQBV3Q2W.js.map +1 -0
  26. package/dist/{chunk-BWGFN44T.js → chunk-JT4O4YVM.js} +28 -16
  27. package/dist/chunk-JT4O4YVM.js.map +1 -0
  28. package/dist/{chunk-VIWUCJ4V.js → chunk-KJ2TRXNK.js} +34 -36
  29. package/dist/chunk-KJ2TRXNK.js.map +1 -0
  30. package/dist/{chunk-JY7R7V4G.js → chunk-OMNXYPXC.js} +2 -2
  31. package/dist/chunk-OMNXYPXC.js.map +1 -0
  32. package/dist/chunk-PELXV435.js +215 -0
  33. package/dist/chunk-PELXV435.js.map +1 -0
  34. package/dist/chunk-PI7Y3PSN.js +797 -0
  35. package/dist/chunk-PI7Y3PSN.js.map +1 -0
  36. package/dist/chunk-RBUO57TC.js +154 -0
  37. package/dist/chunk-RBUO57TC.js.map +1 -0
  38. package/dist/chunk-XFR2DLMR.js +600 -0
  39. package/dist/chunk-XFR2DLMR.js.map +1 -0
  40. package/dist/chunk-XKT5MHPT.js +677 -0
  41. package/dist/chunk-XKT5MHPT.js.map +1 -0
  42. package/dist/{chunk-HCTJFIJJ.js → chunk-YLPSQAM2.js} +2 -2
  43. package/dist/{chunk-HCTJFIJJ.js.map → chunk-YLPSQAM2.js.map} +1 -1
  44. package/dist/{chunk-6HXKTOD7.js → chunk-ZTFNYOC7.js} +53 -38
  45. package/dist/chunk-ZTFNYOC7.js.map +1 -0
  46. package/dist/cli/index.js +4362 -2927
  47. package/dist/cli/index.js.map +1 -1
  48. package/dist/{config-BOAMSKTF.js → config-4CJNUE3O.js} +7 -3
  49. package/dist/dashboard/prompts/merge-agent.md +217 -0
  50. package/dist/dashboard/prompts/review-agent.md +409 -0
  51. package/dist/dashboard/prompts/sync-main.md +84 -0
  52. package/dist/dashboard/prompts/test-agent.md +283 -0
  53. package/dist/dashboard/prompts/work-agent.md +247 -0
  54. package/dist/dashboard/public/assets/index-UjZq6ykz.css +32 -0
  55. package/dist/dashboard/public/assets/index-kAJqtLDO.js +708 -0
  56. package/dist/dashboard/public/index.html +2 -2
  57. package/dist/dashboard/server.js +15194 -3160
  58. package/dist/{dns-L3L2BB27.js → dns-7BDJSD3E.js} +4 -2
  59. package/dist/{feedback-writer-AAKF5BTK.js → feedback-writer-LVZ5TFYZ.js} +8 -4
  60. package/dist/feedback-writer-LVZ5TFYZ.js.map +1 -0
  61. package/dist/hume-WMAUBBV2.js +13 -0
  62. package/dist/index.d.ts +153 -40
  63. package/dist/index.js +65 -23
  64. package/dist/index.js.map +1 -1
  65. package/dist/{projects-VXRUCMLM.js → projects-JEIVIYC6.js} +3 -3
  66. package/dist/rally-RKFSWC7E.js +10 -0
  67. package/dist/{remote-agents-Z3R2A5BN.js → remote-agents-TFSMW7GN.js} +2 -2
  68. package/dist/{remote-workspace-2G6V2KNP.js → remote-workspace-AHVHQEES.js} +8 -8
  69. package/dist/review-status-EPFG4XM7.js +19 -0
  70. package/dist/shadow-state-5MDP6YXH.js +30 -0
  71. package/dist/shadow-state-5MDP6YXH.js.map +1 -0
  72. package/dist/{specialist-context-N32QBNNQ.js → specialist-context-T3NBMCIE.js} +8 -7
  73. package/dist/{specialist-context-N32QBNNQ.js.map → specialist-context-T3NBMCIE.js.map} +1 -1
  74. package/dist/{specialist-logs-GF3YV4KL.js → specialist-logs-CVKD3YJ3.js} +7 -6
  75. package/dist/specialist-logs-CVKD3YJ3.js.map +1 -0
  76. package/dist/{specialists-JBIW6MP4.js → specialists-TKAP6T6Z.js} +7 -6
  77. package/dist/specialists-TKAP6T6Z.js.map +1 -0
  78. package/dist/tldr-daemon-T3THOUGT.js +21 -0
  79. package/dist/tldr-daemon-T3THOUGT.js.map +1 -0
  80. package/dist/traefik-QX4ZV4YG.js +19 -0
  81. package/dist/traefik-QX4ZV4YG.js.map +1 -0
  82. package/dist/tunnel-W2GZBLEV.js +13 -0
  83. package/dist/tunnel-W2GZBLEV.js.map +1 -0
  84. package/dist/workspace-manager-KLHUCIZV.js +22 -0
  85. package/dist/workspace-manager-KLHUCIZV.js.map +1 -0
  86. package/package.json +2 -2
  87. package/scripts/heartbeat-hook +37 -10
  88. package/scripts/patches/llm-tldr-tsx-support.py +109 -0
  89. package/scripts/pre-tool-hook +26 -15
  90. package/scripts/record-cost-event.js +177 -43
  91. package/scripts/record-cost-event.ts +87 -3
  92. package/scripts/statusline.sh +169 -0
  93. package/scripts/stop-hook +14 -11
  94. package/scripts/tldr-post-edit +72 -0
  95. package/scripts/tldr-read-enforcer +275 -0
  96. package/skills/check-merged/SKILL.md +143 -0
  97. package/skills/crash-investigation/SKILL.md +301 -0
  98. package/skills/github-cli/SKILL.md +185 -0
  99. package/skills/pan-reopen/SKILL.md +65 -0
  100. package/skills/pan-sync-main/SKILL.md +87 -0
  101. package/skills/pan-tldr/SKILL.md +149 -0
  102. package/skills/react-best-practices/SKILL.md +125 -0
  103. package/skills/spec-readiness/REPORT-TEMPLATE.md +158 -0
  104. package/skills/spec-readiness/SCORING-REFERENCE.md +369 -0
  105. package/skills/spec-readiness/SKILL.md +400 -0
  106. package/skills/spec-readiness-setup/SKILL.md +361 -0
  107. package/skills/workspace-status/SKILL.md +56 -0
  108. package/templates/traefik/dynamic/panopticon.yml.template +0 -5
  109. package/templates/traefik/traefik.yml +0 -8
  110. package/dist/chunk-2NIAOCIC.js.map +0 -1
  111. package/dist/chunk-3XAB4IXF.js +0 -51
  112. package/dist/chunk-3XAB4IXF.js.map +0 -1
  113. package/dist/chunk-6HXKTOD7.js.map +0 -1
  114. package/dist/chunk-BBCUK6N2.js +0 -241
  115. package/dist/chunk-BBCUK6N2.js.map +0 -1
  116. package/dist/chunk-BWGFN44T.js.map +0 -1
  117. package/dist/chunk-ELK6Q7QI.js +0 -545
  118. package/dist/chunk-ELK6Q7QI.js.map +0 -1
  119. package/dist/chunk-JY7R7V4G.js.map +0 -1
  120. package/dist/chunk-LYSBSZYV.js +0 -1523
  121. package/dist/chunk-LYSBSZYV.js.map +0 -1
  122. package/dist/chunk-VIWUCJ4V.js.map +0 -1
  123. package/dist/chunk-VU4FLXV5.js.map +0 -1
  124. package/dist/chunk-XP2DXWYP.js.map +0 -1
  125. package/dist/dashboard/public/assets/index-C7X6LP5Z.css +0 -32
  126. package/dist/dashboard/public/assets/index-ClYqpcAJ.js +0 -645
  127. package/dist/feedback-writer-AAKF5BTK.js.map +0 -1
  128. package/dist/review-status-GWQYY77L.js.map +0 -1
  129. package/dist/traefik-CUJM6K5Z.js +0 -12
  130. /package/dist/{agents-BDFHF4T3.js.map → agents-VLK4BMVA.js.map} +0 -0
  131. /package/dist/{config-BOAMSKTF.js.map → config-4CJNUE3O.js.map} +0 -0
  132. /package/dist/{dns-L3L2BB27.js.map → dns-7BDJSD3E.js.map} +0 -0
  133. /package/dist/{projects-VXRUCMLM.js.map → hume-WMAUBBV2.js.map} +0 -0
  134. /package/dist/{remote-agents-Z3R2A5BN.js.map → projects-JEIVIYC6.js.map} +0 -0
  135. /package/dist/{specialist-logs-GF3YV4KL.js.map → rally-RKFSWC7E.js.map} +0 -0
  136. /package/dist/{specialists-JBIW6MP4.js.map → remote-agents-TFSMW7GN.js.map} +0 -0
  137. /package/dist/{remote-workspace-2G6V2KNP.js.map → remote-workspace-AHVHQEES.js.map} +0 -0
  138. /package/dist/{traefik-CUJM6K5Z.js.map → review-status-EPFG4XM7.js.map} +0 -0
@@ -0,0 +1,538 @@
1
+ import {
2
+ SETTINGS_FILE,
3
+ init_paths
4
+ } from "./chunk-ZTFNYOC7.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.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
+ var PROVIDERS;
230
+ var init_providers = __esm({
231
+ "src/lib/providers.ts"() {
232
+ "use strict";
233
+ init_esm_shims();
234
+ PROVIDERS = {
235
+ anthropic: {
236
+ name: "anthropic",
237
+ displayName: "Anthropic",
238
+ compatibility: "direct",
239
+ models: ["claude-opus-4-6", "claude-sonnet-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"],
240
+ tested: true,
241
+ description: "Native Claude API"
242
+ },
243
+ kimi: {
244
+ name: "kimi",
245
+ displayName: "Kimi (Moonshot AI)",
246
+ compatibility: "direct",
247
+ baseUrl: "https://api.kimi.com/coding/",
248
+ authType: "credential-file",
249
+ credentialFile: "~/.kimi/credentials/kimi-code.json",
250
+ credentialHelper: "~/.panopticon/bin/kimi-token-helper.sh",
251
+ models: [],
252
+ // Kimi uses same model names as Anthropic
253
+ tested: true,
254
+ description: "Anthropic-compatible API via Kimi Code Plan (OAuth token refresh)"
255
+ },
256
+ zai: {
257
+ name: "zai",
258
+ displayName: "Z.AI (GLM)",
259
+ compatibility: "direct",
260
+ baseUrl: "https://api.z.ai/api/anthropic",
261
+ models: ["glm-4.7", "glm-4.7-flash"],
262
+ tested: true,
263
+ description: "Anthropic-compatible API, tested 2026-01-28"
264
+ },
265
+ openai: {
266
+ name: "openai",
267
+ displayName: "OpenAI",
268
+ compatibility: "router",
269
+ models: ["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"],
270
+ tested: false,
271
+ description: "Requires claude-code-router for API translation"
272
+ },
273
+ google: {
274
+ name: "google",
275
+ displayName: "Google (Gemini)",
276
+ compatibility: "router",
277
+ models: ["gemini-3-pro-preview", "gemini-3-flash-preview"],
278
+ tested: false,
279
+ description: "Requires claude-code-router for API translation"
280
+ }
281
+ };
282
+ }
283
+ });
284
+
285
+ // src/lib/config-yaml.ts
286
+ import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
287
+ import { join as join2 } from "path";
288
+ import { homedir } from "os";
289
+ import yaml from "js-yaml";
290
+ function normalizeProviderConfig(providerConfig, fallbackKey) {
291
+ if (providerConfig === void 0) {
292
+ return { enabled: false };
293
+ }
294
+ if (typeof providerConfig === "boolean") {
295
+ return { enabled: providerConfig, api_key: fallbackKey };
296
+ }
297
+ return {
298
+ enabled: providerConfig.enabled,
299
+ api_key: providerConfig.api_key || fallbackKey
300
+ };
301
+ }
302
+ function resolveEnvVar(value) {
303
+ if (!value) return void 0;
304
+ return value.replace(/\$\{?([A-Z_][A-Z0-9_]*)\}?/g, (match, varName) => {
305
+ const envValue = process.env[varName];
306
+ return envValue !== void 0 ? envValue : match;
307
+ });
308
+ }
309
+ function loadYamlFile(filePath) {
310
+ if (!existsSync3(filePath)) {
311
+ return null;
312
+ }
313
+ try {
314
+ const content = readFileSync3(filePath, "utf-8");
315
+ const parsed = yaml.load(content);
316
+ return parsed || {};
317
+ } catch (error) {
318
+ console.error(`Error loading YAML config from ${filePath}:`, error);
319
+ return null;
320
+ }
321
+ }
322
+ function findProjectRoot(startDir = process.cwd()) {
323
+ let currentDir = startDir;
324
+ while (currentDir !== "/") {
325
+ if (existsSync3(join2(currentDir, ".git"))) {
326
+ return currentDir;
327
+ }
328
+ currentDir = join2(currentDir, "..");
329
+ }
330
+ return null;
331
+ }
332
+ function loadProjectConfig() {
333
+ const projectRoot = findProjectRoot();
334
+ if (!projectRoot) {
335
+ return null;
336
+ }
337
+ const projectConfigPath = join2(projectRoot, ".panopticon.yaml");
338
+ return loadYamlFile(projectConfigPath);
339
+ }
340
+ function loadGlobalConfig() {
341
+ return loadYamlFile(GLOBAL_CONFIG_PATH);
342
+ }
343
+ function mergeShadowConfig(result, config) {
344
+ if (!config?.shadow) return;
345
+ if (config.shadow.enabled !== void 0) {
346
+ result.enabled = config.shadow.enabled;
347
+ }
348
+ if (config.shadow.trackers) {
349
+ if (config.shadow.trackers.linear !== void 0) {
350
+ result.trackers.linear = config.shadow.trackers.linear;
351
+ }
352
+ if (config.shadow.trackers.github !== void 0) {
353
+ result.trackers.github = config.shadow.trackers.github;
354
+ }
355
+ if (config.shadow.trackers.gitlab !== void 0) {
356
+ result.trackers.gitlab = config.shadow.trackers.gitlab;
357
+ }
358
+ if (config.shadow.trackers.rally !== void 0) {
359
+ result.trackers.rally = config.shadow.trackers.rally;
360
+ }
361
+ }
362
+ }
363
+ function mergeConfigs(...configs) {
364
+ const result = {
365
+ ...DEFAULT_CONFIG,
366
+ enabledProviders: new Set(DEFAULT_CONFIG.enabledProviders),
367
+ shadow: {
368
+ enabled: DEFAULT_CONFIG.shadow.enabled,
369
+ trackers: { ...DEFAULT_CONFIG.shadow.trackers }
370
+ }
371
+ };
372
+ const validConfigs = configs.filter((c) => c !== null);
373
+ for (const config of validConfigs.reverse()) {
374
+ if (config.models?.providers) {
375
+ const providers = config.models.providers;
376
+ const legacyKeys = config.api_keys || {};
377
+ result.enabledProviders.add("anthropic");
378
+ const openai = normalizeProviderConfig(providers.openai, legacyKeys.openai);
379
+ if (openai.enabled) {
380
+ result.enabledProviders.add("openai");
381
+ if (openai.api_key) {
382
+ result.apiKeys.openai = resolveEnvVar(openai.api_key);
383
+ }
384
+ }
385
+ const google = normalizeProviderConfig(providers.google, legacyKeys.google);
386
+ if (google.enabled) {
387
+ result.enabledProviders.add("google");
388
+ if (google.api_key) {
389
+ result.apiKeys.google = resolveEnvVar(google.api_key);
390
+ }
391
+ }
392
+ const zai = normalizeProviderConfig(providers.zai, legacyKeys.zai);
393
+ if (zai.enabled) {
394
+ result.enabledProviders.add("zai");
395
+ if (zai.api_key) {
396
+ result.apiKeys.zai = resolveEnvVar(zai.api_key);
397
+ }
398
+ }
399
+ const kimi = normalizeProviderConfig(providers.kimi, legacyKeys.kimi);
400
+ if (kimi.enabled) {
401
+ result.enabledProviders.add("kimi");
402
+ if (kimi.api_key) {
403
+ result.apiKeys.kimi = resolveEnvVar(kimi.api_key);
404
+ }
405
+ }
406
+ }
407
+ if (config.api_keys) {
408
+ if (config.api_keys.openai) {
409
+ result.apiKeys.openai = resolveEnvVar(config.api_keys.openai);
410
+ result.enabledProviders.add("openai");
411
+ }
412
+ if (config.api_keys.google) {
413
+ result.apiKeys.google = resolveEnvVar(config.api_keys.google);
414
+ result.enabledProviders.add("google");
415
+ }
416
+ if (config.api_keys.zai) {
417
+ result.apiKeys.zai = resolveEnvVar(config.api_keys.zai);
418
+ result.enabledProviders.add("zai");
419
+ }
420
+ if (config.api_keys.kimi) {
421
+ result.apiKeys.kimi = resolveEnvVar(config.api_keys.kimi);
422
+ result.enabledProviders.add("kimi");
423
+ }
424
+ }
425
+ if (config.models?.overrides) {
426
+ result.overrides = {
427
+ ...result.overrides,
428
+ ...config.models.overrides
429
+ };
430
+ }
431
+ if (config.models?.gemini_thinking_level) {
432
+ result.geminiThinkingLevel = config.models.gemini_thinking_level;
433
+ }
434
+ if (config.tracker_keys) {
435
+ if (config.tracker_keys.linear) {
436
+ result.trackerKeys.linear = resolveEnvVar(config.tracker_keys.linear);
437
+ }
438
+ if (config.tracker_keys.github) {
439
+ result.trackerKeys.github = resolveEnvVar(config.tracker_keys.github);
440
+ }
441
+ if (config.tracker_keys.gitlab) {
442
+ result.trackerKeys.gitlab = resolveEnvVar(config.tracker_keys.gitlab);
443
+ }
444
+ if (config.tracker_keys.rally) {
445
+ result.trackerKeys.rally = resolveEnvVar(config.tracker_keys.rally);
446
+ }
447
+ }
448
+ mergeShadowConfig(result.shadow, config);
449
+ }
450
+ return result;
451
+ }
452
+ function loadConfig() {
453
+ const globalConfig = loadGlobalConfig();
454
+ const projectConfig = loadProjectConfig();
455
+ const config = mergeConfigs(projectConfig, globalConfig);
456
+ if (process.env.OPENAI_API_KEY && !config.apiKeys.openai) {
457
+ config.apiKeys.openai = process.env.OPENAI_API_KEY;
458
+ config.enabledProviders.add("openai");
459
+ }
460
+ if (process.env.GOOGLE_API_KEY && !config.apiKeys.google) {
461
+ config.apiKeys.google = process.env.GOOGLE_API_KEY;
462
+ config.enabledProviders.add("google");
463
+ }
464
+ if (process.env.ZAI_API_KEY && !config.apiKeys.zai) {
465
+ config.apiKeys.zai = process.env.ZAI_API_KEY;
466
+ config.enabledProviders.add("zai");
467
+ }
468
+ if (process.env.KIMI_API_KEY && !config.apiKeys.kimi) {
469
+ config.apiKeys.kimi = process.env.KIMI_API_KEY;
470
+ config.enabledProviders.add("kimi");
471
+ }
472
+ if (process.env.LINEAR_API_KEY && !config.trackerKeys.linear) {
473
+ config.trackerKeys.linear = process.env.LINEAR_API_KEY;
474
+ }
475
+ if (process.env.GITHUB_TOKEN && !config.trackerKeys.github) {
476
+ config.trackerKeys.github = process.env.GITHUB_TOKEN;
477
+ }
478
+ if (process.env.GITLAB_TOKEN && !config.trackerKeys.gitlab) {
479
+ config.trackerKeys.gitlab = process.env.GITLAB_TOKEN;
480
+ }
481
+ if (process.env.RALLY_API_KEY && !config.trackerKeys.rally) {
482
+ config.trackerKeys.rally = process.env.RALLY_API_KEY;
483
+ }
484
+ if (process.env.SHADOW_MODE !== void 0) {
485
+ const envShadowMode = ["true", "1", "yes"].includes(process.env.SHADOW_MODE.toLowerCase());
486
+ config.shadow.enabled = envShadowMode;
487
+ }
488
+ return config;
489
+ }
490
+ var DEFAULT_CONFIG, GLOBAL_CONFIG_PATH;
491
+ var init_config_yaml = __esm({
492
+ "src/lib/config-yaml.ts"() {
493
+ "use strict";
494
+ init_esm_shims();
495
+ DEFAULT_CONFIG = {
496
+ enabledProviders: /* @__PURE__ */ new Set(["anthropic"]),
497
+ // Only Anthropic by default
498
+ apiKeys: {},
499
+ overrides: {},
500
+ geminiThinkingLevel: 3,
501
+ trackerKeys: {},
502
+ shadow: {
503
+ enabled: false,
504
+ trackers: {
505
+ linear: false,
506
+ github: false,
507
+ gitlab: false,
508
+ rally: false
509
+ }
510
+ }
511
+ };
512
+ GLOBAL_CONFIG_PATH = join2(homedir(), ".panopticon", "config.yaml");
513
+ }
514
+ });
515
+
516
+ export {
517
+ loadSettings,
518
+ saveSettings,
519
+ validateSettings,
520
+ getDefaultSettings,
521
+ getAvailableModels,
522
+ isAnthropicModel,
523
+ getClaudeModelFlag,
524
+ getAgentCommand,
525
+ init_settings,
526
+ loadConfig,
527
+ init_config_yaml,
528
+ PROVIDERS,
529
+ getProviderForModel,
530
+ requiresRouter,
531
+ getRouterProviders,
532
+ getDirectProviders,
533
+ needsRouter,
534
+ getProviderEnv,
535
+ setupCredentialFileAuth,
536
+ init_providers
537
+ };
538
+ //# sourceMappingURL=chunk-7XNJJBH6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/settings.ts","../src/lib/providers.ts","../src/lib/config-yaml.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-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 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-6',\n merge_agent: 'claude-sonnet-4-6',\n },\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 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-6',\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-6': 'sonnet',\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 { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\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\n/**\r\n * Auth type for direct providers:\r\n * - static: Use a long-lived API key passed via ANTHROPIC_AUTH_TOKEN (default)\r\n * - credential-file: Use apiKeyHelper to read a fresh token from a credential file.\r\n * Used for providers like Kimi Code Plan whose JWT tokens expire every ~15 minutes.\r\n */\r\nexport type ProviderAuthType = 'static' | 'credential-file';\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 authType?: ProviderAuthType; // Defaults to 'static'\r\n credentialFile?: string; // Path to credential file (for 'credential-file' auth)\r\n credentialHelper?: string; // Script that reads credential file and prints token\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-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 authType: 'credential-file',\r\n credentialFile: '~/.kimi/credentials/kimi-code.json',\r\n credentialHelper: '~/.panopticon/bin/kimi-token-helper.sh',\r\n models: [], // Kimi uses same model names as Anthropic\r\n tested: true,\r\n description: 'Anthropic-compatible API via Kimi Code Plan (OAuth token refresh)',\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-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 if (provider.authType === 'credential-file') {\r\n // Credential-file providers use apiKeyHelper for dynamic token refresh.\r\n // We still need an initial ANTHROPIC_AUTH_TOKEN for the first request,\r\n // but apiKeyHelper (configured via setupCredentialFileAuth) will keep it fresh.\r\n env.ANTHROPIC_AUTH_TOKEN = apiKey;\r\n // Refresh token every 60 seconds (kimi-cli refreshes credential file automatically)\r\n env.CLAUDE_CODE_API_KEY_HELPER_TTL_MS = '60000';\r\n } else {\r\n // Static providers use a long-lived API key\r\n env.ANTHROPIC_AUTH_TOKEN = apiKey;\r\n }\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\r\n/**\r\n * For credential-file providers (e.g. Kimi Code Plan), configure Claude Code's\r\n * apiKeyHelper in the workspace settings so tokens are refreshed dynamically.\r\n *\r\n * This writes to .claude/settings.local.json in the workspace directory.\r\n * Must be called before spawning the agent.\r\n */\r\nexport function setupCredentialFileAuth(provider: ProviderConfig, workspacePath: string): void {\r\n if (provider.authType !== 'credential-file' || !provider.credentialHelper) return;\r\n\r\n const helperPath = provider.credentialHelper.replace('~', process.env.HOME || '');\r\n const claudeDir = join(workspacePath, '.claude');\r\n const settingsPath = join(claudeDir, 'settings.local.json');\r\n\r\n if (!existsSync(claudeDir)) {\r\n mkdirSync(claudeDir, { recursive: true });\r\n }\r\n\r\n // Read existing settings or start fresh\r\n let settings: Record<string, unknown> = {};\r\n if (existsSync(settingsPath)) {\r\n try {\r\n settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));\r\n } catch { /* start fresh */ }\r\n }\r\n\r\n // Set the apiKeyHelper to our token reader script\r\n settings.apiKeyHelper = helperPath;\r\n\r\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\r\n}\r\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 } 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';\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 * 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 * Load complete configuration (global + project + defaults)\n * Also loads API keys from environment variables as fallback\n */\nexport function loadConfig(): NormalizedConfig {\n const globalConfig = loadGlobalConfig();\n const projectConfig = loadProjectConfig();\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;\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":";;;;;;;;;;AAAA,SAAS,cAAc,eAAe,kBAAkB;AAwExD,SAAS,UAA4B,UAAa,WAA0B;AAC1E,QAAM,SAAS,EAAE,GAAG,SAAS;AAE7B,aAAW,OAAO,OAAO,KAAK,SAAS,GAAkB;AACvD,UAAM,aAAa,SAAS,GAAG;AAC/B,UAAM,cAAc,UAAU,GAAG;AAGjC,QAAI,gBAAgB,OAAW;AAG/B,QACE,OAAO,eAAe,YACtB,eAAe,QACf,CAAC,MAAM,QAAQ,UAAU,KACzB,OAAO,gBAAgB,YACvB,gBAAgB,QAChB,CAAC,MAAM,QAAQ,WAAW,GAC1B;AACA,aAAO,GAAG,IAAI,UAAU,YAAY,WAAkB;AAAA,IACxD,OAAO;AAEL,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,eAA+B;AAC7C,MAAI;AAEJ,MAAI,CAAC,WAAW,aAAa,GAAG;AAC9B,eAAW,mBAAmB;AAAA,EAChC,OAAO;AACL,QAAI;AACF,YAAM,UAAU,aAAa,eAAe,MAAM;AAClD,YAAM,SAAS,KAAK,MAAM,OAAO;AACjC,iBAAW,UAAU,kBAAkB,MAAM;AAAA,IAC/C,SAAS,OAAO;AACd,cAAQ,MAAM,wDAAwD;AACtE,iBAAW,mBAAmB;AAAA,IAChC;AAAA,EACF;AAIA,QAAM,aAA4B,CAAC;AACnC,MAAI,QAAQ,IAAI,eAAgB,YAAW,SAAS,QAAQ,IAAI;AAChE,MAAI,QAAQ,IAAI,eAAgB,YAAW,SAAS,QAAQ,IAAI;AAChE,MAAI,QAAQ,IAAI,YAAa,YAAW,MAAM,QAAQ,IAAI;AAC1D,MAAI,QAAQ,IAAI,aAAc,YAAW,OAAO,QAAQ,IAAI;AAG5D,WAAS,WAAW;AAAA,IAClB,GAAG;AAAA,IACH,GAAG,SAAS;AAAA,EACd;AAEA,SAAO;AACT;AAMO,SAAS,aAAa,UAAgC;AAC3D,QAAM,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC;AAChD,gBAAc,eAAe,SAAS,MAAM;AAC9C;AAMO,SAAS,iBAAiB,UAAyC;AAExE,MAAI,CAAC,SAAS,QAAQ;AACpB,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,SAAS,OAAO,aAAa;AAChC,WAAO;AAAA,EACT;AACA,QAAM,cAAc,SAAS,OAAO;AACpC,MAAI,CAAC,YAAY,gBAAgB,CAAC,YAAY,cAAc,CAAC,YAAY,aAAa;AACpF,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,SAAS,OAAO,YAAY;AAC/B,WAAO;AAAA,EACT;AACA,QAAM,aAAa,SAAS,OAAO;AACnC,QAAM,iBAAoC,CAAC,WAAW,UAAU,UAAU,WAAW,QAAQ;AAC7F,aAAW,SAAS,gBAAgB;AAClC,QAAI,CAAC,WAAW,KAAK,GAAG;AACtB,aAAO,6BAA6B,KAAK;AAAA,IAC3C;AAAA,EACF;AAGA,MAAI,CAAC,SAAS,UAAU;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKO,SAAS,qBAAqC;AACnD,SAAO,KAAK,MAAM,KAAK,UAAU,gBAAgB,CAAC;AACpD;AAMO,SAAS,mBAAmB,UAMjC;AACA,QAAM,kBAAoC;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,eAA8B,SAAS,SAAS,SAClD,CAAC,iBAAiB,oBAAoB,UAAU,aAAa,IAC7D,CAAC;AAEL,QAAM,eAA8B,SAAS,SAAS,SAClD,CAAC,wBAAwB,wBAAwB,IACjD,CAAC;AAEL,QAAM,YAAwB,SAAS,SAAS,MAC5C,CAAC,WAAW,eAAe,IAC3B,CAAC;AAEL,QAAM,aAA0B,SAAS,SAAS,OAC9C,CAAC,WAAW,WAAW,IACvB,CAAC;AAEL,SAAO;AAAA,IACL,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AACF;AAMO,SAAS,iBAAiB,SAAoC;AACnE,SAAO,QAAQ,WAAW,SAAS;AACrC;AAMO,SAAS,mBAAmB,SAAmC;AACpE,QAAM,WAAmC;AAAA,IACvC,mBAAmB;AAAA,IACnB,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,EACtB;AACA,SAAO,SAAS,OAAO,KAAK;AAC9B;AAMO,SAAS,gBAAgB,SAAgE;AAC9F,MAAI,iBAAiB,OAAO,GAAG;AAC7B,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,CAAC,WAAW,mBAAmB,OAAO,CAAC;AAAA,IAC/C;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM,CAAC;AAAA,EACT;AACF;AAlRA,IAgDM;AAhDN;AAAA;AAAA;AAAA;AACA;AA+CA,IAAM,mBAAmC;AAAA,MACvC,QAAQ;AAAA,QACN,aAAa;AAAA,UACX,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,aAAa;AAAA,QACf;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,UACV,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,MACA,UAAU,CAAC;AAAA,IACb;AAAA;AAAA;;;ACzDA,SAAS,cAAAA,aAAY,WAAW,gBAAAC,eAAc,iBAAAC,sBAAqB;AACnE,SAAS,YAAY;AA8Fd,SAAS,oBAAoB,SAAkC;AAEpE,MAAI,CAAC,mBAAmB,qBAAqB,qBAAqB,kBAAkB,EAAE,SAAS,OAAO,GAAG;AACvG,WAAO,UAAU;AAAA,EACnB;AAGA,MAAI,CAAC,iBAAiB,oBAAoB,UAAU,aAAa,EAAE,SAAS,OAAO,GAAG;AACpF,WAAO,UAAU;AAAA,EACnB;AAGA,MAAI,CAAC,wBAAwB,wBAAwB,EAAE,SAAS,OAAO,GAAG;AACxE,WAAO,UAAU;AAAA,EACnB;AAGA,MAAI,CAAC,WAAW,eAAe,EAAE,SAAS,OAAO,GAAG;AAClD,WAAO,UAAU;AAAA,EACnB;AAGA,MAAI,CAAC,WAAW,WAAW,EAAE,SAAS,OAAO,GAAG;AAC9C,WAAO,UAAU;AAAA,EACnB;AAGA,SAAO,UAAU;AACnB;AAKO,SAAS,eAAe,UAAiC;AAC9D,SAAO,UAAU,QAAQ,EAAE,kBAAkB;AAC/C;AAKO,SAAS,qBAAuC;AACrD,SAAO,OAAO,OAAO,SAAS,EAAE,OAAO,OAAK,EAAE,kBAAkB,QAAQ;AAC1E;AAKO,SAAS,qBAAuC;AACrD,SAAO,OAAO,OAAO,SAAS,EAAE,OAAO,OAAK,EAAE,kBAAkB,QAAQ;AAC1E;AAMO,SAAS,YAAY,SAAsE;AAChG,SAAO,CAAC,EAAE,QAAQ,UAAU,QAAQ;AACtC;AAKO,SAAS,eACd,UACA,QACwB;AACxB,MAAI,SAAS,kBAAkB,UAAU;AAEvC,UAAM,MAA8B,CAAC;AAErC,QAAI,SAAS,SAAS;AACpB,UAAI,qBAAqB,SAAS;AAAA,IACpC;AAEA,QAAI,SAAS,SAAS,aAAa;AACjC,UAAI,SAAS,aAAa,mBAAmB;AAI3C,YAAI,uBAAuB;AAE3B,YAAI,oCAAoC;AAAA,MAC1C,OAAO;AAEL,YAAI,uBAAuB;AAAA,MAC7B;AAAA,IACF;AAGA,QAAI,SAAS,SAAS,OAAO;AAC3B,UAAI,iBAAiB;AAAA,IACvB;AAEA,WAAO;AAAA,EACT,OAAO;AAEL,WAAO;AAAA,MACL,oBAAoB;AAAA,MACpB,sBAAsB;AAAA,IACxB;AAAA,EACF;AACF;AASO,SAAS,wBAAwB,UAA0B,eAA6B;AAC7F,MAAI,SAAS,aAAa,qBAAqB,CAAC,SAAS,iBAAkB;AAE3E,QAAM,aAAa,SAAS,iBAAiB,QAAQ,KAAK,QAAQ,IAAI,QAAQ,EAAE;AAChF,QAAM,YAAY,KAAK,eAAe,SAAS;AAC/C,QAAM,eAAe,KAAK,WAAW,qBAAqB;AAE1D,MAAI,CAACF,YAAW,SAAS,GAAG;AAC1B,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AAGA,MAAI,WAAoC,CAAC;AACzC,MAAIA,YAAW,YAAY,GAAG;AAC5B,QAAI;AACF,iBAAW,KAAK,MAAMC,cAAa,cAAc,OAAO,CAAC;AAAA,IAC3D,QAAQ;AAAA,IAAoB;AAAA,EAC9B;AAGA,WAAS,eAAe;AAExB,EAAAC,eAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AACtE;AA5OA,IAgDa;AAhDb;AAAA;AAAA;AAAA;AAgDO,IAAM,YAAkD;AAAA,MAC7D,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,QACb,eAAe;AAAA,QACf,QAAQ,CAAC,mBAAmB,qBAAqB,qBAAqB,kBAAkB;AAAA,QACxF,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,MAEA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,QACb,eAAe;AAAA,QACf,SAAS;AAAA,QACT,UAAU;AAAA,QACV,gBAAgB;AAAA,QAChB,kBAAkB;AAAA,QAClB,QAAQ,CAAC;AAAA;AAAA,QACT,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,MAEA,KAAK;AAAA,QACH,MAAM;AAAA,QACN,aAAa;AAAA,QACb,eAAe;AAAA,QACf,SAAS;AAAA,QACT,QAAQ,CAAC,WAAW,eAAe;AAAA,QACnC,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,MAEA,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,aAAa;AAAA,QACb,eAAe;AAAA,QACf,QAAQ,CAAC,iBAAiB,oBAAoB,UAAU,aAAa;AAAA,QACrE,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,MAEA,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,aAAa;AAAA,QACb,eAAe;AAAA,QACf,QAAQ,CAAC,wBAAwB,wBAAwB;AAAA,QACzD,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,IACF;AAAA;AAAA;;;ACxFA,SAAS,gBAAAC,eAAc,cAAAC,mBAAkB;AACzC,SAAS,QAAAC,aAAY;AACrB,SAAS,eAAe;AACxB,OAAO,UAAU;AAsJjB,SAAS,wBACP,gBACA,aACwC;AACxC,MAAI,mBAAmB,QAAW;AAChC,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AAEA,MAAI,OAAO,mBAAmB,WAAW;AACvC,WAAO,EAAE,SAAS,gBAAgB,SAAS,YAAY;AAAA,EACzD;AAEA,SAAO;AAAA,IACL,SAAS,eAAe;AAAA,IACxB,SAAS,eAAe,WAAW;AAAA,EACrC;AACF;AAOA,SAAS,cAAc,OAA+C;AACpE,MAAI,CAAC,MAAO,QAAO;AAInB,SAAO,MAAM,QAAQ,+BAA+B,CAAC,OAAO,YAAY;AACtE,UAAM,WAAW,QAAQ,IAAI,OAAO;AACpC,WAAO,aAAa,SAAY,WAAW;AAAA,EAC7C,CAAC;AACH;AAKA,SAAS,aAAa,UAAqC;AACzD,MAAI,CAACD,YAAW,QAAQ,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAUD,cAAa,UAAU,OAAO;AAC9C,UAAM,SAAS,KAAK,KAAK,OAAO;AAChC,WAAO,UAAU,CAAC;AAAA,EACpB,SAAS,OAAO;AACd,YAAQ,MAAM,kCAAkC,QAAQ,KAAK,KAAK;AAClE,WAAO;AAAA,EACT;AACF;AAKA,SAAS,gBAAgB,WAAmB,QAAQ,IAAI,GAAkB;AACxE,MAAI,aAAa;AAEjB,SAAO,eAAe,KAAK;AACzB,QAAIC,YAAWC,MAAK,YAAY,MAAM,CAAC,GAAG;AACxC,aAAO;AAAA,IACT;AACA,iBAAaA,MAAK,YAAY,IAAI;AAAA,EACpC;AAEA,SAAO;AACT;AAKA,SAAS,oBAAuC;AAC9C,QAAM,cAAc,gBAAgB;AACpC,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,oBAAoBA,MAAK,aAAa,kBAAkB;AAC9D,SAAO,aAAa,iBAAiB;AACvC;AAKA,SAAS,mBAAsC;AAC7C,SAAO,aAAa,kBAAkB;AACxC;AAKA,SAAS,kBACP,QACA,QACM;AACN,MAAI,CAAC,QAAQ,OAAQ;AAGrB,MAAI,OAAO,OAAO,YAAY,QAAW;AACvC,WAAO,UAAU,OAAO,OAAO;AAAA,EACjC;AAGA,MAAI,OAAO,OAAO,UAAU;AAC1B,QAAI,OAAO,OAAO,SAAS,WAAW,QAAW;AAC/C,aAAO,SAAS,SAAS,OAAO,OAAO,SAAS;AAAA,IAClD;AACA,QAAI,OAAO,OAAO,SAAS,WAAW,QAAW;AAC/C,aAAO,SAAS,SAAS,OAAO,OAAO,SAAS;AAAA,IAClD;AACA,QAAI,OAAO,OAAO,SAAS,WAAW,QAAW;AAC/C,aAAO,SAAS,SAAS,OAAO,OAAO,SAAS;AAAA,IAClD;AACA,QAAI,OAAO,OAAO,SAAS,UAAU,QAAW;AAC9C,aAAO,SAAS,QAAQ,OAAO,OAAO,SAAS;AAAA,IACjD;AAAA,EACF;AACF;AAKA,SAAS,gBAAgB,SAAkD;AACzE,QAAM,SAA2B;AAAA,IAC/B,GAAG;AAAA,IACH,kBAAkB,IAAI,IAAI,eAAe,gBAAgB;AAAA,IACzD,QAAQ;AAAA,MACN,SAAS,eAAe,OAAO;AAAA,MAC/B,UAAU,EAAE,GAAG,eAAe,OAAO,SAAS;AAAA,IAChD;AAAA,EACF;AAGA,QAAM,eAAe,QAAQ,OAAO,CAAC,MAAuB,MAAM,IAAI;AAGtE,aAAW,UAAU,aAAa,QAAQ,GAAG;AAE3C,QAAI,OAAO,QAAQ,WAAW;AAC5B,YAAM,YAAY,OAAO,OAAO;AAChC,YAAM,aAAa,OAAO,YAAY,CAAC;AAGvC,aAAO,iBAAiB,IAAI,WAAW;AAGvC,YAAM,SAAS,wBAAwB,UAAU,QAAQ,WAAW,MAAM;AAC1E,UAAI,OAAO,SAAS;AAClB,eAAO,iBAAiB,IAAI,QAAQ;AACpC,YAAI,OAAO,SAAS;AAClB,iBAAO,QAAQ,SAAS,cAAc,OAAO,OAAO;AAAA,QACtD;AAAA,MACF;AAGA,YAAM,SAAS,wBAAwB,UAAU,QAAQ,WAAW,MAAM;AAC1E,UAAI,OAAO,SAAS;AAClB,eAAO,iBAAiB,IAAI,QAAQ;AACpC,YAAI,OAAO,SAAS;AAClB,iBAAO,QAAQ,SAAS,cAAc,OAAO,OAAO;AAAA,QACtD;AAAA,MACF;AAGA,YAAM,MAAM,wBAAwB,UAAU,KAAK,WAAW,GAAG;AACjE,UAAI,IAAI,SAAS;AACf,eAAO,iBAAiB,IAAI,KAAK;AACjC,YAAI,IAAI,SAAS;AACf,iBAAO,QAAQ,MAAM,cAAc,IAAI,OAAO;AAAA,QAChD;AAAA,MACF;AAGA,YAAM,OAAO,wBAAwB,UAAU,MAAM,WAAW,IAAI;AACpE,UAAI,KAAK,SAAS;AAChB,eAAO,iBAAiB,IAAI,MAAM;AAClC,YAAI,KAAK,SAAS;AAChB,iBAAO,QAAQ,OAAO,cAAc,KAAK,OAAO;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,UAAU;AACnB,UAAI,OAAO,SAAS,QAAQ;AAC1B,eAAO,QAAQ,SAAS,cAAc,OAAO,SAAS,MAAM;AAC5D,eAAO,iBAAiB,IAAI,QAAQ;AAAA,MACtC;AACA,UAAI,OAAO,SAAS,QAAQ;AAC1B,eAAO,QAAQ,SAAS,cAAc,OAAO,SAAS,MAAM;AAC5D,eAAO,iBAAiB,IAAI,QAAQ;AAAA,MACtC;AACA,UAAI,OAAO,SAAS,KAAK;AACvB,eAAO,QAAQ,MAAM,cAAc,OAAO,SAAS,GAAG;AACtD,eAAO,iBAAiB,IAAI,KAAK;AAAA,MACnC;AACA,UAAI,OAAO,SAAS,MAAM;AACxB,eAAO,QAAQ,OAAO,cAAc,OAAO,SAAS,IAAI;AACxD,eAAO,iBAAiB,IAAI,MAAM;AAAA,MACpC;AAAA,IACF;AAGA,QAAI,OAAO,QAAQ,WAAW;AAC5B,aAAO,YAAY;AAAA,QACjB,GAAG,OAAO;AAAA,QACV,GAAG,OAAO,OAAO;AAAA,MACnB;AAAA,IACF;AAGA,QAAI,OAAO,QAAQ,uBAAuB;AACxC,aAAO,sBAAsB,OAAO,OAAO;AAAA,IAC7C;AAGA,QAAI,OAAO,cAAc;AACvB,UAAI,OAAO,aAAa,QAAQ;AAC9B,eAAO,YAAY,SAAS,cAAc,OAAO,aAAa,MAAM;AAAA,MACtE;AACA,UAAI,OAAO,aAAa,QAAQ;AAC9B,eAAO,YAAY,SAAS,cAAc,OAAO,aAAa,MAAM;AAAA,MACtE;AACA,UAAI,OAAO,aAAa,QAAQ;AAC9B,eAAO,YAAY,SAAS,cAAc,OAAO,aAAa,MAAM;AAAA,MACtE;AACA,UAAI,OAAO,aAAa,OAAO;AAC7B,eAAO,YAAY,QAAQ,cAAc,OAAO,aAAa,KAAK;AAAA,MACpE;AAAA,IACF;AAGA,sBAAkB,OAAO,QAAQ,MAAM;AAAA,EACzC;AAEA,SAAO;AACT;AAMO,SAAS,aAA+B;AAC7C,QAAM,eAAe,iBAAiB;AACtC,QAAM,gBAAgB,kBAAkB;AACxC,QAAM,SAAS,aAAa,eAAe,YAAY;AAIvD,MAAI,QAAQ,IAAI,kBAAkB,CAAC,OAAO,QAAQ,QAAQ;AACxD,WAAO,QAAQ,SAAS,QAAQ,IAAI;AACpC,WAAO,iBAAiB,IAAI,QAAQ;AAAA,EACtC;AACA,MAAI,QAAQ,IAAI,kBAAkB,CAAC,OAAO,QAAQ,QAAQ;AACxD,WAAO,QAAQ,SAAS,QAAQ,IAAI;AACpC,WAAO,iBAAiB,IAAI,QAAQ;AAAA,EACtC;AACA,MAAI,QAAQ,IAAI,eAAe,CAAC,OAAO,QAAQ,KAAK;AAClD,WAAO,QAAQ,MAAM,QAAQ,IAAI;AACjC,WAAO,iBAAiB,IAAI,KAAK;AAAA,EACnC;AACA,MAAI,QAAQ,IAAI,gBAAgB,CAAC,OAAO,QAAQ,MAAM;AACpD,WAAO,QAAQ,OAAO,QAAQ,IAAI;AAClC,WAAO,iBAAiB,IAAI,MAAM;AAAA,EACpC;AAGA,MAAI,QAAQ,IAAI,kBAAkB,CAAC,OAAO,YAAY,QAAQ;AAC5D,WAAO,YAAY,SAAS,QAAQ,IAAI;AAAA,EAC1C;AACA,MAAI,QAAQ,IAAI,gBAAgB,CAAC,OAAO,YAAY,QAAQ;AAC1D,WAAO,YAAY,SAAS,QAAQ,IAAI;AAAA,EAC1C;AACA,MAAI,QAAQ,IAAI,gBAAgB,CAAC,OAAO,YAAY,QAAQ;AAC1D,WAAO,YAAY,SAAS,QAAQ,IAAI;AAAA,EAC1C;AACA,MAAI,QAAQ,IAAI,iBAAiB,CAAC,OAAO,YAAY,OAAO;AAC1D,WAAO,YAAY,QAAQ,QAAQ,IAAI;AAAA,EACzC;AAIA,MAAI,QAAQ,IAAI,gBAAgB,QAAW;AACzC,UAAM,gBAAgB,CAAC,QAAQ,KAAK,KAAK,EAAE,SAAS,QAAQ,IAAI,YAAY,YAAY,CAAC;AACzF,WAAO,OAAO,UAAU;AAAA,EAC1B;AAEA,SAAO;AACT;AAncA,IA0IM,gBAoBA;AA9JN;AAAA;AAAA;AAAA;AA0IA,IAAM,iBAAmC;AAAA,MACvC,kBAAkB,oBAAI,IAAI,CAAC,WAAW,CAAC;AAAA;AAAA,MACvC,SAAS,CAAC;AAAA,MACV,WAAW,CAAC;AAAA,MACZ,qBAAqB;AAAA,MACrB,aAAa,CAAC;AAAA,MACd,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,UAAU;AAAA,UACR,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAKA,IAAM,qBAAqBA,MAAK,QAAQ,GAAG,eAAe,aAAa;AAAA;AAAA;","names":["existsSync","readFileSync","writeFileSync","readFileSync","existsSync","join"]}