aemeathcli 1.0.10 → 1.0.12
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/LICENSE +21 -0
- package/README.md +66 -54
- package/dist/App-JQ622M66.js +4431 -0
- package/dist/App-JQ622M66.js.map +1 -0
- package/dist/agent-store/architect.md +32 -0
- package/dist/agent-store/debugger.md +32 -0
- package/dist/agent-store/developer.md +29 -0
- package/dist/agent-store/documenter.md +30 -0
- package/dist/agent-store/researcher.md +31 -0
- package/dist/agent-store/reviewer.md +28 -0
- package/dist/agent-store/supervisor.md +37 -0
- package/dist/agent-store/tester.md +30 -0
- package/dist/api-key-fallback-RJLPM3KH.js +11 -0
- package/dist/{api-key-fallback-YQQBOQIL.js.map → api-key-fallback-RJLPM3KH.js.map} +1 -1
- package/dist/auth-status-JQJOKUPF.js +13 -0
- package/dist/auth-status-JQJOKUPF.js.map +1 -0
- package/dist/{chunk-RWCNNAL7.js → chunk-2KMA5RBC.js} +25 -48
- package/dist/chunk-2KMA5RBC.js.map +1 -0
- package/dist/{chunk-CYQNBB25.js → chunk-2Y7TR6BS.js} +28 -5
- package/dist/chunk-2Y7TR6BS.js.map +1 -0
- package/dist/{chunk-DAHGLHNR.js → chunk-2ZYK5IJG.js} +6 -141
- package/dist/chunk-2ZYK5IJG.js.map +1 -0
- package/dist/chunk-36RXCZOV.js +88 -0
- package/dist/chunk-36RXCZOV.js.map +1 -0
- package/dist/{chunk-DMBPX3RG.js → chunk-7EBLXPL4.js} +9 -9
- package/dist/{chunk-DMBPX3RG.js.map → chunk-7EBLXPL4.js.map} +1 -1
- package/dist/chunk-BIMQL4AG.js +186 -0
- package/dist/chunk-BIMQL4AG.js.map +1 -0
- package/dist/{chunk-NBR3GHMT.js → chunk-D275MCIH.js} +39 -7
- package/dist/chunk-D275MCIH.js.map +1 -0
- package/dist/{chunk-Y5XVD2CD.js → chunk-FFS4T7BZ.js} +109 -82
- package/dist/chunk-FFS4T7BZ.js.map +1 -0
- package/dist/{chunk-CARHU3DO.js → chunk-GXAJGP2T.js} +64 -16
- package/dist/chunk-GXAJGP2T.js.map +1 -0
- package/dist/{chunk-I5PZ4JTS.js → chunk-HESQLCLU.js} +4 -4
- package/dist/{chunk-I5PZ4JTS.js.map → chunk-HESQLCLU.js.map} +1 -1
- package/dist/{chunk-JAXXTYID.js → chunk-IR5HLBMH.js} +2 -2
- package/dist/{chunk-JAXXTYID.js.map → chunk-IR5HLBMH.js.map} +1 -1
- package/dist/{chunk-MFBHNWGV.js → chunk-K2FCMRXH.js} +11 -19
- package/dist/chunk-K2FCMRXH.js.map +1 -0
- package/dist/{chunk-H66O5Z2V.js → chunk-KIC7UI5U.js} +41 -6
- package/dist/chunk-KIC7UI5U.js.map +1 -0
- package/dist/{chunk-MXZSI3AY.js → chunk-KMOAJRDE.js} +42 -10
- package/dist/chunk-KMOAJRDE.js.map +1 -0
- package/dist/chunk-LQBALETG.js +71 -0
- package/dist/chunk-LQBALETG.js.map +1 -0
- package/dist/chunk-M3FPQSRU.js +12 -0
- package/dist/chunk-M3FPQSRU.js.map +1 -0
- package/dist/chunk-NQEUK763.js +26 -0
- package/dist/chunk-NQEUK763.js.map +1 -0
- package/dist/chunk-OPWAFS6Y.js +38 -0
- package/dist/chunk-OPWAFS6Y.js.map +1 -0
- package/dist/{chunk-6PDJ45T4.js → chunk-PS4WEFW6.js} +50 -25
- package/dist/chunk-PS4WEFW6.js.map +1 -0
- package/dist/{chunk-HMJRPNPZ.js → chunk-QK7TKNHV.js} +93 -21
- package/dist/chunk-QK7TKNHV.js.map +1 -0
- package/dist/{chunk-LSOYPSAT.js → chunk-RADJSEG5.js} +4 -4
- package/dist/chunk-RADJSEG5.js.map +1 -0
- package/dist/{chunk-4IJD72YB.js → chunk-SNWPI6XJ.js} +7 -7
- package/dist/chunk-SNWPI6XJ.js.map +1 -0
- package/dist/{chunk-TEVZS4FA.js → chunk-UM7MSLOV.js} +16 -9
- package/dist/chunk-UM7MSLOV.js.map +1 -0
- package/dist/chunk-VNZ3YTQD.js +232 -0
- package/dist/chunk-VNZ3YTQD.js.map +1 -0
- package/dist/{chunk-IYW62KKR.js → chunk-WXIN65UG.js} +66 -23
- package/dist/chunk-WXIN65UG.js.map +1 -0
- package/dist/chunk-XEXWX7C7.js +241 -0
- package/dist/chunk-XEXWX7C7.js.map +1 -0
- package/dist/{chunk-CGEV3ARR.js → chunk-YCCYXDW7.js} +3 -3
- package/dist/chunk-YCCYXDW7.js.map +1 -0
- package/dist/chunk-YPQ2MLAV.js +140 -0
- package/dist/chunk-YPQ2MLAV.js.map +1 -0
- package/dist/chunk-ZCOVMVK4.js +26 -0
- package/dist/chunk-ZCOVMVK4.js.map +1 -0
- package/dist/{claude-login-5WELXPKT.js → claude-login-AIFIWTYF.js} +9 -9
- package/dist/{claude-login-5WELXPKT.js.map → claude-login-AIFIWTYF.js.map} +1 -1
- package/dist/cli.js +370 -171
- package/dist/cli.js.map +1 -1
- package/dist/{codex-login-GZIFXUWD.js → codex-login-LW5X7GAM.js} +10 -10
- package/dist/codex-login-LW5X7GAM.js.map +1 -0
- package/dist/config-store-NF56VHFU.js +7 -0
- package/dist/{config-store-W6FBCQAQ.js.map → config-store-NF56VHFU.js.map} +1 -1
- package/dist/conversation-store-7GRDQZD2.js +4 -0
- package/dist/conversation-store-7GRDQZD2.js.map +1 -0
- package/dist/detect-providers-QICJ5U3R.js +4 -0
- package/dist/detect-providers-QICJ5U3R.js.map +1 -0
- package/dist/executor-FTABX2AW.js +4 -0
- package/dist/{executor-6RIKIGXK.js.map → executor-FTABX2AW.js.map} +1 -1
- package/dist/first-run-ADROZVYF.js +230 -0
- package/dist/first-run-ADROZVYF.js.map +1 -0
- package/dist/{gemini-login-AZGL3CE7.js → gemini-login-TST454MX.js} +9 -9
- package/dist/{gemini-login-AZGL3CE7.js.map → gemini-login-TST454MX.js.map} +1 -1
- package/dist/index.d.ts +46 -70
- package/dist/index.js +79 -468
- package/dist/index.js.map +1 -1
- package/dist/input-history-BEICE7PT.js +57 -0
- package/dist/input-history-BEICE7PT.js.map +1 -0
- package/dist/kimi-adapter-7FYOAKOI.js +6 -0
- package/dist/{kimi-adapter-JN4HFFHU.js.map → kimi-adapter-7FYOAKOI.js.map} +1 -1
- package/dist/{kimi-login-6LUWB7P6.js → kimi-login-3IGVOBJI.js} +9 -9
- package/dist/{kimi-login-6LUWB7P6.js.map → kimi-login-3IGVOBJI.js.map} +1 -1
- package/dist/logger-KGHUQ4VE.js +3 -0
- package/dist/logger-KGHUQ4VE.js.map +1 -0
- package/dist/model-discovery-AAJDHRFO.js +6 -0
- package/dist/model-discovery-AAJDHRFO.js.map +1 -0
- package/dist/native-cli-adapters-CLONTZOA.js +8 -0
- package/dist/{native-cli-adapters-OLW3XX57.js.map → native-cli-adapters-CLONTZOA.js.map} +1 -1
- package/dist/ollama-adapter-2N5OQIEV.js +5 -0
- package/dist/{ollama-adapter-OJQ3FKWK.js.map → ollama-adapter-2N5OQIEV.js.map} +1 -1
- package/dist/pathResolver-UVAB2FCW.js +3 -0
- package/dist/pathResolver-UVAB2FCW.js.map +1 -0
- package/dist/profile-loader-EMLV4J7S.js +162 -0
- package/dist/profile-loader-EMLV4J7S.js.map +1 -0
- package/dist/registry-LRURZVUL.js +5 -0
- package/dist/{registry-AZ2LOHHJ.js.map → registry-LRURZVUL.js.map} +1 -1
- package/dist/registry-MVNSXCEF.js +6 -0
- package/dist/{registry-H7B3AHPQ.js.map → registry-MVNSXCEF.js.map} +1 -1
- package/dist/server-manager-THGZBBZB.js +5 -0
- package/dist/{server-manager-PTGBHCLS.js.map → server-manager-THGZBBZB.js.map} +1 -1
- package/dist/session-manager-X3DXT53M.js +12 -0
- package/dist/{session-manager-XOMDMC77.js.map → session-manager-X3DXT53M.js.map} +1 -1
- package/dist/skills/built-in/code-review/SKILL.md +85 -0
- package/dist/skills/built-in/commit/SKILL.md +83 -0
- package/dist/skills/built-in/debug/SKILL.md +119 -0
- package/dist/skills/built-in/plan/SKILL.md +123 -0
- package/dist/skills/built-in/refactor/SKILL.md +132 -0
- package/dist/skills/built-in/test/SKILL.md +128 -0
- package/dist/sqlite-store-7OECRTXM.js +5 -0
- package/dist/sqlite-store-7OECRTXM.js.map +1 -0
- package/dist/team-manager-2VSMALAA.js +11 -0
- package/dist/{team-manager-HC4XGCFY.js.map → team-manager-2VSMALAA.js.map} +1 -1
- package/dist/team-state-HZNVMQHT.js +3 -0
- package/dist/team-state-HZNVMQHT.js.map +1 -0
- package/dist/tmux-manager-57QCUVHU.js +6 -0
- package/dist/{tmux-manager-GPYZ3WQH.js.map → tmux-manager-57QCUVHU.js.map} +1 -1
- package/dist/tools-KWFSYT56.js +6 -0
- package/dist/{tools-TSMXMHIF.js.map → tools-KWFSYT56.js.map} +1 -1
- package/package.json +11 -11
- package/dist/App-FKRSMFMB.js +0 -2789
- package/dist/App-FKRSMFMB.js.map +0 -1
- package/dist/api-key-fallback-YQQBOQIL.js +0 -11
- package/dist/chunk-4IJD72YB.js.map +0 -1
- package/dist/chunk-6PDJ45T4.js.map +0 -1
- package/dist/chunk-CARHU3DO.js.map +0 -1
- package/dist/chunk-CGEV3ARR.js.map +0 -1
- package/dist/chunk-CS5X3BWX.js +0 -27
- package/dist/chunk-CS5X3BWX.js.map +0 -1
- package/dist/chunk-CYQNBB25.js.map +0 -1
- package/dist/chunk-DAHGLHNR.js.map +0 -1
- package/dist/chunk-H66O5Z2V.js.map +0 -1
- package/dist/chunk-HMJRPNPZ.js.map +0 -1
- package/dist/chunk-IYW62KKR.js.map +0 -1
- package/dist/chunk-LSOYPSAT.js.map +0 -1
- package/dist/chunk-MFBHNWGV.js.map +0 -1
- package/dist/chunk-MXZSI3AY.js.map +0 -1
- package/dist/chunk-NBR3GHMT.js.map +0 -1
- package/dist/chunk-RWCNNAL7.js.map +0 -1
- package/dist/chunk-TEVZS4FA.js.map +0 -1
- package/dist/chunk-UY2SYSEZ.js +0 -211
- package/dist/chunk-UY2SYSEZ.js.map +0 -1
- package/dist/chunk-WAHVZH7V.js +0 -260
- package/dist/chunk-WAHVZH7V.js.map +0 -1
- package/dist/chunk-WPP3PEDE.js +0 -234
- package/dist/chunk-WPP3PEDE.js.map +0 -1
- package/dist/chunk-Y5XVD2CD.js.map +0 -1
- package/dist/claude-adapter-QMLFMSP3.js +0 -6
- package/dist/claude-adapter-QMLFMSP3.js.map +0 -1
- package/dist/codex-login-GZIFXUWD.js.map +0 -1
- package/dist/config-store-W6FBCQAQ.js +0 -6
- package/dist/executor-6RIKIGXK.js +0 -4
- package/dist/gemini-adapter-6JIHZ7WI.js +0 -6
- package/dist/gemini-adapter-6JIHZ7WI.js.map +0 -1
- package/dist/kimi-adapter-JN4HFFHU.js +0 -6
- package/dist/native-cli-adapters-OLW3XX57.js +0 -6
- package/dist/ollama-adapter-OJQ3FKWK.js +0 -6
- package/dist/openai-adapter-XU46EN7B.js +0 -6
- package/dist/openai-adapter-XU46EN7B.js.map +0 -1
- package/dist/registry-AZ2LOHHJ.js +0 -6
- package/dist/registry-H7B3AHPQ.js +0 -5
- package/dist/server-manager-PTGBHCLS.js +0 -5
- package/dist/session-manager-XOMDMC77.js +0 -12
- package/dist/team-manager-HC4XGCFY.js +0 -11
- package/dist/tmux-manager-GPYZ3WQH.js +0 -6
- package/dist/tools-TSMXMHIF.js +0 -6
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { SUPPORTED_MODELS, PROVIDER_MODEL_ORDER } from './chunk-HCIHOHLX.js';
|
|
2
|
+
import { logger } from './chunk-IR5HLBMH.js';
|
|
3
|
+
import { getAemeathHome } from './chunk-D275MCIH.js';
|
|
4
|
+
import { readFile } from 'fs/promises';
|
|
5
|
+
import { join, dirname } from 'path';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
import { execa } from 'execa';
|
|
9
|
+
|
|
10
|
+
var discoveryComplete = false;
|
|
11
|
+
var dynamicModels = {};
|
|
12
|
+
var dynamicDisplayOrder = {};
|
|
13
|
+
function makeModelInfo(id, name, provider, description, contextWindow) {
|
|
14
|
+
return {
|
|
15
|
+
id,
|
|
16
|
+
name,
|
|
17
|
+
provider,
|
|
18
|
+
contextWindow: contextWindow ?? 2e5,
|
|
19
|
+
maxOutputTokens: 16384,
|
|
20
|
+
inputPricePerMToken: 0,
|
|
21
|
+
outputPricePerMToken: 0,
|
|
22
|
+
supportsStreaming: true,
|
|
23
|
+
supportsToolCalling: true,
|
|
24
|
+
supportedRoles: ["coding"],
|
|
25
|
+
description
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async function discoverCodexModels() {
|
|
29
|
+
const configPath = join(homedir(), ".codex", "config.toml");
|
|
30
|
+
let currentModel;
|
|
31
|
+
try {
|
|
32
|
+
const raw = await readFile(configPath, "utf-8");
|
|
33
|
+
const match = raw.match(/^model\s*=\s*"([^"]+)"/m);
|
|
34
|
+
if (match?.[1]) {
|
|
35
|
+
currentModel = match[1];
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const { stdout } = await execa("codex", ["exec", "--help"], {
|
|
41
|
+
timeout: 5e3,
|
|
42
|
+
stdin: "ignore",
|
|
43
|
+
env: { ...process.env, NO_COLOR: "1" }
|
|
44
|
+
});
|
|
45
|
+
const modelRefs = stdout.matchAll(/model="([^"]+)"/g);
|
|
46
|
+
for (const m of modelRefs) {
|
|
47
|
+
const id = m[1];
|
|
48
|
+
if (id && !SUPPORTED_MODELS[id] && !dynamicModels[id]) {
|
|
49
|
+
addModel(id, id, "openai");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
if (currentModel && !SUPPORTED_MODELS[currentModel] && !dynamicModels[currentModel]) {
|
|
55
|
+
addModel(currentModel, currentModel, "openai", `User's current Codex model`);
|
|
56
|
+
}
|
|
57
|
+
const knownCodexModels = [
|
|
58
|
+
{ id: "gpt-5.4", name: "GPT-5.4", desc: "Latest frontier agentic coding model" },
|
|
59
|
+
{ id: "gpt-5.4-mini", name: "GPT-5.4 Mini", desc: "Smaller frontier agentic coding model" },
|
|
60
|
+
{ id: "o3", name: "o3", desc: "OpenAI o3 reasoning model" },
|
|
61
|
+
{ id: "o4-mini", name: "o4-mini", desc: "OpenAI o4-mini reasoning model" }
|
|
62
|
+
];
|
|
63
|
+
for (const m of knownCodexModels) {
|
|
64
|
+
if (!SUPPORTED_MODELS[m.id] && !dynamicModels[m.id]) {
|
|
65
|
+
addModel(m.id, m.name, "openai", m.desc);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function discoverGeminiModels() {
|
|
70
|
+
const geminiModelsPath = await findGeminiModelsFile();
|
|
71
|
+
if (geminiModelsPath) {
|
|
72
|
+
try {
|
|
73
|
+
const source = await readFile(geminiModelsPath, "utf-8");
|
|
74
|
+
const modelMatches = source.matchAll(/['"]([^'"]*gemini[^'"]+)['"]/g);
|
|
75
|
+
for (const m of modelMatches) {
|
|
76
|
+
const id = m[1];
|
|
77
|
+
if (id && !id.includes("embedding") && !id.includes("auto-") && !id.includes("customtools") && !SUPPORTED_MODELS[id] && !dynamicModels[id]) {
|
|
78
|
+
const name = id.replace(/-preview$/, " Preview").replace(/-lite$/, " Lite").split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
|
|
79
|
+
addModel(id, name, "google", void 0, 2e6);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
logger.debug("Failed to read Gemini CLI models source");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const knownGeminiModels = [
|
|
87
|
+
{ id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro Preview", desc: "Latest Gemini Pro preview" },
|
|
88
|
+
{ id: "gemini-3.1-flash-lite-preview", name: "Gemini 3.1 Flash Lite Preview", desc: "Latest Flash Lite preview" },
|
|
89
|
+
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash Preview", desc: "Gemini 3 Flash preview" }
|
|
90
|
+
];
|
|
91
|
+
for (const m of knownGeminiModels) {
|
|
92
|
+
if (!SUPPORTED_MODELS[m.id] && !dynamicModels[m.id]) {
|
|
93
|
+
addModel(m.id, m.name, "google", m.desc, 2e6);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function findGeminiModelsFile() {
|
|
98
|
+
try {
|
|
99
|
+
const { stdout } = await execa("which", ["gemini"], {
|
|
100
|
+
stdin: "ignore",
|
|
101
|
+
timeout: 5e3,
|
|
102
|
+
env: { ...process.env, NO_COLOR: "1" }
|
|
103
|
+
});
|
|
104
|
+
const whichResult = stdout.trim();
|
|
105
|
+
if (whichResult) {
|
|
106
|
+
const binDir = dirname(whichResult);
|
|
107
|
+
const modelsPath = join(
|
|
108
|
+
binDir,
|
|
109
|
+
"..",
|
|
110
|
+
"lib",
|
|
111
|
+
"node_modules",
|
|
112
|
+
"@google",
|
|
113
|
+
"gemini-cli",
|
|
114
|
+
"node_modules",
|
|
115
|
+
"@google",
|
|
116
|
+
"gemini-cli-core",
|
|
117
|
+
"dist",
|
|
118
|
+
"src",
|
|
119
|
+
"config",
|
|
120
|
+
"models.js"
|
|
121
|
+
);
|
|
122
|
+
if (existsSync(modelsPath)) {
|
|
123
|
+
return modelsPath;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
return void 0;
|
|
129
|
+
}
|
|
130
|
+
async function discoverClaudeModels() {
|
|
131
|
+
try {
|
|
132
|
+
await execa("claude", ["--version"], {
|
|
133
|
+
timeout: 5e3,
|
|
134
|
+
stdin: "ignore",
|
|
135
|
+
env: { ...process.env, NO_COLOR: "1" }
|
|
136
|
+
});
|
|
137
|
+
} catch {
|
|
138
|
+
logger.debug("Claude CLI not available");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function loadUserModels() {
|
|
142
|
+
const modelsPath = join(getAemeathHome(), "models.json");
|
|
143
|
+
try {
|
|
144
|
+
const raw = await readFile(modelsPath, "utf-8");
|
|
145
|
+
const parsed = JSON.parse(raw);
|
|
146
|
+
if (!Array.isArray(parsed)) return;
|
|
147
|
+
for (const entry of parsed) {
|
|
148
|
+
if (typeof entry === "object" && entry !== null && typeof entry.id === "string" && typeof entry.name === "string" && typeof entry.provider === "string") {
|
|
149
|
+
const e = entry;
|
|
150
|
+
addModel(
|
|
151
|
+
e.id,
|
|
152
|
+
e.name,
|
|
153
|
+
e.provider,
|
|
154
|
+
e.description,
|
|
155
|
+
e.contextWindow
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function addModel(id, name, provider, description, contextWindow) {
|
|
163
|
+
dynamicModels[id] = makeModelInfo(id, name, provider, description, contextWindow);
|
|
164
|
+
if (!dynamicDisplayOrder[provider]) {
|
|
165
|
+
dynamicDisplayOrder[provider] = [];
|
|
166
|
+
}
|
|
167
|
+
if (!dynamicDisplayOrder[provider].some((e) => e.id === id)) {
|
|
168
|
+
dynamicDisplayOrder[provider].push({
|
|
169
|
+
id,
|
|
170
|
+
label: name,
|
|
171
|
+
description: description ?? ""
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function discoverModels() {
|
|
176
|
+
if (discoveryComplete) return;
|
|
177
|
+
await Promise.allSettled([
|
|
178
|
+
loadUserModels(),
|
|
179
|
+
discoverClaudeModels(),
|
|
180
|
+
discoverCodexModels(),
|
|
181
|
+
discoverGeminiModels()
|
|
182
|
+
]);
|
|
183
|
+
discoveryComplete = true;
|
|
184
|
+
const count = Object.keys(dynamicModels).length;
|
|
185
|
+
if (count > 0) {
|
|
186
|
+
logger.info({ count }, "Discovered additional models at runtime");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function registerModel(model) {
|
|
190
|
+
dynamicModels[model.id] = model;
|
|
191
|
+
const provider = model.provider;
|
|
192
|
+
if (!dynamicDisplayOrder[provider]) {
|
|
193
|
+
dynamicDisplayOrder[provider] = [];
|
|
194
|
+
}
|
|
195
|
+
if (!dynamicDisplayOrder[provider].some((e) => e.id === model.id)) {
|
|
196
|
+
dynamicDisplayOrder[provider].push({
|
|
197
|
+
id: model.id,
|
|
198
|
+
label: model.name,
|
|
199
|
+
description: model.description ?? ""
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function getModelInfo(modelId) {
|
|
204
|
+
return dynamicModels[modelId] ?? SUPPORTED_MODELS[modelId];
|
|
205
|
+
}
|
|
206
|
+
function getAllModels() {
|
|
207
|
+
return { ...SUPPORTED_MODELS, ...dynamicModels };
|
|
208
|
+
}
|
|
209
|
+
function getDisplayOrder() {
|
|
210
|
+
const result = {};
|
|
211
|
+
for (const [provider, entries] of Object.entries(PROVIDER_MODEL_ORDER)) {
|
|
212
|
+
result[provider] = [...entries];
|
|
213
|
+
}
|
|
214
|
+
for (const [provider, entries] of Object.entries(dynamicDisplayOrder)) {
|
|
215
|
+
if (!result[provider]) {
|
|
216
|
+
result[provider] = [];
|
|
217
|
+
}
|
|
218
|
+
for (const entry of entries) {
|
|
219
|
+
if (!result[provider].some((e) => e.id === entry.id)) {
|
|
220
|
+
result[provider].unshift(entry);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
function isKnownModel(modelId) {
|
|
227
|
+
return modelId in SUPPORTED_MODELS || modelId in dynamicModels;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export { discoverModels, getAllModels, getDisplayOrder, getModelInfo, isKnownModel, registerModel };
|
|
231
|
+
//# sourceMappingURL=chunk-VNZ3YTQD.js.map
|
|
232
|
+
//# sourceMappingURL=chunk-VNZ3YTQD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/model-discovery.ts"],"names":[],"mappings":"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","file":"chunk-VNZ3YTQD.js","sourcesContent":["/**\n * Runtime model discovery — reads real model lists from provider CLIs\n * and user config. Merges with the hardcoded fallback registry.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { existsSync } from \"node:fs\";\nimport { execa } from \"execa\";\nimport { logger } from \"../utils/logger.js\";\nimport { getAemeathHome } from \"../utils/pathResolver.js\";\nimport {\n SUPPORTED_MODELS,\n PROVIDER_MODEL_ORDER,\n type IModelInfo,\n type IModelDisplayEntry,\n type ProviderName,\n} from \"../types/model.js\";\n\n// ── Cache ────────────────────────────────────────────────────────────────\n\nlet discoveryComplete = false;\nconst dynamicModels: Record<string, IModelInfo> = {};\nconst dynamicDisplayOrder: Record<string, IModelDisplayEntry[]> = {};\n\n// ── Helper to create a model entry with defaults ────────────────────────\n\nfunction makeModelInfo(\n id: string,\n name: string,\n provider: ProviderName,\n description?: string,\n contextWindow?: number,\n): IModelInfo {\n return {\n id,\n name,\n provider,\n contextWindow: contextWindow ?? 200_000,\n maxOutputTokens: 16_384,\n inputPricePerMToken: 0,\n outputPricePerMToken: 0,\n supportsStreaming: true,\n supportsToolCalling: true,\n supportedRoles: [\"coding\"],\n description,\n };\n}\n\n// ── Codex (OpenAI) discovery ────────────────────────────────────────────\n\nasync function discoverCodexModels(): Promise<void> {\n // 1. Read user's current model from ~/.codex/config.toml\n const configPath = join(homedir(), \".codex\", \"config.toml\");\n let currentModel: string | undefined;\n try {\n const raw = await readFile(configPath, \"utf-8\");\n const match = raw.match(/^model\\s*=\\s*\"([^\"]+)\"/m);\n if (match?.[1]) {\n currentModel = match[1];\n }\n } catch {\n // No config file\n }\n\n // 2. Try to get the interactive model list via `codex model` (needs TTY, may fail)\n // Fallback: run `codex exec --help` and extract model references\n try {\n const { stdout } = await execa(\"codex\", [\"exec\", \"--help\"], {\n timeout: 5000,\n stdin: \"ignore\",\n env: { ...process.env, NO_COLOR: \"1\" },\n });\n // Extract model IDs from help text (e.g. model=\"o3\")\n const modelRefs = stdout.matchAll(/model=\"([^\"]+)\"/g);\n for (const m of modelRefs) {\n const id = m[1];\n if (id && !SUPPORTED_MODELS[id] && !dynamicModels[id]) {\n addModel(id, id, \"openai\");\n }\n }\n } catch {\n // Codex not installed\n }\n\n // 3. If the user's current model isn't in our list, add it\n if (currentModel && !SUPPORTED_MODELS[currentModel] && !dynamicModels[currentModel]) {\n addModel(currentModel, currentModel, \"openai\", `User's current Codex model`);\n }\n\n // 4. Probe for known recent Codex models that may not be in our hardcoded list\n const knownCodexModels = [\n { id: \"gpt-5.4\", name: \"GPT-5.4\", desc: \"Latest frontier agentic coding model\" },\n { id: \"gpt-5.4-mini\", name: \"GPT-5.4 Mini\", desc: \"Smaller frontier agentic coding model\" },\n { id: \"o3\", name: \"o3\", desc: \"OpenAI o3 reasoning model\" },\n { id: \"o4-mini\", name: \"o4-mini\", desc: \"OpenAI o4-mini reasoning model\" },\n ];\n for (const m of knownCodexModels) {\n if (!SUPPORTED_MODELS[m.id] && !dynamicModels[m.id]) {\n addModel(m.id, m.name, \"openai\", m.desc);\n }\n }\n}\n\n// ── Gemini (Google) discovery ───────────────────────────────────────────\n\nasync function discoverGeminiModels(): Promise<void> {\n // 1. Read the Gemini CLI's models.js source for VALID_GEMINI_MODELS\n const geminiModelsPath = await findGeminiModelsFile();\n if (geminiModelsPath) {\n try {\n const source = await readFile(geminiModelsPath, \"utf-8\");\n\n // Extract model IDs from export const statements and Set entries\n const modelMatches = source.matchAll(/['\"]([^'\"]*gemini[^'\"]+)['\"]/g);\n for (const m of modelMatches) {\n const id = m[1];\n if (\n id &&\n !id.includes(\"embedding\") &&\n !id.includes(\"auto-\") &&\n !id.includes(\"customtools\") &&\n !SUPPORTED_MODELS[id] &&\n !dynamicModels[id]\n ) {\n const name = id\n .replace(/-preview$/, \" Preview\")\n .replace(/-lite$/, \" Lite\")\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(\" \");\n addModel(id, name, \"google\", undefined, 2_000_000);\n }\n }\n } catch {\n logger.debug(\"Failed to read Gemini CLI models source\");\n }\n }\n\n // 2. Fallback: probe known recent Gemini models\n const knownGeminiModels = [\n { id: \"gemini-3.1-pro-preview\", name: \"Gemini 3.1 Pro Preview\", desc: \"Latest Gemini Pro preview\" },\n { id: \"gemini-3.1-flash-lite-preview\", name: \"Gemini 3.1 Flash Lite Preview\", desc: \"Latest Flash Lite preview\" },\n { id: \"gemini-3-flash-preview\", name: \"Gemini 3 Flash Preview\", desc: \"Gemini 3 Flash preview\" },\n ];\n for (const m of knownGeminiModels) {\n if (!SUPPORTED_MODELS[m.id] && !dynamicModels[m.id]) {\n addModel(m.id, m.name, \"google\", m.desc, 2_000_000);\n }\n }\n}\n\n/** Find the Gemini CLI's models.js file in node_modules. */\nasync function findGeminiModelsFile(): Promise<string | undefined> {\n // Try to find via which gemini → resolve symlink → find models.js\n try {\n const { stdout } = await execa(\"which\", [\"gemini\"], {\n stdin: \"ignore\",\n timeout: 5_000,\n env: { ...process.env, NO_COLOR: \"1\" },\n });\n const whichResult = stdout.trim();\n if (whichResult) {\n // gemini binary is at e.g. /Users/x/.nvm/versions/node/v22.18.0/bin/gemini\n // models.js is at .../lib/node_modules/@google/gemini-cli/node_modules/@google/gemini-cli-core/dist/src/config/models.js\n const binDir = dirname(whichResult);\n const modelsPath = join(\n binDir,\n \"..\",\n \"lib\",\n \"node_modules\",\n \"@google\",\n \"gemini-cli\",\n \"node_modules\",\n \"@google\",\n \"gemini-cli-core\",\n \"dist\",\n \"src\",\n \"config\",\n \"models.js\",\n );\n if (existsSync(modelsPath)) {\n return modelsPath;\n }\n }\n } catch {\n // which not available or gemini not installed\n }\n\n return undefined;\n}\n\n// ── Claude (Anthropic) discovery ────────────────────────────────────────\n\nasync function discoverClaudeModels(): Promise<void> {\n // Claude models are well-known and change infrequently.\n // The hardcoded list is accurate. Just verify claude CLI exists.\n try {\n await execa(\"claude\", [\"--version\"], {\n timeout: 5000,\n stdin: \"ignore\",\n env: { ...process.env, NO_COLOR: \"1\" },\n });\n } catch {\n logger.debug(\"Claude CLI not available\");\n }\n}\n\n// ── User-defined models from ~/.aemeathcli/models.json ─────────────────\n\ninterface IUserModelEntry {\n readonly id: string;\n readonly name: string;\n readonly provider: string;\n readonly description?: string;\n readonly contextWindow?: number;\n}\n\nasync function loadUserModels(): Promise<void> {\n const modelsPath = join(getAemeathHome(), \"models.json\");\n try {\n const raw = await readFile(modelsPath, \"utf-8\");\n const parsed = JSON.parse(raw) as unknown;\n if (!Array.isArray(parsed)) return;\n\n for (const entry of parsed) {\n if (\n typeof entry === \"object\" &&\n entry !== null &&\n typeof (entry as IUserModelEntry).id === \"string\" &&\n typeof (entry as IUserModelEntry).name === \"string\" &&\n typeof (entry as IUserModelEntry).provider === \"string\"\n ) {\n const e = entry as IUserModelEntry;\n addModel(\n e.id,\n e.name,\n e.provider as ProviderName,\n e.description,\n e.contextWindow,\n );\n }\n }\n } catch {\n // File doesn't exist or invalid\n }\n}\n\n// ── Internal helpers ────────────────────────────────────────────────────\n\nfunction addModel(\n id: string,\n name: string,\n provider: ProviderName,\n description?: string,\n contextWindow?: number,\n): void {\n dynamicModels[id] = makeModelInfo(id, name, provider, description, contextWindow);\n\n if (!dynamicDisplayOrder[provider]) {\n dynamicDisplayOrder[provider] = [];\n }\n if (!dynamicDisplayOrder[provider].some((e) => e.id === id)) {\n dynamicDisplayOrder[provider].push({\n id,\n label: name,\n description: description ?? \"\",\n });\n }\n}\n\n// ── Public API ──────────────────────────────────────────────────────────\n\nexport async function discoverModels(): Promise<void> {\n if (discoveryComplete) return;\n\n await Promise.allSettled([\n loadUserModels(),\n discoverClaudeModels(),\n discoverCodexModels(),\n discoverGeminiModels(),\n ]);\n\n discoveryComplete = true;\n const count = Object.keys(dynamicModels).length;\n if (count > 0) {\n logger.info({ count }, \"Discovered additional models at runtime\");\n }\n}\n\nexport function registerModel(model: IModelInfo): void {\n dynamicModels[model.id] = model;\n const provider = model.provider;\n if (!dynamicDisplayOrder[provider]) {\n dynamicDisplayOrder[provider] = [];\n }\n if (!dynamicDisplayOrder[provider].some((e) => e.id === model.id)) {\n dynamicDisplayOrder[provider].push({\n id: model.id,\n label: model.name,\n description: model.description ?? \"\",\n });\n }\n}\n\nexport function getModelInfo(modelId: string): IModelInfo | undefined {\n return dynamicModels[modelId] ?? SUPPORTED_MODELS[modelId];\n}\n\nexport function getAllModels(): Record<string, IModelInfo> {\n return { ...SUPPORTED_MODELS, ...dynamicModels };\n}\n\nexport function getDisplayOrder(): Record<string, readonly IModelDisplayEntry[]> {\n const result: Record<string, IModelDisplayEntry[]> = {};\n\n for (const [provider, entries] of Object.entries(PROVIDER_MODEL_ORDER)) {\n result[provider] = [...entries];\n }\n\n // Prepend dynamic models to their provider group (new models show at top)\n for (const [provider, entries] of Object.entries(dynamicDisplayOrder)) {\n if (!result[provider]) {\n result[provider] = [];\n }\n for (const entry of entries) {\n if (!result[provider].some((e) => e.id === entry.id)) {\n result[provider].unshift(entry);\n }\n }\n }\n\n return result;\n}\n\nexport function isKnownModel(modelId: string): boolean {\n return modelId in SUPPORTED_MODELS || modelId in dynamicModels;\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { logger } from './chunk-
|
|
1
|
+
import { CLI_PROVIDERS } from './chunk-M3FPQSRU.js';
|
|
2
|
+
import { DEFAULT_CONFIG } from './chunk-2Y7TR6BS.js';
|
|
3
|
+
import { logger } from './chunk-IR5HLBMH.js';
|
|
4
|
+
import { getConfigPath, getProjectConfigPath, ensureDirectory } from './chunk-D275MCIH.js';
|
|
4
5
|
import { existsSync, readFileSync, writeFileSync, unwatchFile, watchFile } from 'fs';
|
|
5
6
|
import { dirname } from 'path';
|
|
6
7
|
import { z } from 'zod';
|
|
@@ -8,44 +9,51 @@ import { z } from 'zod';
|
|
|
8
9
|
var ProviderConfigSchema = z.object({
|
|
9
10
|
enabled: z.boolean(),
|
|
10
11
|
baseUrl: z.string().optional()
|
|
11
|
-
});
|
|
12
|
+
}).strict();
|
|
12
13
|
var PermissionConfigSchema = z.object({
|
|
13
14
|
mode: z.enum(["strict", "standard", "permissive"]),
|
|
14
15
|
allowedPaths: z.array(z.string()),
|
|
15
16
|
blockedCommands: z.array(z.string())
|
|
16
|
-
});
|
|
17
|
+
}).strict();
|
|
17
18
|
var SplitPanelConfigSchema = z.object({
|
|
18
19
|
enabled: z.boolean(),
|
|
19
20
|
backend: z.enum(["tmux", "iterm2"]),
|
|
20
|
-
defaultLayout: z.enum(["auto", "horizontal", "vertical", "grid"]),
|
|
21
|
+
defaultLayout: z.enum(["auto", "horizontal", "vertical", "grid", "hub-spoke"]),
|
|
21
22
|
maxPanes: z.number().int().min(1).max(16)
|
|
22
|
-
});
|
|
23
|
+
}).strict();
|
|
24
|
+
var CLI_PROVIDER_ENUM_VALUES = [...CLI_PROVIDERS];
|
|
25
|
+
var SwarmConfigSchema = z.object({
|
|
26
|
+
onboardingComplete: z.boolean(),
|
|
27
|
+
detectedProviders: z.array(z.enum(CLI_PROVIDER_ENUM_VALUES)),
|
|
28
|
+
primaryMasterProvider: z.enum(CLI_PROVIDER_ENUM_VALUES).optional(),
|
|
29
|
+
fallbackMasterProviders: z.array(z.enum(CLI_PROVIDER_ENUM_VALUES))
|
|
30
|
+
}).strict();
|
|
23
31
|
var CostConfigSchema = z.object({
|
|
24
32
|
budgetWarning: z.number().nonnegative(),
|
|
25
33
|
budgetHardStop: z.number().nonnegative(),
|
|
26
34
|
currency: z.string()
|
|
27
|
-
});
|
|
35
|
+
}).strict();
|
|
28
36
|
var TelemetryConfigSchema = z.object({
|
|
29
37
|
enabled: z.boolean(),
|
|
30
38
|
anonymized: z.boolean()
|
|
31
|
-
});
|
|
39
|
+
}).strict();
|
|
32
40
|
var RoleConfigSchema = z.object({
|
|
33
41
|
primary: z.string(),
|
|
34
42
|
fallback: z.array(z.string())
|
|
35
|
-
});
|
|
43
|
+
}).strict();
|
|
36
44
|
var OAuthProviderConfigSchema = z.object({
|
|
37
45
|
clientId: z.string(),
|
|
38
46
|
clientSecret: z.string().optional(),
|
|
39
47
|
authorizeUrl: z.string().optional(),
|
|
40
48
|
tokenUrl: z.string().optional(),
|
|
41
49
|
scope: z.string().optional()
|
|
42
|
-
});
|
|
50
|
+
}).strict();
|
|
43
51
|
var OAuthConfigSchema = z.object({
|
|
44
52
|
anthropic: OAuthProviderConfigSchema.optional(),
|
|
45
53
|
openai: OAuthProviderConfigSchema.optional(),
|
|
46
54
|
google: OAuthProviderConfigSchema.optional(),
|
|
47
55
|
kimi: OAuthProviderConfigSchema.optional()
|
|
48
|
-
});
|
|
56
|
+
}).strict();
|
|
49
57
|
var GlobalConfigSchema = z.object({
|
|
50
58
|
version: z.string(),
|
|
51
59
|
defaultModel: z.string(),
|
|
@@ -53,10 +61,14 @@ var GlobalConfigSchema = z.object({
|
|
|
53
61
|
providers: z.record(z.string(), ProviderConfigSchema).optional(),
|
|
54
62
|
permissions: PermissionConfigSchema.optional(),
|
|
55
63
|
splitPanel: SplitPanelConfigSchema.optional(),
|
|
64
|
+
swarm: SwarmConfigSchema.optional(),
|
|
56
65
|
cost: CostConfigSchema.optional(),
|
|
57
66
|
telemetry: TelemetryConfigSchema.optional(),
|
|
58
67
|
oauth: OAuthConfigSchema.optional()
|
|
59
|
-
});
|
|
68
|
+
}).strict();
|
|
69
|
+
function formatValidationError(error) {
|
|
70
|
+
return error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
|
|
71
|
+
}
|
|
60
72
|
var ConfigStore = class {
|
|
61
73
|
globalConfig = DEFAULT_CONFIG;
|
|
62
74
|
projectConfig;
|
|
@@ -77,8 +89,17 @@ var ConfigStore = class {
|
|
|
77
89
|
this.rebuildMergedConfig();
|
|
78
90
|
return this.mergedConfig;
|
|
79
91
|
}
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
const raw = readFileSync(resolvedPath, "utf-8");
|
|
95
|
+
parsed = JSON.parse(raw);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
98
|
+
logger.warn({ error: message, path: resolvedPath }, "Global config parse failed, using defaults");
|
|
99
|
+
this.globalConfig = DEFAULT_CONFIG;
|
|
100
|
+
this.rebuildMergedConfig();
|
|
101
|
+
return this.mergedConfig;
|
|
102
|
+
}
|
|
82
103
|
const validated = GlobalConfigSchema.safeParse(parsed);
|
|
83
104
|
if (!validated.success) {
|
|
84
105
|
logger.warn(
|
|
@@ -105,8 +126,17 @@ var ConfigStore = class {
|
|
|
105
126
|
this.rebuildMergedConfig();
|
|
106
127
|
return this.mergedConfig;
|
|
107
128
|
}
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
let parsed;
|
|
130
|
+
try {
|
|
131
|
+
const raw = readFileSync(resolvedPath, "utf-8");
|
|
132
|
+
parsed = JSON.parse(raw);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
135
|
+
logger.warn({ error: message, path: resolvedPath }, "Project config parse failed, ignoring");
|
|
136
|
+
this.projectConfig = void 0;
|
|
137
|
+
this.rebuildMergedConfig();
|
|
138
|
+
return this.mergedConfig;
|
|
139
|
+
}
|
|
110
140
|
const validated = GlobalConfigSchema.partial().safeParse(parsed);
|
|
111
141
|
if (!validated.success) {
|
|
112
142
|
logger.warn(
|
|
@@ -125,21 +155,29 @@ var ConfigStore = class {
|
|
|
125
155
|
saveGlobal(config, configPath) {
|
|
126
156
|
const resolvedPath = configPath ?? getConfigPath();
|
|
127
157
|
const configToSave = config ?? this.globalConfig;
|
|
158
|
+
const validated = GlobalConfigSchema.safeParse(configToSave);
|
|
159
|
+
if (!validated.success) {
|
|
160
|
+
throw new Error(`Invalid global config: ${formatValidationError(validated.error)}`);
|
|
161
|
+
}
|
|
128
162
|
ensureDirectory(dirname(resolvedPath));
|
|
129
|
-
const json = JSON.stringify(
|
|
163
|
+
const json = JSON.stringify(validated.data, null, 2);
|
|
130
164
|
writeFileSync(resolvedPath, json, { encoding: "utf-8", mode: 384 });
|
|
131
165
|
logger.info({ path: resolvedPath }, "Global config saved");
|
|
132
166
|
if (config) {
|
|
133
|
-
this.globalConfig =
|
|
167
|
+
this.globalConfig = this.applyDefaults(validated.data);
|
|
134
168
|
this.rebuildMergedConfig();
|
|
135
169
|
}
|
|
136
170
|
}
|
|
137
171
|
saveProject(projectRoot, config) {
|
|
138
172
|
const resolvedPath = getProjectConfigPath(projectRoot);
|
|
173
|
+
const validated = GlobalConfigSchema.partial().safeParse(config);
|
|
174
|
+
if (!validated.success) {
|
|
175
|
+
throw new Error(`Invalid project config: ${formatValidationError(validated.error)}`);
|
|
176
|
+
}
|
|
139
177
|
ensureDirectory(dirname(resolvedPath));
|
|
140
|
-
const json = JSON.stringify(
|
|
178
|
+
const json = JSON.stringify(validated.data, null, 2);
|
|
141
179
|
writeFileSync(resolvedPath, json, { encoding: "utf-8", mode: 384 });
|
|
142
|
-
this.projectConfig =
|
|
180
|
+
this.projectConfig = validated.data;
|
|
143
181
|
this.rebuildMergedConfig();
|
|
144
182
|
logger.info({ path: resolvedPath }, "Project config saved");
|
|
145
183
|
}
|
|
@@ -209,6 +247,10 @@ var ConfigStore = class {
|
|
|
209
247
|
...this.globalConfig.splitPanel,
|
|
210
248
|
...this.projectConfig.splitPanel ?? {}
|
|
211
249
|
},
|
|
250
|
+
swarm: {
|
|
251
|
+
...this.globalConfig.swarm,
|
|
252
|
+
...this.projectConfig.swarm ?? {}
|
|
253
|
+
},
|
|
212
254
|
cost: {
|
|
213
255
|
...this.globalConfig.cost,
|
|
214
256
|
...this.projectConfig.cost ?? {}
|
|
@@ -243,6 +285,7 @@ var ConfigStore = class {
|
|
|
243
285
|
},
|
|
244
286
|
permissions: partial.permissions ? partial.permissions : DEFAULT_CONFIG.permissions,
|
|
245
287
|
splitPanel: partial.splitPanel ? partial.splitPanel : DEFAULT_CONFIG.splitPanel,
|
|
288
|
+
swarm: partial.swarm ? partial.swarm : DEFAULT_CONFIG.swarm,
|
|
246
289
|
cost: partial.cost ? partial.cost : DEFAULT_CONFIG.cost,
|
|
247
290
|
telemetry: partial.telemetry ? partial.telemetry : DEFAULT_CONFIG.telemetry,
|
|
248
291
|
oauth: partial.oauth ? partial.oauth : void 0
|
|
@@ -251,5 +294,5 @@ var ConfigStore = class {
|
|
|
251
294
|
};
|
|
252
295
|
|
|
253
296
|
export { ConfigStore };
|
|
254
|
-
//# sourceMappingURL=chunk-
|
|
255
|
-
//# sourceMappingURL=chunk-
|
|
297
|
+
//# sourceMappingURL=chunk-WXIN65UG.js.map
|
|
298
|
+
//# sourceMappingURL=chunk-WXIN65UG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/config-store.ts"],"names":[],"mappings":"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","file":"chunk-WXIN65UG.js","sourcesContent":["/**\n * Configuration store — PRD sections 17.2, 17.3\n * Loads/saves global and project config with Zod validation.\n * Merges project config over global config.\n * Watches for config file changes.\n */\n\nimport { readFileSync, writeFileSync, watchFile, unwatchFile } from \"node:fs\";\nimport { existsSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { z } from \"zod\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n getConfigPath,\n getProjectConfigPath,\n ensureDirectory,\n} from \"../utils/pathResolver.js\";\nimport { DEFAULT_CONFIG } from \"../types/config.js\";\nimport type { IGlobalConfig } from \"../types/config.js\";\nimport { CLI_PROVIDERS, type CliProviderType } from \"../orchestrator/constants.js\";\n\n// ── Zod Schemas ─────────────────────────────────────────────────────────\n\nconst ProviderConfigSchema = z.object({\n enabled: z.boolean(),\n baseUrl: z.string().optional(),\n}).strict();\n\nconst PermissionConfigSchema = z.object({\n mode: z.enum([\"strict\", \"standard\", \"permissive\"]),\n allowedPaths: z.array(z.string()),\n blockedCommands: z.array(z.string()),\n}).strict();\n\nconst SplitPanelConfigSchema = z.object({\n enabled: z.boolean(),\n backend: z.enum([\"tmux\", \"iterm2\"]),\n defaultLayout: z.enum([\"auto\", \"horizontal\", \"vertical\", \"grid\", \"hub-spoke\"]),\n maxPanes: z.number().int().min(1).max(16),\n}).strict();\n\nconst CLI_PROVIDER_ENUM_VALUES = [...CLI_PROVIDERS] as [\n CliProviderType,\n ...CliProviderType[],\n];\n\nconst SwarmConfigSchema = z.object({\n onboardingComplete: z.boolean(),\n detectedProviders: z.array(z.enum(CLI_PROVIDER_ENUM_VALUES)),\n primaryMasterProvider: z.enum(CLI_PROVIDER_ENUM_VALUES).optional(),\n fallbackMasterProviders: z.array(z.enum(CLI_PROVIDER_ENUM_VALUES)),\n}).strict();\n\nconst CostConfigSchema = z.object({\n budgetWarning: z.number().nonnegative(),\n budgetHardStop: z.number().nonnegative(),\n currency: z.string(),\n}).strict();\n\nconst TelemetryConfigSchema = z.object({\n enabled: z.boolean(),\n anonymized: z.boolean(),\n}).strict();\n\nconst RoleConfigSchema = z.object({\n primary: z.string(),\n fallback: z.array(z.string()),\n}).strict();\n\nconst OAuthProviderConfigSchema = z.object({\n clientId: z.string(),\n clientSecret: z.string().optional(),\n authorizeUrl: z.string().optional(),\n tokenUrl: z.string().optional(),\n scope: z.string().optional(),\n}).strict();\n\nconst OAuthConfigSchema = z.object({\n anthropic: OAuthProviderConfigSchema.optional(),\n openai: OAuthProviderConfigSchema.optional(),\n google: OAuthProviderConfigSchema.optional(),\n kimi: OAuthProviderConfigSchema.optional(),\n}).strict();\n\nconst GlobalConfigSchema = z.object({\n version: z.string(),\n defaultModel: z.string(),\n roles: z.record(z.string(), RoleConfigSchema).optional(),\n providers: z.record(z.string(), ProviderConfigSchema).optional(),\n permissions: PermissionConfigSchema.optional(),\n splitPanel: SplitPanelConfigSchema.optional(),\n swarm: SwarmConfigSchema.optional(),\n cost: CostConfigSchema.optional(),\n telemetry: TelemetryConfigSchema.optional(),\n oauth: OAuthConfigSchema.optional(),\n}).strict();\n\nfunction formatValidationError(error: z.ZodError): string {\n return error.issues\n .map((issue) => `${issue.path.join(\".\") || \"<root>\"}: ${issue.message}`)\n .join(\"; \");\n}\n\ntype ConfigChangeCallback = (config: IGlobalConfig) => void;\n\nexport class ConfigStore {\n private globalConfig: IGlobalConfig = DEFAULT_CONFIG;\n private projectConfig: Partial<IGlobalConfig> | undefined;\n private mergedConfig: IGlobalConfig = DEFAULT_CONFIG;\n private watchers: Array<{ path: string; active: boolean }> = [];\n private changeCallbacks: ConfigChangeCallback[] = [];\n\n get config(): IGlobalConfig {\n return this.mergedConfig;\n }\n\n loadGlobal(configPath?: string): IGlobalConfig {\n const resolvedPath = configPath ?? getConfigPath();\n\n if (!existsSync(resolvedPath)) {\n logger.info(\n { path: resolvedPath },\n \"Global config not found, using defaults\",\n );\n this.globalConfig = DEFAULT_CONFIG;\n this.rebuildMergedConfig();\n return this.mergedConfig;\n }\n\n let parsed: unknown;\n try {\n const raw = readFileSync(resolvedPath, \"utf-8\");\n parsed = JSON.parse(raw);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n logger.warn({ error: message, path: resolvedPath }, \"Global config parse failed, using defaults\");\n this.globalConfig = DEFAULT_CONFIG;\n this.rebuildMergedConfig();\n return this.mergedConfig;\n }\n const validated = GlobalConfigSchema.safeParse(parsed);\n\n if (!validated.success) {\n logger.warn(\n { errors: validated.error.issues },\n \"Global config validation failed, using defaults\",\n );\n this.globalConfig = DEFAULT_CONFIG;\n this.rebuildMergedConfig();\n return this.mergedConfig;\n }\n\n this.globalConfig = this.applyDefaults(validated.data);\n this.rebuildMergedConfig();\n\n logger.info({ path: resolvedPath }, \"Global config loaded\");\n return this.mergedConfig;\n }\n\n loadProject(projectRoot: string): IGlobalConfig {\n const resolvedPath = getProjectConfigPath(projectRoot);\n\n if (!existsSync(resolvedPath)) {\n logger.debug(\n { path: resolvedPath },\n \"Project config not found, using global only\",\n );\n this.projectConfig = undefined;\n this.rebuildMergedConfig();\n return this.mergedConfig;\n }\n\n let parsed: unknown;\n try {\n const raw = readFileSync(resolvedPath, \"utf-8\");\n parsed = JSON.parse(raw);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n logger.warn({ error: message, path: resolvedPath }, \"Project config parse failed, ignoring\");\n this.projectConfig = undefined;\n this.rebuildMergedConfig();\n return this.mergedConfig;\n }\n const validated = GlobalConfigSchema.partial().safeParse(parsed);\n\n if (!validated.success) {\n logger.warn(\n { errors: validated.error.issues },\n \"Project config validation failed, ignoring\",\n );\n this.projectConfig = undefined;\n this.rebuildMergedConfig();\n return this.mergedConfig;\n }\n\n this.projectConfig = validated.data as Partial<IGlobalConfig>;\n this.rebuildMergedConfig();\n\n logger.info({ path: resolvedPath }, \"Project config loaded\");\n return this.mergedConfig;\n }\n\n saveGlobal(\n config?: IGlobalConfig,\n configPath?: string,\n ): void {\n const resolvedPath = configPath ?? getConfigPath();\n const configToSave = config ?? this.globalConfig;\n const validated = GlobalConfigSchema.safeParse(configToSave);\n\n if (!validated.success) {\n throw new Error(`Invalid global config: ${formatValidationError(validated.error)}`);\n }\n\n ensureDirectory(dirname(resolvedPath));\n const json = JSON.stringify(validated.data, null, 2);\n writeFileSync(resolvedPath, json, { encoding: \"utf-8\", mode: 0o600 });\n\n logger.info({ path: resolvedPath }, \"Global config saved\");\n\n if (config) {\n this.globalConfig = this.applyDefaults(validated.data);\n this.rebuildMergedConfig();\n }\n }\n\n saveProject(\n projectRoot: string,\n config: Partial<IGlobalConfig>,\n ): void {\n const resolvedPath = getProjectConfigPath(projectRoot);\n const validated = GlobalConfigSchema.partial().safeParse(config);\n\n if (!validated.success) {\n throw new Error(`Invalid project config: ${formatValidationError(validated.error)}`);\n }\n\n ensureDirectory(dirname(resolvedPath));\n const json = JSON.stringify(validated.data, null, 2);\n writeFileSync(resolvedPath, json, { encoding: \"utf-8\", mode: 0o600 });\n\n this.projectConfig = validated.data as Partial<IGlobalConfig>;\n this.rebuildMergedConfig();\n\n logger.info({ path: resolvedPath }, \"Project config saved\");\n }\n\n watchGlobal(configPath?: string): void {\n const resolvedPath = configPath ?? getConfigPath();\n this.watchConfigFile(resolvedPath, () => {\n this.loadGlobal(resolvedPath);\n });\n }\n\n watchProject(projectRoot: string): void {\n const resolvedPath = getProjectConfigPath(projectRoot);\n this.watchConfigFile(resolvedPath, () => {\n this.loadProject(projectRoot);\n });\n }\n\n onChange(callback: ConfigChangeCallback): void {\n this.changeCallbacks.push(callback);\n }\n\n stopWatching(): void {\n for (const watcher of this.watchers) {\n if (watcher.active) {\n unwatchFile(watcher.path);\n watcher.active = false;\n }\n }\n this.watchers = [];\n }\n\n private watchConfigFile(filePath: string, onUpdate: () => void): void {\n if (!existsSync(filePath)) {\n return;\n }\n\n const entry = { path: filePath, active: true };\n this.watchers.push(entry);\n\n watchFile(filePath, { interval: 2000 }, () => {\n if (!entry.active) {\n return;\n }\n logger.info({ path: filePath }, \"Config file changed, reloading\");\n try {\n onUpdate();\n } catch (error: unknown) {\n const message =\n error instanceof Error ? error.message : String(error);\n logger.error({ error: message }, \"Failed to reload config\");\n }\n });\n }\n\n private rebuildMergedConfig(): void {\n if (!this.projectConfig) {\n this.mergedConfig = { ...this.globalConfig };\n } else {\n this.mergedConfig = {\n ...this.globalConfig,\n ...this.projectConfig,\n roles: {\n ...this.globalConfig.roles,\n ...(this.projectConfig.roles ?? {}),\n },\n providers: {\n ...this.globalConfig.providers,\n ...(this.projectConfig.providers ?? {}),\n },\n permissions: {\n ...this.globalConfig.permissions,\n ...(this.projectConfig.permissions ?? {}),\n },\n splitPanel: {\n ...this.globalConfig.splitPanel,\n ...(this.projectConfig.splitPanel ?? {}),\n },\n swarm: {\n ...this.globalConfig.swarm,\n ...(this.projectConfig.swarm ?? {}),\n },\n cost: {\n ...this.globalConfig.cost,\n ...(this.projectConfig.cost ?? {}),\n },\n telemetry: {\n ...this.globalConfig.telemetry,\n ...(this.projectConfig.telemetry ?? {}),\n },\n oauth: this.projectConfig.oauth ?? this.globalConfig.oauth,\n };\n }\n\n for (const cb of this.changeCallbacks) {\n try {\n cb(this.mergedConfig);\n } catch (error: unknown) {\n const message =\n error instanceof Error ? error.message : String(error);\n logger.error({ error: message }, \"Config change callback failed\");\n }\n }\n }\n\n private applyDefaults(\n partial: z.infer<typeof GlobalConfigSchema>,\n ): IGlobalConfig {\n return {\n version: partial.version,\n defaultModel: partial.defaultModel,\n roles: {\n ...DEFAULT_CONFIG.roles,\n ...(partial.roles as IGlobalConfig[\"roles\"] | undefined),\n },\n providers: {\n ...DEFAULT_CONFIG.providers,\n ...(partial.providers as IGlobalConfig[\"providers\"] | undefined),\n },\n permissions: partial.permissions\n ? (partial.permissions as IGlobalConfig[\"permissions\"])\n : DEFAULT_CONFIG.permissions,\n splitPanel: partial.splitPanel\n ? (partial.splitPanel as IGlobalConfig[\"splitPanel\"])\n : DEFAULT_CONFIG.splitPanel,\n swarm: partial.swarm\n ? (partial.swarm as IGlobalConfig[\"swarm\"])\n : DEFAULT_CONFIG.swarm,\n cost: partial.cost\n ? (partial.cost as IGlobalConfig[\"cost\"])\n : DEFAULT_CONFIG.cost,\n telemetry: partial.telemetry\n ? (partial.telemetry as IGlobalConfig[\"telemetry\"])\n : DEFAULT_CONFIG.telemetry,\n oauth: partial.oauth\n ? (partial.oauth as IGlobalConfig[\"oauth\"])\n : undefined,\n };\n }\n}\n"]}
|