@heroor/x 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/index.js +599 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Herox contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join, parse } from "node:path";
|
|
5
|
+
//#region ../providers/dist/index.js
|
|
6
|
+
var ProviderError = class extends Error {
|
|
7
|
+
status;
|
|
8
|
+
code;
|
|
9
|
+
type;
|
|
10
|
+
constructor(message, options = {}) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "ProviderError";
|
|
13
|
+
this.status = options.status;
|
|
14
|
+
this.code = options.code;
|
|
15
|
+
this.type = options.type;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const providerPresets = [
|
|
19
|
+
{
|
|
20
|
+
name: "openai",
|
|
21
|
+
displayName: "OpenAI",
|
|
22
|
+
baseUrl: "https://api.openai.com/v1",
|
|
23
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
24
|
+
defaultModel: "gpt-5.5",
|
|
25
|
+
compatibility: "openai-chat-completions"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "deepseek",
|
|
29
|
+
displayName: "DeepSeek",
|
|
30
|
+
baseUrl: "https://api.deepseek.com",
|
|
31
|
+
apiKeyEnv: "DEEPSEEK_API_KEY",
|
|
32
|
+
defaultModel: "deepseek-v4-pro",
|
|
33
|
+
compatibility: "openai-chat-completions"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "qwen",
|
|
37
|
+
displayName: "Qwen",
|
|
38
|
+
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
39
|
+
apiKeyEnv: "QWEN_API_KEY",
|
|
40
|
+
defaultModel: "qwen-plus",
|
|
41
|
+
compatibility: "openai-chat-completions"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "moonshot",
|
|
45
|
+
displayName: "Moonshot",
|
|
46
|
+
baseUrl: "https://api.moonshot.cn/v1",
|
|
47
|
+
apiKeyEnv: "MOONSHOT_API_KEY",
|
|
48
|
+
defaultModel: "moonshot-v1-8k",
|
|
49
|
+
compatibility: "openai-chat-completions"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "mimo",
|
|
53
|
+
displayName: "Mimo",
|
|
54
|
+
baseUrl: "https://api.xiaomimimo.com/v1",
|
|
55
|
+
apiKeyEnv: "MIMO_API_KEY",
|
|
56
|
+
defaultModel: "mimo-v2.5",
|
|
57
|
+
compatibility: "openai-chat-completions"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "openrouter",
|
|
61
|
+
displayName: "OpenRouter",
|
|
62
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
63
|
+
apiKeyEnv: "OPENROUTER_API_KEY",
|
|
64
|
+
defaultModel: "deepseek/deepseek-v4-flash:free",
|
|
65
|
+
compatibility: "openai-chat-completions"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "ollama",
|
|
69
|
+
displayName: "Ollama",
|
|
70
|
+
baseUrl: "http://localhost:11434/v1",
|
|
71
|
+
defaultModel: "llama3.1",
|
|
72
|
+
compatibility: "partial"
|
|
73
|
+
}
|
|
74
|
+
];
|
|
75
|
+
function listProviderPresets() {
|
|
76
|
+
return [...providerPresets];
|
|
77
|
+
}
|
|
78
|
+
function getProviderPreset(name) {
|
|
79
|
+
return providerPresets.find((preset) => preset.name === name);
|
|
80
|
+
}
|
|
81
|
+
function resolveProviderConnection(options) {
|
|
82
|
+
const config = options.config ?? {};
|
|
83
|
+
const env = options.env ?? process.env;
|
|
84
|
+
const providerName = options.providerName ?? config.model?.provider ?? "openai";
|
|
85
|
+
const preset = getProviderPreset(providerName);
|
|
86
|
+
const override = config.providers?.[providerName];
|
|
87
|
+
if (preset === void 0 && override === void 0) throw new ProviderError(`Unknown provider "${providerName}".`);
|
|
88
|
+
const baseUrl = override?.baseURL ?? override?.baseUrl ?? preset?.baseUrl;
|
|
89
|
+
if (baseUrl === void 0) throw new ProviderError(`Provider "${providerName}" is missing baseURL.`);
|
|
90
|
+
const apiKeyEnv = override?.apiKeyEnv ?? preset?.apiKeyEnv;
|
|
91
|
+
const apiKey = override?.apiKey ?? (apiKeyEnv !== void 0 ? env[apiKeyEnv] : void 0);
|
|
92
|
+
const model = override?.model ?? config.model?.name ?? override?.defaultModel ?? preset?.defaultModel;
|
|
93
|
+
const temperature = override?.temperature ?? config.model?.temperature;
|
|
94
|
+
const maxOutputTokens = override?.maxOutputTokens ?? config.model?.maxOutputTokens;
|
|
95
|
+
if (model === void 0) throw new ProviderError(`Provider "${providerName}" is missing a model.`);
|
|
96
|
+
return {
|
|
97
|
+
provider: providerName,
|
|
98
|
+
baseUrl,
|
|
99
|
+
model,
|
|
100
|
+
temperature,
|
|
101
|
+
maxOutputTokens,
|
|
102
|
+
apiKey,
|
|
103
|
+
apiKeyEnv,
|
|
104
|
+
headers: override?.headers,
|
|
105
|
+
compatibility: preset?.compatibility ?? "partial"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async function createChatCompletion(connection, options, fetchLike = defaultFetch) {
|
|
109
|
+
const response = await fetchLike(joinUrl(connection.baseUrl, "chat/completions"), {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: buildHeaders(connection),
|
|
112
|
+
body: JSON.stringify({
|
|
113
|
+
model: options.model ?? connection.model,
|
|
114
|
+
messages: options.messages,
|
|
115
|
+
temperature: options.temperature ?? connection.temperature,
|
|
116
|
+
max_tokens: options.maxOutputTokens ?? connection.maxOutputTokens,
|
|
117
|
+
stream: false
|
|
118
|
+
}),
|
|
119
|
+
signal: options.signal
|
|
120
|
+
});
|
|
121
|
+
if (!response.ok) throw await providerErrorFromResponse(response);
|
|
122
|
+
const raw = await response.json();
|
|
123
|
+
return {
|
|
124
|
+
content: extractAssistantContent(raw),
|
|
125
|
+
raw
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function testProviderConnection(connection, fetchLike = defaultFetch) {
|
|
129
|
+
if (connection.apiKeyEnv !== void 0 && connection.apiKey === void 0) return {
|
|
130
|
+
status: "error",
|
|
131
|
+
message: `Missing API key. Set ${connection.apiKeyEnv} and retry.`
|
|
132
|
+
};
|
|
133
|
+
try {
|
|
134
|
+
const result = await createChatCompletion(connection, {
|
|
135
|
+
messages: [{
|
|
136
|
+
role: "user",
|
|
137
|
+
content: "Reply with OK."
|
|
138
|
+
}],
|
|
139
|
+
temperature: connection.temperature ?? 0,
|
|
140
|
+
maxOutputTokens: connection.maxOutputTokens ?? 8
|
|
141
|
+
}, fetchLike);
|
|
142
|
+
return {
|
|
143
|
+
status: "ok",
|
|
144
|
+
message: result.content.length > 0 ? `Model responded: ${result.content}` : "Model responded."
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return {
|
|
148
|
+
status: "error",
|
|
149
|
+
message: error instanceof Error ? error.message : "Provider test failed."
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function buildHeaders(connection) {
|
|
154
|
+
const headers = {
|
|
155
|
+
"Content-Type": "application/json",
|
|
156
|
+
...connection.headers ?? {}
|
|
157
|
+
};
|
|
158
|
+
if (connection.apiKey !== void 0) headers.Authorization = `Bearer ${connection.apiKey}`;
|
|
159
|
+
return headers;
|
|
160
|
+
}
|
|
161
|
+
function extractAssistantContent(raw) {
|
|
162
|
+
return readNestedString(raw, [
|
|
163
|
+
"choices",
|
|
164
|
+
"0",
|
|
165
|
+
"message",
|
|
166
|
+
"content"
|
|
167
|
+
]) ?? "";
|
|
168
|
+
}
|
|
169
|
+
function readNestedString(value, path) {
|
|
170
|
+
let current = value;
|
|
171
|
+
for (const key of path) {
|
|
172
|
+
if (Array.isArray(current)) {
|
|
173
|
+
current = current[Number.parseInt(key, 10)];
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (!isRecord$1(current)) return;
|
|
177
|
+
current = current[key];
|
|
178
|
+
}
|
|
179
|
+
return typeof current === "string" ? current : void 0;
|
|
180
|
+
}
|
|
181
|
+
async function providerErrorFromResponse(response) {
|
|
182
|
+
const parsed = parseJson(await response.text());
|
|
183
|
+
const error = isRecord$1(parsed) && isRecord$1(parsed.error) ? parsed.error : void 0;
|
|
184
|
+
return new ProviderError(readErrorMessage(error) ?? `${response.status} ${response.statusText}`, {
|
|
185
|
+
status: response.status,
|
|
186
|
+
code: readOptionalString(error, "code"),
|
|
187
|
+
type: readOptionalString(error, "type")
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function readErrorMessage(error) {
|
|
191
|
+
return readOptionalString(error, "message");
|
|
192
|
+
}
|
|
193
|
+
function readOptionalString(value, key) {
|
|
194
|
+
const entry = value?.[key];
|
|
195
|
+
return typeof entry === "string" ? entry : void 0;
|
|
196
|
+
}
|
|
197
|
+
function parseJson(text) {
|
|
198
|
+
try {
|
|
199
|
+
return JSON.parse(text);
|
|
200
|
+
} catch {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function joinUrl(baseUrl, path) {
|
|
205
|
+
return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
206
|
+
}
|
|
207
|
+
async function defaultFetch(url, init) {
|
|
208
|
+
return fetch(url, init);
|
|
209
|
+
}
|
|
210
|
+
function isRecord$1(value) {
|
|
211
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region ../shared/dist/index.js
|
|
215
|
+
function createTextBlock(lines) {
|
|
216
|
+
return `${lines.join("\n")}\n`;
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region ../tools/dist/index.js
|
|
220
|
+
const builtinTools = [
|
|
221
|
+
{
|
|
222
|
+
name: "fs.read",
|
|
223
|
+
description: "Read a UTF-8 text file from the current workspace.",
|
|
224
|
+
risk: "read"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: "fs.list",
|
|
228
|
+
description: "List files and directories under a workspace path.",
|
|
229
|
+
risk: "read"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: "fs.search",
|
|
233
|
+
description: "Search workspace files with ripgrep-compatible semantics.",
|
|
234
|
+
risk: "read"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: "fs.patch",
|
|
238
|
+
description: "Apply a structured patch to workspace files.",
|
|
239
|
+
risk: "write"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: "shell.exec",
|
|
243
|
+
description: "Run a local shell command after permission checks.",
|
|
244
|
+
risk: "execute"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: "git.status",
|
|
248
|
+
description: "Inspect git working tree status.",
|
|
249
|
+
risk: "read"
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: "git.diff",
|
|
253
|
+
description: "Inspect git diffs without changing repository state.",
|
|
254
|
+
risk: "read"
|
|
255
|
+
}
|
|
256
|
+
];
|
|
257
|
+
function listBuiltinTools() {
|
|
258
|
+
return [...builtinTools];
|
|
259
|
+
}
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region ../core/dist/index.js
|
|
262
|
+
const defaultConfig = {
|
|
263
|
+
model: { provider: "openai" },
|
|
264
|
+
providers: { openai: { apiKeyEnv: "OPENAI_API_KEY" } },
|
|
265
|
+
env: {}
|
|
266
|
+
};
|
|
267
|
+
function loadHeroxConfig(options = {}) {
|
|
268
|
+
const cwd = options.cwd ?? process.cwd();
|
|
269
|
+
const env = options.env ?? process.env;
|
|
270
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
271
|
+
const sourcePaths = [
|
|
272
|
+
{
|
|
273
|
+
label: "user",
|
|
274
|
+
path: join(options.homeDir ?? homedir(), ".herox", "settings.json")
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
label: "project",
|
|
278
|
+
path: join(workspaceRoot, ".herox", "settings.json")
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
label: "local",
|
|
282
|
+
path: join(workspaceRoot, ".herox", "settings.local.json")
|
|
283
|
+
}
|
|
284
|
+
];
|
|
285
|
+
let config = cloneConfig(defaultConfig);
|
|
286
|
+
const sources = [];
|
|
287
|
+
for (const source of sourcePaths) {
|
|
288
|
+
const loaded = readConfigSource(source.label, source.path);
|
|
289
|
+
sources.push(loaded.source);
|
|
290
|
+
if (loaded.config !== void 0) config = mergeHeroxConfig(config, loaded.config);
|
|
291
|
+
}
|
|
292
|
+
config = applyEnvironmentOverrides(config, env);
|
|
293
|
+
return {
|
|
294
|
+
config,
|
|
295
|
+
sources,
|
|
296
|
+
workspaceRoot
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function getConfigValue(config, path) {
|
|
300
|
+
if (path === void 0 || path.length === 0) return config;
|
|
301
|
+
return path.split(".").reduce((current, key) => {
|
|
302
|
+
if (!isRecord(current)) return;
|
|
303
|
+
return current[key];
|
|
304
|
+
}, config);
|
|
305
|
+
}
|
|
306
|
+
function mergeHeroxConfig(base, override) {
|
|
307
|
+
return {
|
|
308
|
+
model: {
|
|
309
|
+
...base.model,
|
|
310
|
+
...isRecord(override.model) ? stripUndefined(override.model) : {}
|
|
311
|
+
},
|
|
312
|
+
providers: mergeProviderConfigs(base.providers, override.providers),
|
|
313
|
+
env: {
|
|
314
|
+
...base.env,
|
|
315
|
+
...isStringRecord(override.env) ? override.env : {}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function applyEnvironmentOverrides(config, env) {
|
|
320
|
+
return mergeHeroxConfig(config, { model: stripUndefined({
|
|
321
|
+
provider: env.HEROX_PROVIDER,
|
|
322
|
+
name: env.HEROX_MODEL
|
|
323
|
+
}) });
|
|
324
|
+
}
|
|
325
|
+
function readConfigSource(label, path) {
|
|
326
|
+
if (!existsSync(path)) return { source: {
|
|
327
|
+
label,
|
|
328
|
+
path,
|
|
329
|
+
exists: false
|
|
330
|
+
} };
|
|
331
|
+
try {
|
|
332
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
333
|
+
if (!isRecord(parsed)) return { source: {
|
|
334
|
+
label,
|
|
335
|
+
path,
|
|
336
|
+
exists: true,
|
|
337
|
+
error: "Config file must contain a JSON object."
|
|
338
|
+
} };
|
|
339
|
+
return {
|
|
340
|
+
source: {
|
|
341
|
+
label,
|
|
342
|
+
path,
|
|
343
|
+
exists: true
|
|
344
|
+
},
|
|
345
|
+
config: parsed
|
|
346
|
+
};
|
|
347
|
+
} catch (error) {
|
|
348
|
+
return { source: {
|
|
349
|
+
label,
|
|
350
|
+
path,
|
|
351
|
+
exists: true,
|
|
352
|
+
error: error instanceof Error ? error.message : "Unknown config parse error."
|
|
353
|
+
} };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function findWorkspaceRoot(cwd) {
|
|
357
|
+
let current = cwd;
|
|
358
|
+
const root = parse(cwd).root;
|
|
359
|
+
while (current !== root) {
|
|
360
|
+
if (existsSync(join(current, ".git")) || existsSync(join(current, ".herox")) || existsSync(join(current, "HEROX.md"))) return current;
|
|
361
|
+
current = dirname(current);
|
|
362
|
+
}
|
|
363
|
+
return cwd;
|
|
364
|
+
}
|
|
365
|
+
function mergeProviderConfigs(base, override) {
|
|
366
|
+
if (!isRecord(override)) return { ...base };
|
|
367
|
+
const merged = { ...base };
|
|
368
|
+
for (const [name, value] of Object.entries(override)) {
|
|
369
|
+
if (!isRecord(value)) continue;
|
|
370
|
+
merged[name] = {
|
|
371
|
+
...merged[name] ?? {},
|
|
372
|
+
...stripUndefined(value)
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
return merged;
|
|
376
|
+
}
|
|
377
|
+
function cloneConfig(config) {
|
|
378
|
+
return {
|
|
379
|
+
model: { ...config.model },
|
|
380
|
+
providers: Object.fromEntries(Object.entries(config.providers).map(([name, provider]) => [name, { ...provider }])),
|
|
381
|
+
env: { ...config.env }
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function isStringRecord(value) {
|
|
385
|
+
return isRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
386
|
+
}
|
|
387
|
+
function stripUndefined(value) {
|
|
388
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
389
|
+
}
|
|
390
|
+
function isRecord(value) {
|
|
391
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
392
|
+
}
|
|
393
|
+
function buildDoctorReport(options) {
|
|
394
|
+
const nodeVersion = options.nodeVersion ?? process.version;
|
|
395
|
+
const nodeMajor = parseNodeMajor(nodeVersion);
|
|
396
|
+
const providerCount = listProviderPresets().length;
|
|
397
|
+
const toolCount = listBuiltinTools().length;
|
|
398
|
+
return {
|
|
399
|
+
title: "Herox doctor",
|
|
400
|
+
checks: [
|
|
401
|
+
{
|
|
402
|
+
name: "Node.js",
|
|
403
|
+
status: nodeMajor >= 20 ? "ok" : "error",
|
|
404
|
+
message: nodeMajor >= 20 ? `${nodeVersion} satisfies >=20` : `${nodeVersion} is unsupported; Herox requires Node.js >=20`
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: "Package",
|
|
408
|
+
status: "ok",
|
|
409
|
+
message: `@heroor/x ${options.version}`
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: "Workspace",
|
|
413
|
+
status: "ok",
|
|
414
|
+
message: options.cwd ?? process.cwd()
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "Provider presets",
|
|
418
|
+
status: providerCount > 0 ? "ok" : "warn",
|
|
419
|
+
message: `${providerCount} presets registered`
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
name: "Builtin tools",
|
|
423
|
+
status: toolCount > 0 ? "ok" : "warn",
|
|
424
|
+
message: `${toolCount} tools registered`
|
|
425
|
+
}
|
|
426
|
+
]
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function formatDoctorReport(report) {
|
|
430
|
+
return createTextBlock([
|
|
431
|
+
report.title,
|
|
432
|
+
"",
|
|
433
|
+
...report.checks.map((check) => {
|
|
434
|
+
return `${check.status.toUpperCase().padEnd(5, " ")} ${check.name}: ${check.message}`;
|
|
435
|
+
})
|
|
436
|
+
]);
|
|
437
|
+
}
|
|
438
|
+
function parseNodeMajor(version) {
|
|
439
|
+
const [major] = version.replace(/^v/, "").split(".");
|
|
440
|
+
const parsed = Number.parseInt(major ?? "", 10);
|
|
441
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
442
|
+
}
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/cli.ts
|
|
445
|
+
const helpText = createTextBlock([
|
|
446
|
+
"Herox Agent CLI",
|
|
447
|
+
"",
|
|
448
|
+
"Usage: herox [command] [options]",
|
|
449
|
+
"",
|
|
450
|
+
"Commands:",
|
|
451
|
+
" config get [path] Print effective config or a dot-path value",
|
|
452
|
+
" config paths Print config source paths",
|
|
453
|
+
" doctor Check local Herox runtime readiness",
|
|
454
|
+
" provider list List built-in provider presets",
|
|
455
|
+
" provider test [provider] Test an OpenAI-compatible provider",
|
|
456
|
+
" run <task> Execute a one-shot task (coming soon)",
|
|
457
|
+
" init Initialize project Herox files (coming soon)",
|
|
458
|
+
" resume [session] Resume a saved session (coming soon)",
|
|
459
|
+
"",
|
|
460
|
+
"Options:",
|
|
461
|
+
" -h, --help Show help",
|
|
462
|
+
" -v, --version Show version"
|
|
463
|
+
]);
|
|
464
|
+
async function runCli(args = process.argv.slice(2), io = {
|
|
465
|
+
stdout: process.stdout,
|
|
466
|
+
stderr: process.stderr
|
|
467
|
+
}, options = {}) {
|
|
468
|
+
const normalizedArgs = normalizeArgs(args);
|
|
469
|
+
const [command] = normalizedArgs;
|
|
470
|
+
const runtime = {
|
|
471
|
+
cwd: options.cwd ?? process.cwd(),
|
|
472
|
+
env: options.env ?? process.env,
|
|
473
|
+
homeDir: options.homeDir
|
|
474
|
+
};
|
|
475
|
+
if (command === void 0 || command === "-h" || command === "--help" || command === "help") {
|
|
476
|
+
io.stdout.write(helpText);
|
|
477
|
+
return 0;
|
|
478
|
+
}
|
|
479
|
+
if (command === "-v" || command === "--version" || command === "version") {
|
|
480
|
+
io.stdout.write(`${readPackageVersion()}\n`);
|
|
481
|
+
return 0;
|
|
482
|
+
}
|
|
483
|
+
if (command === "doctor") {
|
|
484
|
+
const report = buildDoctorReport({
|
|
485
|
+
cwd: runtime.cwd,
|
|
486
|
+
version: readPackageVersion()
|
|
487
|
+
});
|
|
488
|
+
io.stdout.write(formatDoctorReport(report));
|
|
489
|
+
return hasDoctorErrors(report.checks) ? 1 : 0;
|
|
490
|
+
}
|
|
491
|
+
if (command === "config") return handleConfigCommand(normalizedArgs.slice(1), io, runtime);
|
|
492
|
+
if (command === "provider") return handleProviderCommand(normalizedArgs.slice(1), io, runtime);
|
|
493
|
+
if (command === "run" || command === "init" || command === "resume") {
|
|
494
|
+
io.stderr.write(`herox "${command}" is not implemented yet.\n`);
|
|
495
|
+
return 2;
|
|
496
|
+
}
|
|
497
|
+
io.stderr.write(`Unknown command: "${command}"\n\n`);
|
|
498
|
+
io.stderr.write(helpText);
|
|
499
|
+
return 1;
|
|
500
|
+
}
|
|
501
|
+
function handleConfigCommand(args, io, runtime) {
|
|
502
|
+
const [subcommand, key] = args;
|
|
503
|
+
const loaded = loadHeroxConfig(runtime);
|
|
504
|
+
if (subcommand === "get" || subcommand === void 0) {
|
|
505
|
+
io.stdout.write(`${JSON.stringify(redactConfigValue(getConfigValue(loaded.config, key), key), null, 2)}\n`);
|
|
506
|
+
return 0;
|
|
507
|
+
}
|
|
508
|
+
if (subcommand === "paths") {
|
|
509
|
+
io.stdout.write(createTextBlock([`workspaceRoot: ${loaded.workspaceRoot}`, ...loaded.sources.map((source) => {
|
|
510
|
+
const state = source.exists ? "found" : "missing";
|
|
511
|
+
return `${source.label}: ${state} ${source.path}${source.error ? ` (${source.error})` : ""}`;
|
|
512
|
+
})]));
|
|
513
|
+
return loaded.sources.some((source) => source.error !== void 0) ? 1 : 0;
|
|
514
|
+
}
|
|
515
|
+
io.stderr.write(`Unknown config command: "${subcommand}"\n`);
|
|
516
|
+
return 1;
|
|
517
|
+
}
|
|
518
|
+
async function handleProviderCommand(args, io, runtime) {
|
|
519
|
+
const [subcommand, providerName] = args;
|
|
520
|
+
if (subcommand === "list" || subcommand === void 0) {
|
|
521
|
+
io.stdout.write(formatProviderList());
|
|
522
|
+
return 0;
|
|
523
|
+
}
|
|
524
|
+
if (subcommand === "test") {
|
|
525
|
+
const loaded = loadHeroxConfig(runtime);
|
|
526
|
+
try {
|
|
527
|
+
const connection = resolveProviderConnection({
|
|
528
|
+
config: loaded.config,
|
|
529
|
+
providerName,
|
|
530
|
+
env: mergeProviderEnv(loaded.config.env, runtime.env)
|
|
531
|
+
});
|
|
532
|
+
const result = await testProviderConnection(connection);
|
|
533
|
+
io.stdout.write(`${result.status.toUpperCase()} ${connection.provider}(${connection.model}): ${result.message}\n`);
|
|
534
|
+
return result.status === "ok" ? 0 : 1;
|
|
535
|
+
} catch (error) {
|
|
536
|
+
io.stderr.write(`${error instanceof Error ? error.message : "Provider test failed."}\n`);
|
|
537
|
+
return 1;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
io.stderr.write(`Unknown provider command: "${subcommand}"\n`);
|
|
541
|
+
return 1;
|
|
542
|
+
}
|
|
543
|
+
function mergeProviderEnv(configEnv, runtimeEnv) {
|
|
544
|
+
return {
|
|
545
|
+
...configEnv,
|
|
546
|
+
...runtimeEnv
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function formatProviderList() {
|
|
550
|
+
return createTextBlock([
|
|
551
|
+
"Provider presets",
|
|
552
|
+
"",
|
|
553
|
+
...listProviderPresets().map((provider) => {
|
|
554
|
+
const key = provider.apiKeyEnv === void 0 ? "no key" : provider.apiKeyEnv;
|
|
555
|
+
return `${provider.name.padEnd(12, " ")} ${provider.defaultModel.padEnd(22, " ")} ${key}`;
|
|
556
|
+
})
|
|
557
|
+
]);
|
|
558
|
+
}
|
|
559
|
+
function redactConfigValue(value, path) {
|
|
560
|
+
if (path !== void 0 && shouldRedactConfigPath(path, value)) return value === void 0 ? void 0 : "<redacted>";
|
|
561
|
+
if (Array.isArray(value)) return value.map((entry, index) => redactConfigValue(entry, appendConfigPath(path, String(index))));
|
|
562
|
+
if (typeof value === "object" && value !== null) return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, shouldRedactConfigPath(appendConfigPath(path, key), entry) ? "<redacted>" : redactConfigValue(entry, appendConfigPath(path, key))]));
|
|
563
|
+
return value;
|
|
564
|
+
}
|
|
565
|
+
function appendConfigPath(path, key) {
|
|
566
|
+
return path === void 0 || path.length === 0 ? key : `${path}.${key}`;
|
|
567
|
+
}
|
|
568
|
+
function shouldRedactConfigPath(path, value) {
|
|
569
|
+
const segments = path.split(".");
|
|
570
|
+
return isSecretKey(segments.at(-1) ?? "") || segments[0] === "env" && !isRedactableContainer(value);
|
|
571
|
+
}
|
|
572
|
+
function isRedactableContainer(value) {
|
|
573
|
+
return typeof value === "object" && value !== null;
|
|
574
|
+
}
|
|
575
|
+
function isSecretKey(key) {
|
|
576
|
+
const compact = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
577
|
+
return compact === "apikey" || compact === "authorization" || compact.endsWith("apikey") || compact.endsWith("token") || compact.endsWith("secret") || compact.endsWith("password");
|
|
578
|
+
}
|
|
579
|
+
function normalizeArgs(args) {
|
|
580
|
+
return args[0] === "--" ? args.slice(1) : args;
|
|
581
|
+
}
|
|
582
|
+
function readPackageVersion() {
|
|
583
|
+
try {
|
|
584
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
585
|
+
return typeof packageJson.version === "string" ? packageJson.version : "0.0.0";
|
|
586
|
+
} catch {
|
|
587
|
+
return "0.0.0";
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function hasDoctorErrors(checks) {
|
|
591
|
+
return checks.some((check) => check.status === "error");
|
|
592
|
+
}
|
|
593
|
+
//#endregion
|
|
594
|
+
//#region src/index.ts
|
|
595
|
+
process.exitCode = await runCli();
|
|
596
|
+
//#endregion
|
|
597
|
+
export {};
|
|
598
|
+
|
|
599
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["isRecord"],"sources":["../../providers/dist/index.js","../../shared/dist/index.js","../../tools/dist/index.js","../../core/dist/index.js","../src/cli.ts","../src/index.ts"],"sourcesContent":["//#region src/index.ts\nvar ProviderError = class extends Error {\n\tstatus;\n\tcode;\n\ttype;\n\tconstructor(message, options = {}) {\n\t\tsuper(message);\n\t\tthis.name = \"ProviderError\";\n\t\tthis.status = options.status;\n\t\tthis.code = options.code;\n\t\tthis.type = options.type;\n\t}\n};\nconst providerPresets = [\n\t{\n\t\tname: \"openai\",\n\t\tdisplayName: \"OpenAI\",\n\t\tbaseUrl: \"https://api.openai.com/v1\",\n\t\tapiKeyEnv: \"OPENAI_API_KEY\",\n\t\tdefaultModel: \"gpt-5.5\",\n\t\tcompatibility: \"openai-chat-completions\"\n\t},\n\t{\n\t\tname: \"deepseek\",\n\t\tdisplayName: \"DeepSeek\",\n\t\tbaseUrl: \"https://api.deepseek.com\",\n\t\tapiKeyEnv: \"DEEPSEEK_API_KEY\",\n\t\tdefaultModel: \"deepseek-v4-pro\",\n\t\tcompatibility: \"openai-chat-completions\"\n\t},\n\t{\n\t\tname: \"qwen\",\n\t\tdisplayName: \"Qwen\",\n\t\tbaseUrl: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n\t\tapiKeyEnv: \"QWEN_API_KEY\",\n\t\tdefaultModel: \"qwen-plus\",\n\t\tcompatibility: \"openai-chat-completions\"\n\t},\n\t{\n\t\tname: \"moonshot\",\n\t\tdisplayName: \"Moonshot\",\n\t\tbaseUrl: \"https://api.moonshot.cn/v1\",\n\t\tapiKeyEnv: \"MOONSHOT_API_KEY\",\n\t\tdefaultModel: \"moonshot-v1-8k\",\n\t\tcompatibility: \"openai-chat-completions\"\n\t},\n\t{\n\t\tname: \"mimo\",\n\t\tdisplayName: \"Mimo\",\n\t\tbaseUrl: \"https://api.xiaomimimo.com/v1\",\n\t\tapiKeyEnv: \"MIMO_API_KEY\",\n\t\tdefaultModel: \"mimo-v2.5\",\n\t\tcompatibility: \"openai-chat-completions\"\n\t},\n\t{\n\t\tname: \"openrouter\",\n\t\tdisplayName: \"OpenRouter\",\n\t\tbaseUrl: \"https://openrouter.ai/api/v1\",\n\t\tapiKeyEnv: \"OPENROUTER_API_KEY\",\n\t\tdefaultModel: \"deepseek/deepseek-v4-flash:free\",\n\t\tcompatibility: \"openai-chat-completions\"\n\t},\n\t{\n\t\tname: \"ollama\",\n\t\tdisplayName: \"Ollama\",\n\t\tbaseUrl: \"http://localhost:11434/v1\",\n\t\tdefaultModel: \"llama3.1\",\n\t\tcompatibility: \"partial\"\n\t}\n];\nfunction listProviderPresets() {\n\treturn [...providerPresets];\n}\nfunction getProviderPreset(name) {\n\treturn providerPresets.find((preset) => preset.name === name);\n}\nfunction resolveProviderConnection(options) {\n\tconst config = options.config ?? {};\n\tconst env = options.env ?? process.env;\n\tconst providerName = options.providerName ?? config.model?.provider ?? \"openai\";\n\tconst preset = getProviderPreset(providerName);\n\tconst override = config.providers?.[providerName];\n\tif (preset === void 0 && override === void 0) throw new ProviderError(`Unknown provider \"${providerName}\".`);\n\tconst baseUrl = override?.baseURL ?? override?.baseUrl ?? preset?.baseUrl;\n\tif (baseUrl === void 0) throw new ProviderError(`Provider \"${providerName}\" is missing baseURL.`);\n\tconst apiKeyEnv = override?.apiKeyEnv ?? preset?.apiKeyEnv;\n\tconst apiKey = override?.apiKey ?? (apiKeyEnv !== void 0 ? env[apiKeyEnv] : void 0);\n\tconst model = override?.model ?? config.model?.name ?? override?.defaultModel ?? preset?.defaultModel;\n\tconst temperature = override?.temperature ?? config.model?.temperature;\n\tconst maxOutputTokens = override?.maxOutputTokens ?? config.model?.maxOutputTokens;\n\tif (model === void 0) throw new ProviderError(`Provider \"${providerName}\" is missing a model.`);\n\treturn {\n\t\tprovider: providerName,\n\t\tbaseUrl,\n\t\tmodel,\n\t\ttemperature,\n\t\tmaxOutputTokens,\n\t\tapiKey,\n\t\tapiKeyEnv,\n\t\theaders: override?.headers,\n\t\tcompatibility: preset?.compatibility ?? \"partial\"\n\t};\n}\nasync function createChatCompletion(connection, options, fetchLike = defaultFetch) {\n\tconst response = await fetchLike(joinUrl(connection.baseUrl, \"chat/completions\"), {\n\t\tmethod: \"POST\",\n\t\theaders: buildHeaders(connection),\n\t\tbody: JSON.stringify({\n\t\t\tmodel: options.model ?? connection.model,\n\t\t\tmessages: options.messages,\n\t\t\ttemperature: options.temperature ?? connection.temperature,\n\t\t\tmax_tokens: options.maxOutputTokens ?? connection.maxOutputTokens,\n\t\t\tstream: false\n\t\t}),\n\t\tsignal: options.signal\n\t});\n\tif (!response.ok) throw await providerErrorFromResponse(response);\n\tconst raw = await response.json();\n\treturn {\n\t\tcontent: extractAssistantContent(raw),\n\t\traw\n\t};\n}\nasync function testProviderConnection(connection, fetchLike = defaultFetch) {\n\tif (connection.apiKeyEnv !== void 0 && connection.apiKey === void 0) return {\n\t\tstatus: \"error\",\n\t\tmessage: `Missing API key. Set ${connection.apiKeyEnv} and retry.`\n\t};\n\ttry {\n\t\tconst result = await createChatCompletion(connection, {\n\t\t\tmessages: [{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: \"Reply with OK.\"\n\t\t\t}],\n\t\t\ttemperature: connection.temperature ?? 0,\n\t\t\tmaxOutputTokens: connection.maxOutputTokens ?? 8\n\t\t}, fetchLike);\n\t\treturn {\n\t\t\tstatus: \"ok\",\n\t\t\tmessage: result.content.length > 0 ? `Model responded: ${result.content}` : \"Model responded.\"\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tstatus: \"error\",\n\t\t\tmessage: error instanceof Error ? error.message : \"Provider test failed.\"\n\t\t};\n\t}\n}\nfunction extractStreamContent(streamText) {\n\treturn parseSseDataLines(streamText).flatMap((data) => {\n\t\tif (data === \"[DONE]\") return [];\n\t\ttry {\n\t\t\tconst content = readNestedString(JSON.parse(data), [\n\t\t\t\t\"choices\",\n\t\t\t\t\"0\",\n\t\t\t\t\"delta\",\n\t\t\t\t\"content\"\n\t\t\t]);\n\t\t\treturn content === void 0 ? [] : [content];\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t});\n}\nfunction parseSseDataLines(streamText) {\n\tconst events = [];\n\tlet current = [];\n\tfor (const line of streamText.split(/\\r?\\n/)) {\n\t\tif (line.length === 0) {\n\t\t\tif (current.length > 0) {\n\t\t\t\tevents.push(current.join(\"\\n\"));\n\t\t\t\tcurrent = [];\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (line.startsWith(\"data:\")) current.push(line.slice(5).trimStart());\n\t}\n\tif (current.length > 0) events.push(current.join(\"\\n\"));\n\treturn events;\n}\nfunction buildHeaders(connection) {\n\tconst headers = {\n\t\t\"Content-Type\": \"application/json\",\n\t\t...connection.headers ?? {}\n\t};\n\tif (connection.apiKey !== void 0) headers.Authorization = `Bearer ${connection.apiKey}`;\n\treturn headers;\n}\nfunction extractAssistantContent(raw) {\n\treturn readNestedString(raw, [\n\t\t\"choices\",\n\t\t\"0\",\n\t\t\"message\",\n\t\t\"content\"\n\t]) ?? \"\";\n}\nfunction readNestedString(value, path) {\n\tlet current = value;\n\tfor (const key of path) {\n\t\tif (Array.isArray(current)) {\n\t\t\tcurrent = current[Number.parseInt(key, 10)];\n\t\t\tcontinue;\n\t\t}\n\t\tif (!isRecord(current)) return;\n\t\tcurrent = current[key];\n\t}\n\treturn typeof current === \"string\" ? current : void 0;\n}\nasync function providerErrorFromResponse(response) {\n\tconst parsed = parseJson(await response.text());\n\tconst error = isRecord(parsed) && isRecord(parsed.error) ? parsed.error : void 0;\n\treturn new ProviderError(readErrorMessage(error) ?? `${response.status} ${response.statusText}`, {\n\t\tstatus: response.status,\n\t\tcode: readOptionalString(error, \"code\"),\n\t\ttype: readOptionalString(error, \"type\")\n\t});\n}\nfunction readErrorMessage(error) {\n\treturn readOptionalString(error, \"message\");\n}\nfunction readOptionalString(value, key) {\n\tconst entry = value?.[key];\n\treturn typeof entry === \"string\" ? entry : void 0;\n}\nfunction parseJson(text) {\n\ttry {\n\t\treturn JSON.parse(text);\n\t} catch {\n\t\treturn;\n\t}\n}\nfunction joinUrl(baseUrl, path) {\n\treturn `${baseUrl.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n}\nasync function defaultFetch(url, init) {\n\treturn fetch(url, init);\n}\nfunction isRecord(value) {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n//#endregion\nexport { ProviderError, createChatCompletion, extractStreamContent, getProviderPreset, listProviderPresets, providerPresets, resolveProviderConnection, testProviderConnection };\n\n//# sourceMappingURL=index.js.map","//#region src/index.ts\nfunction createTextBlock(lines) {\n\treturn `${lines.join(\"\\n\")}\\n`;\n}\n//#endregion\nexport { createTextBlock };\n\n//# sourceMappingURL=index.js.map","//#region src/index.ts\nconst builtinTools = [\n\t{\n\t\tname: \"fs.read\",\n\t\tdescription: \"Read a UTF-8 text file from the current workspace.\",\n\t\trisk: \"read\"\n\t},\n\t{\n\t\tname: \"fs.list\",\n\t\tdescription: \"List files and directories under a workspace path.\",\n\t\trisk: \"read\"\n\t},\n\t{\n\t\tname: \"fs.search\",\n\t\tdescription: \"Search workspace files with ripgrep-compatible semantics.\",\n\t\trisk: \"read\"\n\t},\n\t{\n\t\tname: \"fs.patch\",\n\t\tdescription: \"Apply a structured patch to workspace files.\",\n\t\trisk: \"write\"\n\t},\n\t{\n\t\tname: \"shell.exec\",\n\t\tdescription: \"Run a local shell command after permission checks.\",\n\t\trisk: \"execute\"\n\t},\n\t{\n\t\tname: \"git.status\",\n\t\tdescription: \"Inspect git working tree status.\",\n\t\trisk: \"read\"\n\t},\n\t{\n\t\tname: \"git.diff\",\n\t\tdescription: \"Inspect git diffs without changing repository state.\",\n\t\trisk: \"read\"\n\t}\n];\nfunction listBuiltinTools() {\n\treturn [...builtinTools];\n}\n//#endregion\nexport { builtinTools, listBuiltinTools };\n\n//# sourceMappingURL=index.js.map","import { listProviderPresets } from \"@heroor/x-providers\";\nimport { createTextBlock } from \"@heroor/x-shared\";\nimport { listBuiltinTools } from \"@heroor/x-tools\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, parse } from \"node:path\";\n//#region src/config.ts\nconst defaultConfig = {\n\tmodel: { provider: \"openai\" },\n\tproviders: { openai: { apiKeyEnv: \"OPENAI_API_KEY\" } },\n\tenv: {}\n};\nfunction loadHeroxConfig(options = {}) {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst env = options.env ?? process.env;\n\tconst workspaceRoot = findWorkspaceRoot(cwd);\n\tconst sourcePaths = [\n\t\t{\n\t\t\tlabel: \"user\",\n\t\t\tpath: join(options.homeDir ?? homedir(), \".herox\", \"settings.json\")\n\t\t},\n\t\t{\n\t\t\tlabel: \"project\",\n\t\t\tpath: join(workspaceRoot, \".herox\", \"settings.json\")\n\t\t},\n\t\t{\n\t\t\tlabel: \"local\",\n\t\t\tpath: join(workspaceRoot, \".herox\", \"settings.local.json\")\n\t\t}\n\t];\n\tlet config = cloneConfig(defaultConfig);\n\tconst sources = [];\n\tfor (const source of sourcePaths) {\n\t\tconst loaded = readConfigSource(source.label, source.path);\n\t\tsources.push(loaded.source);\n\t\tif (loaded.config !== void 0) config = mergeHeroxConfig(config, loaded.config);\n\t}\n\tconfig = applyEnvironmentOverrides(config, env);\n\treturn {\n\t\tconfig,\n\t\tsources,\n\t\tworkspaceRoot\n\t};\n}\nfunction getConfigValue(config, path) {\n\tif (path === void 0 || path.length === 0) return config;\n\treturn path.split(\".\").reduce((current, key) => {\n\t\tif (!isRecord(current)) return;\n\t\treturn current[key];\n\t}, config);\n}\nfunction mergeHeroxConfig(base, override) {\n\treturn {\n\t\tmodel: {\n\t\t\t...base.model,\n\t\t\t...isRecord(override.model) ? stripUndefined(override.model) : {}\n\t\t},\n\t\tproviders: mergeProviderConfigs(base.providers, override.providers),\n\t\tenv: {\n\t\t\t...base.env,\n\t\t\t...isStringRecord(override.env) ? override.env : {}\n\t\t}\n\t};\n}\nfunction applyEnvironmentOverrides(config, env) {\n\treturn mergeHeroxConfig(config, { model: stripUndefined({\n\t\tprovider: env.HEROX_PROVIDER,\n\t\tname: env.HEROX_MODEL\n\t}) });\n}\nfunction readConfigSource(label, path) {\n\tif (!existsSync(path)) return { source: {\n\t\tlabel,\n\t\tpath,\n\t\texists: false\n\t} };\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(path, \"utf8\"));\n\t\tif (!isRecord(parsed)) return { source: {\n\t\t\tlabel,\n\t\t\tpath,\n\t\t\texists: true,\n\t\t\terror: \"Config file must contain a JSON object.\"\n\t\t} };\n\t\treturn {\n\t\t\tsource: {\n\t\t\t\tlabel,\n\t\t\t\tpath,\n\t\t\t\texists: true\n\t\t\t},\n\t\t\tconfig: parsed\n\t\t};\n\t} catch (error) {\n\t\treturn { source: {\n\t\t\tlabel,\n\t\t\tpath,\n\t\t\texists: true,\n\t\t\terror: error instanceof Error ? error.message : \"Unknown config parse error.\"\n\t\t} };\n\t}\n}\nfunction findWorkspaceRoot(cwd) {\n\tlet current = cwd;\n\tconst root = parse(cwd).root;\n\twhile (current !== root) {\n\t\tif (existsSync(join(current, \".git\")) || existsSync(join(current, \".herox\")) || existsSync(join(current, \"HEROX.md\"))) return current;\n\t\tcurrent = dirname(current);\n\t}\n\treturn cwd;\n}\nfunction mergeProviderConfigs(base, override) {\n\tif (!isRecord(override)) return { ...base };\n\tconst merged = { ...base };\n\tfor (const [name, value] of Object.entries(override)) {\n\t\tif (!isRecord(value)) continue;\n\t\tmerged[name] = {\n\t\t\t...merged[name] ?? {},\n\t\t\t...stripUndefined(value)\n\t\t};\n\t}\n\treturn merged;\n}\nfunction cloneConfig(config) {\n\treturn {\n\t\tmodel: { ...config.model },\n\t\tproviders: Object.fromEntries(Object.entries(config.providers).map(([name, provider]) => [name, { ...provider }])),\n\t\tenv: { ...config.env }\n\t};\n}\nfunction isStringRecord(value) {\n\treturn isRecord(value) && Object.values(value).every((entry) => typeof entry === \"string\");\n}\nfunction stripUndefined(value) {\n\treturn Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));\n}\nfunction isRecord(value) {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n//#endregion\n//#region src/index.ts\nfunction buildDoctorReport(options) {\n\tconst nodeVersion = options.nodeVersion ?? process.version;\n\tconst nodeMajor = parseNodeMajor(nodeVersion);\n\tconst providerCount = listProviderPresets().length;\n\tconst toolCount = listBuiltinTools().length;\n\treturn {\n\t\ttitle: \"Herox doctor\",\n\t\tchecks: [\n\t\t\t{\n\t\t\t\tname: \"Node.js\",\n\t\t\t\tstatus: nodeMajor >= 20 ? \"ok\" : \"error\",\n\t\t\t\tmessage: nodeMajor >= 20 ? `${nodeVersion} satisfies >=20` : `${nodeVersion} is unsupported; Herox requires Node.js >=20`\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Package\",\n\t\t\t\tstatus: \"ok\",\n\t\t\t\tmessage: `@heroor/x ${options.version}`\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Workspace\",\n\t\t\t\tstatus: \"ok\",\n\t\t\t\tmessage: options.cwd ?? process.cwd()\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Provider presets\",\n\t\t\t\tstatus: providerCount > 0 ? \"ok\" : \"warn\",\n\t\t\t\tmessage: `${providerCount} presets registered`\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Builtin tools\",\n\t\t\t\tstatus: toolCount > 0 ? \"ok\" : \"warn\",\n\t\t\t\tmessage: `${toolCount} tools registered`\n\t\t\t}\n\t\t]\n\t};\n}\nfunction formatDoctorReport(report) {\n\treturn createTextBlock([\n\t\treport.title,\n\t\t\"\",\n\t\t...report.checks.map((check) => {\n\t\t\treturn `${check.status.toUpperCase().padEnd(5, \" \")} ${check.name}: ${check.message}`;\n\t\t})\n\t]);\n}\nfunction parseNodeMajor(version) {\n\tconst [major] = version.replace(/^v/, \"\").split(\".\");\n\tconst parsed = Number.parseInt(major ?? \"\", 10);\n\treturn Number.isFinite(parsed) ? parsed : 0;\n}\n//#endregion\nexport { buildDoctorReport, formatDoctorReport, getConfigValue, loadHeroxConfig, mergeHeroxConfig };\n\n//# sourceMappingURL=index.js.map","import {\n buildDoctorReport,\n formatDoctorReport,\n getConfigValue,\n loadHeroxConfig,\n} from \"@heroor/x-core\"\nimport {\n listProviderPresets,\n resolveProviderConnection,\n testProviderConnection,\n} from \"@heroor/x-providers\"\nimport type { CliIo } from \"@heroor/x-shared\"\nimport { createTextBlock } from \"@heroor/x-shared\"\nimport { readFileSync } from \"node:fs\"\n\nexport interface RunCliOptions {\n cwd?: string\n env?: NodeJS.ProcessEnv\n homeDir?: string\n}\n\nconst helpText = createTextBlock([\n \"Herox Agent CLI\",\n \"\",\n \"Usage: herox [command] [options]\",\n \"\",\n \"Commands:\",\n \" config get [path] Print effective config or a dot-path value\",\n \" config paths Print config source paths\",\n \" doctor Check local Herox runtime readiness\",\n \" provider list List built-in provider presets\",\n \" provider test [provider] Test an OpenAI-compatible provider\",\n \" run <task> Execute a one-shot task (coming soon)\",\n \" init Initialize project Herox files (coming soon)\",\n \" resume [session] Resume a saved session (coming soon)\",\n \"\",\n \"Options:\",\n \" -h, --help Show help\",\n \" -v, --version Show version\",\n])\n\nexport async function runCli(\n args: string[] = process.argv.slice(2),\n io: CliIo = { stdout: process.stdout, stderr: process.stderr },\n options: RunCliOptions = {},\n): Promise<number> {\n const normalizedArgs = normalizeArgs(args)\n const [command] = normalizedArgs\n const runtime = {\n cwd: options.cwd ?? process.cwd(),\n env: options.env ?? process.env,\n homeDir: options.homeDir,\n }\n\n if (command === undefined || command === \"-h\" || command === \"--help\" || command === \"help\") {\n io.stdout.write(helpText)\n return 0\n }\n\n if (command === \"-v\" || command === \"--version\" || command === \"version\") {\n io.stdout.write(`${readPackageVersion()}\\n`)\n return 0\n }\n\n if (command === \"doctor\") {\n const report = buildDoctorReport({\n cwd: runtime.cwd,\n version: readPackageVersion(),\n })\n io.stdout.write(formatDoctorReport(report))\n return hasDoctorErrors(report.checks) ? 1 : 0\n }\n\n if (command === \"config\") {\n return handleConfigCommand(normalizedArgs.slice(1), io, runtime)\n }\n\n if (command === \"provider\") {\n return handleProviderCommand(normalizedArgs.slice(1), io, runtime)\n }\n\n if (command === \"run\" || command === \"init\" || command === \"resume\") {\n io.stderr.write(`herox \"${command}\" is not implemented yet.\\n`)\n return 2\n }\n\n io.stderr.write(`Unknown command: \"${command}\"\\n\\n`)\n io.stderr.write(helpText)\n return 1\n}\n\nfunction handleConfigCommand(\n args: string[],\n io: CliIo,\n runtime: Required<Pick<RunCliOptions, \"cwd\" | \"env\">> & Pick<RunCliOptions, \"homeDir\">,\n): number {\n const [subcommand, key] = args\n const loaded = loadHeroxConfig(runtime)\n\n if (subcommand === \"get\" || subcommand === undefined) {\n io.stdout.write(\n `${JSON.stringify(redactConfigValue(getConfigValue(loaded.config, key), key), null, 2)}\\n`,\n )\n return 0\n }\n\n if (subcommand === \"paths\") {\n io.stdout.write(\n createTextBlock([\n `workspaceRoot: ${loaded.workspaceRoot}`,\n ...loaded.sources.map((source) => {\n const state = source.exists ? \"found\" : \"missing\"\n return `${source.label}: ${state} ${source.path}${source.error ? ` (${source.error})` : \"\"}`\n }),\n ]),\n )\n return loaded.sources.some((source) => source.error !== undefined) ? 1 : 0\n }\n\n io.stderr.write(`Unknown config command: \"${subcommand}\"\\n`)\n return 1\n}\n\nasync function handleProviderCommand(\n args: string[],\n io: CliIo,\n runtime: Required<Pick<RunCliOptions, \"cwd\" | \"env\">> & Pick<RunCliOptions, \"homeDir\">,\n): Promise<number> {\n const [subcommand, providerName] = args\n\n if (subcommand === \"list\" || subcommand === undefined) {\n io.stdout.write(formatProviderList())\n return 0\n }\n\n if (subcommand === \"test\") {\n const loaded = loadHeroxConfig(runtime)\n\n try {\n const connection = resolveProviderConnection({\n config: loaded.config,\n providerName,\n env: mergeProviderEnv(loaded.config.env, runtime.env),\n })\n const result = await testProviderConnection(connection)\n io.stdout.write(\n `${result.status.toUpperCase()} ${connection.provider}(${connection.model}): ${result.message}\\n`,\n )\n return result.status === \"ok\" ? 0 : 1\n } catch (error) {\n io.stderr.write(`${error instanceof Error ? error.message : \"Provider test failed.\"}\\n`)\n return 1\n }\n }\n\n io.stderr.write(`Unknown provider command: \"${subcommand}\"\\n`)\n return 1\n}\n\nfunction mergeProviderEnv(\n configEnv: Record<string, string>,\n runtimeEnv: NodeJS.ProcessEnv,\n): NodeJS.ProcessEnv {\n return {\n ...configEnv,\n ...runtimeEnv,\n }\n}\n\nfunction formatProviderList(): string {\n const lines = [\n \"Provider presets\",\n \"\",\n ...listProviderPresets().map((provider) => {\n const key = provider.apiKeyEnv === undefined ? \"no key\" : provider.apiKeyEnv\n return `${provider.name.padEnd(12, \" \")} ${provider.defaultModel.padEnd(22, \" \")} ${key}`\n }),\n ]\n\n return createTextBlock(lines)\n}\n\nfunction redactConfigValue(value: unknown, path?: string): unknown {\n if (path !== undefined && shouldRedactConfigPath(path, value)) {\n return value === undefined ? undefined : \"<redacted>\"\n }\n\n if (Array.isArray(value)) {\n return value.map((entry, index) =>\n redactConfigValue(entry, appendConfigPath(path, String(index))),\n )\n }\n\n if (typeof value === \"object\" && value !== null) {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [\n key,\n shouldRedactConfigPath(appendConfigPath(path, key), entry)\n ? \"<redacted>\"\n : redactConfigValue(entry, appendConfigPath(path, key)),\n ]),\n )\n }\n\n return value\n}\n\nfunction appendConfigPath(path: string | undefined, key: string): string {\n return path === undefined || path.length === 0 ? key : `${path}.${key}`\n}\n\nfunction shouldRedactConfigPath(path: string, value: unknown): boolean {\n const segments = path.split(\".\")\n const key = segments.at(-1) ?? \"\"\n\n return isSecretKey(key) || (segments[0] === \"env\" && !isRedactableContainer(value))\n}\n\nfunction isRedactableContainer(value: unknown): boolean {\n return typeof value === \"object\" && value !== null\n}\n\nfunction isSecretKey(key: string): boolean {\n const normalized = key.toLowerCase()\n const compact = normalized.replace(/[^a-z0-9]/g, \"\")\n\n return (\n compact === \"apikey\" ||\n compact === \"authorization\" ||\n compact.endsWith(\"apikey\") ||\n compact.endsWith(\"token\") ||\n compact.endsWith(\"secret\") ||\n compact.endsWith(\"password\")\n )\n}\n\nfunction normalizeArgs(args: string[]): string[] {\n return args[0] === \"--\" ? args.slice(1) : args\n}\n\nfunction readPackageVersion(): string {\n try {\n const packageJson = JSON.parse(\n readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\"),\n )\n return typeof packageJson.version === \"string\" ? packageJson.version : \"0.0.0\"\n } catch {\n return \"0.0.0\"\n }\n}\n\nfunction hasDoctorErrors(checks: Array<{ status: string }>): boolean {\n return checks.some((check) => check.status === \"error\")\n}\n","#!/usr/bin/env node\nimport { runCli } from \"./cli.js\"\n\nprocess.exitCode = await runCli()\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heroor/x",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Herox Agent CLI for OpenAI-compatible AI models and local developer workflows.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"herox": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"build": "tsdown src/index.ts --format esm --target node20 --platform node --clean",
|
|
13
|
+
"pack:dry": "pnpm pack --dry-run"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"!dist/*.tsbuildinfo",
|
|
18
|
+
"!dist/**/*.tsbuildinfo",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"tsdown": {
|
|
30
|
+
"fixedExtension": false,
|
|
31
|
+
"dts": false,
|
|
32
|
+
"sourcemap": true,
|
|
33
|
+
"deps": {
|
|
34
|
+
"alwaysBundle": [
|
|
35
|
+
"@heroor/x-core",
|
|
36
|
+
"@heroor/x-providers",
|
|
37
|
+
"@heroor/x-shared",
|
|
38
|
+
"@heroor/x-tools"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@heroor/x-core": "workspace:*",
|
|
44
|
+
"@heroor/x-providers": "workspace:*",
|
|
45
|
+
"@heroor/x-shared": "workspace:*"
|
|
46
|
+
}
|
|
47
|
+
}
|