@hilbras/omninode 2.0.0-alpha.15 → 2.0.0-alpha.16
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/README.md +9 -2
- package/dist/cli/index.js +277 -87
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +65 -4
- package/dist/index.js +202 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { realpathSync } from "fs";
|
|
|
23
23
|
import { Command } from "commander";
|
|
24
24
|
|
|
25
25
|
// src/version.ts
|
|
26
|
-
var OMNINODE_VERSION = "2.0.0-alpha.
|
|
26
|
+
var OMNINODE_VERSION = "2.0.0-alpha.16";
|
|
27
27
|
|
|
28
28
|
// src/cli/exit-codes.ts
|
|
29
29
|
var EXIT = {
|
|
@@ -147,6 +147,10 @@ var projectConfigSchema = z.object({
|
|
|
147
147
|
}).strict().optional()
|
|
148
148
|
}).strict();
|
|
149
149
|
var appConfigSchema = z.object({
|
|
150
|
+
/** Active profile; overlays live under `profiles` (§16). */
|
|
151
|
+
profile: z.string().min(1).optional(),
|
|
152
|
+
/** Named partial overlays, e.g. development/production/testing. */
|
|
153
|
+
profiles: z.record(z.unknown()).optional(),
|
|
150
154
|
project: projectConfigSchema,
|
|
151
155
|
logging: z.object({
|
|
152
156
|
level: z.enum(["debug", "info", "warn", "error"]),
|
|
@@ -187,6 +191,85 @@ ${lines}
|
|
|
187
191
|
OmniNode never stores credentials in configuration. Replace each value with an environment-variable reference, e.g. api_key_env_var: MY_API_KEY.`;
|
|
188
192
|
}
|
|
189
193
|
|
|
194
|
+
// src/config/profiles.ts
|
|
195
|
+
import { homedir } from "os";
|
|
196
|
+
import path from "path";
|
|
197
|
+
function userConfigPath(env = process.env) {
|
|
198
|
+
if (env.OMNINODE_USER_CONFIG) return env.OMNINODE_USER_CONFIG;
|
|
199
|
+
const home = env.HOME ?? homedir();
|
|
200
|
+
return path.join(home, ".omninode", "config.yaml");
|
|
201
|
+
}
|
|
202
|
+
function resolveProfile(input, env) {
|
|
203
|
+
if (env.OMNINODE_PROFILE) return env.OMNINODE_PROFILE;
|
|
204
|
+
if (typeof input.profile === "string" && input.profile.length > 0) return input.profile;
|
|
205
|
+
return "default";
|
|
206
|
+
}
|
|
207
|
+
function mergeConfig(base, overlay) {
|
|
208
|
+
if (!isPlainObject(base) || !isPlainObject(overlay)) {
|
|
209
|
+
return overlay === void 0 ? base : overlay;
|
|
210
|
+
}
|
|
211
|
+
const result = { ...base };
|
|
212
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
213
|
+
const existing = result[key];
|
|
214
|
+
if (Array.isArray(value)) {
|
|
215
|
+
result[key] = value;
|
|
216
|
+
} else if (isPlainObject(value) && isPlainObject(existing)) {
|
|
217
|
+
result[key] = mergeConfig(existing, value);
|
|
218
|
+
} else {
|
|
219
|
+
result[key] = value;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
function isPlainObject(value) {
|
|
225
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
226
|
+
}
|
|
227
|
+
function asObject(value) {
|
|
228
|
+
return isPlainObject(value) ? value : {};
|
|
229
|
+
}
|
|
230
|
+
function applyEnvOverrides(input, env) {
|
|
231
|
+
const applied = [];
|
|
232
|
+
const config = mergeConfig(input, {});
|
|
233
|
+
if (env.OMNINODE_LOG_LEVEL) {
|
|
234
|
+
config.logging = { ...asObject(config.logging), level: env.OMNINODE_LOG_LEVEL };
|
|
235
|
+
applied.push("OMNINODE_LOG_LEVEL");
|
|
236
|
+
}
|
|
237
|
+
if (env.OMNINODE_LOG_FORMAT) {
|
|
238
|
+
config.logging = { ...asObject(config.logging), format: env.OMNINODE_LOG_FORMAT };
|
|
239
|
+
applied.push("OMNINODE_LOG_FORMAT");
|
|
240
|
+
}
|
|
241
|
+
const memoryProvider = env.OMNINODE_MEMORY_PROVIDER;
|
|
242
|
+
const memoryRequired = env.OMNINODE_MEMORY_REQUIRED;
|
|
243
|
+
if (memoryProvider || memoryRequired) {
|
|
244
|
+
const project = asObject(config.project);
|
|
245
|
+
config.project = {
|
|
246
|
+
...project,
|
|
247
|
+
memory: {
|
|
248
|
+
...asObject(project.memory ?? { provider: "local" }),
|
|
249
|
+
...memoryProvider ? { provider: memoryProvider } : {},
|
|
250
|
+
...memoryRequired ? { required: memoryRequired !== "false" } : {}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
if (memoryProvider) applied.push("OMNINODE_MEMORY_PROVIDER");
|
|
254
|
+
if (memoryRequired) applied.push("OMNINODE_MEMORY_REQUIRED");
|
|
255
|
+
}
|
|
256
|
+
return { config, applied };
|
|
257
|
+
}
|
|
258
|
+
function applyDefaults(input) {
|
|
259
|
+
const applied = [];
|
|
260
|
+
const config = mergeConfig(input, {});
|
|
261
|
+
const logging = asObject(config.logging);
|
|
262
|
+
if (logging.level === void 0) {
|
|
263
|
+
config.logging = { ...logging, level: "info" };
|
|
264
|
+
applied.push("logging.level");
|
|
265
|
+
}
|
|
266
|
+
if (logging.format === void 0) {
|
|
267
|
+
config.logging = { ...asObject(config.logging), format: "text" };
|
|
268
|
+
applied.push("logging.format");
|
|
269
|
+
}
|
|
270
|
+
return { config, applied };
|
|
271
|
+
}
|
|
272
|
+
|
|
190
273
|
// src/config/loader.ts
|
|
191
274
|
import { existsSync, readFileSync } from "fs";
|
|
192
275
|
import { parse as parseYaml } from "yaml";
|
|
@@ -213,38 +296,108 @@ function expandEnvRefs(text, env, source = "config") {
|
|
|
213
296
|
}
|
|
214
297
|
);
|
|
215
298
|
}
|
|
216
|
-
function
|
|
299
|
+
function loadConfigDetailed(options = {}) {
|
|
217
300
|
const env = options.env ?? process.env;
|
|
218
|
-
const
|
|
219
|
-
|
|
301
|
+
const explicitPath = options.path;
|
|
302
|
+
let input = {};
|
|
303
|
+
let projectFile;
|
|
304
|
+
let userFile;
|
|
305
|
+
if (!explicitPath) {
|
|
306
|
+
const candidate = userConfigPath(env);
|
|
307
|
+
if (existsSync(candidate)) {
|
|
308
|
+
input = mergeConfig(input, readConfigDocument(candidate, env));
|
|
309
|
+
userFile = candidate;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const resolvedProject = explicitPath ?? (options.directory ? findConfigFile(options.directory) : findConfigFile());
|
|
313
|
+
if (resolvedProject) {
|
|
314
|
+
input = mergeConfig(input, readConfigDocument(resolvedProject, env));
|
|
315
|
+
projectFile = resolvedProject;
|
|
316
|
+
}
|
|
317
|
+
if (!projectFile && !userFile) {
|
|
220
318
|
throw new ConfigError(
|
|
221
319
|
"CONFIG_NOT_FOUND",
|
|
222
320
|
"No omninode.yaml found. Run `omninode init` to create one, or pass an explicit path."
|
|
223
321
|
);
|
|
224
322
|
}
|
|
225
|
-
const
|
|
323
|
+
const profile = options.profile ?? resolveProfile(input, env);
|
|
324
|
+
const overlay = input.profiles?.[profile];
|
|
325
|
+
if (overlay !== void 0) {
|
|
326
|
+
const { profiles: _drop, ...base } = input;
|
|
327
|
+
input = mergeConfig(base, overlay);
|
|
328
|
+
} else if (profile !== "default" && profile !== "") {
|
|
329
|
+
throw new ConfigError(
|
|
330
|
+
"CONFIG_INVALID",
|
|
331
|
+
`Profile "${profile}" is not defined in this configuration (known: ${Object.keys(input.profiles ?? {}).join(", ") || "none"}).`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
const withEnv = applyEnvOverrides(input, env);
|
|
335
|
+
const withDefaults = applyDefaults(withEnv.config);
|
|
336
|
+
const parsed = appConfigSchema.safeParse(withDefaults.config);
|
|
337
|
+
if (!parsed.success) {
|
|
338
|
+
const issues = parsed.error.issues.map((issue) => {
|
|
339
|
+
const location = issue.path.join(".") || "(root)";
|
|
340
|
+
return `${location}: ${issue.message}`;
|
|
341
|
+
});
|
|
342
|
+
throw new ConfigError(
|
|
343
|
+
"CONFIG_INVALID",
|
|
344
|
+
`Invalid configuration:
|
|
345
|
+
${issues.join("\n ")}`,
|
|
346
|
+
{ details: { issues } }
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
config: toAppConfig(parsed.data),
|
|
351
|
+
sources: {
|
|
352
|
+
...projectFile !== void 0 ? { projectFile } : {},
|
|
353
|
+
...userFile !== void 0 ? { userFile } : {},
|
|
354
|
+
profile,
|
|
355
|
+
envOverrides: withEnv.applied,
|
|
356
|
+
defaults: withDefaults.applied
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function readConfigDocument(path2, optionsEnv) {
|
|
361
|
+
const raw = readFileSync(path2, "utf8");
|
|
226
362
|
const secretFindings = scanForInlineSecrets(raw);
|
|
227
363
|
if (secretFindings.length > 0) {
|
|
228
|
-
throw new ConfigError("CONFIG_INVALID", describeSecretFindings(
|
|
364
|
+
throw new ConfigError("CONFIG_INVALID", describeSecretFindings(path2, secretFindings));
|
|
229
365
|
}
|
|
230
|
-
const expanded = expandEnvRefs(raw,
|
|
231
|
-
let data;
|
|
366
|
+
const expanded = expandEnvRefs(raw, optionsEnv, path2);
|
|
232
367
|
try {
|
|
233
|
-
|
|
368
|
+
return parseYaml(expanded);
|
|
234
369
|
} catch (error) {
|
|
235
|
-
throw new ConfigError("CONFIG_INVALID", `Failed to parse ${
|
|
370
|
+
throw new ConfigError("CONFIG_INVALID", `Failed to parse ${path2}.`, { cause: error });
|
|
236
371
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
372
|
+
}
|
|
373
|
+
function configDiagnostics(config) {
|
|
374
|
+
const notes = [];
|
|
375
|
+
for (const provider of config.project.providers) {
|
|
376
|
+
if (!provider.apiKeyEnvVar && provider.baseUrl.startsWith("https://")) {
|
|
377
|
+
notes.push(
|
|
378
|
+
`Provider "${provider.name}": API key reference is missing \u2014 add api_key_env_var: <ENV_VAR> unless this endpoint is intentionally keyless.`
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
for (const agent of config.project.agents) {
|
|
383
|
+
if (!agent.command && agent.integration !== "custom") {
|
|
384
|
+
notes.push(`Agent "${agent.name}": no command configured \u2014 the process adapter needs one.`);
|
|
385
|
+
}
|
|
386
|
+
if (agent.allowExternalCwd) {
|
|
387
|
+
notes.push(`Agent "${agent.name}": may run outside the project root (allow_external_cwd).`);
|
|
388
|
+
}
|
|
246
389
|
}
|
|
247
|
-
|
|
390
|
+
for (const pipeline of config.project.pipelines) {
|
|
391
|
+
const ids = new Set(pipeline.steps.map((s) => s.id));
|
|
392
|
+
for (const step of pipeline.steps) {
|
|
393
|
+
for (const dep of step.dependsOn ?? []) {
|
|
394
|
+
if (!ids.has(dep)) {
|
|
395
|
+
notes.push(`Pipeline "${pipeline.id}": step "${step.id}" depends on unknown step "${dep}".`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return notes;
|
|
248
401
|
}
|
|
249
402
|
function toAppConfig(yaml) {
|
|
250
403
|
return {
|
|
@@ -372,6 +525,18 @@ function defaultProjectConfigYaml(projectName = "My Project") {
|
|
|
372
525
|
`;
|
|
373
526
|
}
|
|
374
527
|
|
|
528
|
+
// src/cli/options.ts
|
|
529
|
+
var cliConfigOptions = {};
|
|
530
|
+
function loadProjectConfig() {
|
|
531
|
+
return loadConfigDetailed({
|
|
532
|
+
...cliConfigOptions.path !== void 0 ? { path: cliConfigOptions.path } : {},
|
|
533
|
+
...cliConfigOptions.profile !== void 0 ? { profile: cliConfigOptions.profile } : {}
|
|
534
|
+
}).config;
|
|
535
|
+
}
|
|
536
|
+
function activeConfigPath() {
|
|
537
|
+
return cliConfigOptions.path ?? findConfigFile();
|
|
538
|
+
}
|
|
539
|
+
|
|
375
540
|
// src/cli/commands/init.ts
|
|
376
541
|
import { existsSync as existsSync2, writeFileSync } from "fs";
|
|
377
542
|
async function runInit(options) {
|
|
@@ -459,7 +624,7 @@ function truncate(text, max) {
|
|
|
459
624
|
return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
460
625
|
}
|
|
461
626
|
function appendToConfigList(listKey, item) {
|
|
462
|
-
const configPath =
|
|
627
|
+
const configPath = activeConfigPath();
|
|
463
628
|
if (!configPath) {
|
|
464
629
|
throw new OmniNodeError("CONFIG_NOT_FOUND", "No omninode.yaml found. Run `omninode init` first.");
|
|
465
630
|
}
|
|
@@ -1365,15 +1530,15 @@ async function writeFileAtomic(target, contents, durability = "rename") {
|
|
|
1365
1530
|
}
|
|
1366
1531
|
}
|
|
1367
1532
|
}
|
|
1368
|
-
async function inspectDocument(
|
|
1533
|
+
async function inspectDocument(path2) {
|
|
1369
1534
|
let raw;
|
|
1370
1535
|
try {
|
|
1371
|
-
await stat(
|
|
1536
|
+
await stat(path2);
|
|
1372
1537
|
} catch {
|
|
1373
1538
|
return { status: "missing" };
|
|
1374
1539
|
}
|
|
1375
1540
|
try {
|
|
1376
|
-
raw = await readFile(
|
|
1541
|
+
raw = await readFile(path2, "utf8");
|
|
1377
1542
|
} catch (error) {
|
|
1378
1543
|
return { status: "corrupt", detail: `unreadable: ${error instanceof Error ? error.message : String(error)}` };
|
|
1379
1544
|
}
|
|
@@ -1393,21 +1558,21 @@ async function inspectDocument(path) {
|
|
|
1393
1558
|
if (version < SCHEMA_VERSION) return { status: "v1-legacy" };
|
|
1394
1559
|
return { status: "current", schemaVersion: version, count: doc.items.length };
|
|
1395
1560
|
}
|
|
1396
|
-
async function readDocument(
|
|
1397
|
-
const state = await inspectDocument(
|
|
1561
|
+
async function readDocument(path2, onCorrupt, quarantine = true) {
|
|
1562
|
+
const state = await inspectDocument(path2);
|
|
1398
1563
|
if (state.status === "missing" || state.status === "empty") return [];
|
|
1399
1564
|
if (state.status === "future") {
|
|
1400
1565
|
const error = new Error(
|
|
1401
|
-
`${
|
|
1566
|
+
`${path2} was written by a newer OmniNode (schema v${state.schemaVersion} > v${SCHEMA_VERSION})`
|
|
1402
1567
|
);
|
|
1403
|
-
onCorrupt?.(
|
|
1568
|
+
onCorrupt?.(path2, error);
|
|
1404
1569
|
return [];
|
|
1405
1570
|
}
|
|
1406
1571
|
let raw;
|
|
1407
1572
|
try {
|
|
1408
|
-
raw = await readFile(
|
|
1573
|
+
raw = await readFile(path2, "utf8");
|
|
1409
1574
|
} catch (error) {
|
|
1410
|
-
onCorrupt?.(
|
|
1575
|
+
onCorrupt?.(path2, error);
|
|
1411
1576
|
return [];
|
|
1412
1577
|
}
|
|
1413
1578
|
try {
|
|
@@ -1416,16 +1581,16 @@ async function readDocument(path, onCorrupt, quarantine = true) {
|
|
|
1416
1581
|
return parsed.items ?? [];
|
|
1417
1582
|
} catch (error) {
|
|
1418
1583
|
if (quarantine) {
|
|
1419
|
-
const quarantinePath = `${
|
|
1584
|
+
const quarantinePath = `${path2}.corrupt-${Date.now()}`;
|
|
1420
1585
|
try {
|
|
1421
|
-
await rename(
|
|
1586
|
+
await rename(path2, quarantinePath);
|
|
1422
1587
|
logger.child({ component: "persistence" }).warn(
|
|
1423
|
-
`Quarantined unreadable store file ${
|
|
1588
|
+
`Quarantined unreadable store file ${path2} \u2192 ${quarantinePath}`
|
|
1424
1589
|
);
|
|
1425
1590
|
} catch {
|
|
1426
1591
|
}
|
|
1427
1592
|
}
|
|
1428
|
-
onCorrupt?.(
|
|
1593
|
+
onCorrupt?.(path2, error);
|
|
1429
1594
|
return [];
|
|
1430
1595
|
}
|
|
1431
1596
|
}
|
|
@@ -1955,7 +2120,7 @@ function registerPipelineInspect(pipelineGroup) {
|
|
|
1955
2120
|
function registerAgentInspect(agentGroup) {
|
|
1956
2121
|
jsonOption(
|
|
1957
2122
|
agentGroup.command("inspect <name>").description("Show an agent's effective configuration and recent tasks.").action(async (name, options) => {
|
|
1958
|
-
const config =
|
|
2123
|
+
const config = loadProjectConfig();
|
|
1959
2124
|
const agentConfig = config.project.agents.find((a) => a.name === name);
|
|
1960
2125
|
if (!agentConfig) throw new OmniNodeError("AGENT_NOT_FOUND", `No agent "${name}" is configured.`);
|
|
1961
2126
|
const registry = buildAgentRegistry(config);
|
|
@@ -1985,7 +2150,7 @@ function registerAgentInspect(agentGroup) {
|
|
|
1985
2150
|
function registerProviderInspect(providerGroup) {
|
|
1986
2151
|
jsonOption(
|
|
1987
2152
|
providerGroup.command("inspect <name>").description("Show a provider's authentication status and capabilities.").action(async (name, options) => {
|
|
1988
|
-
const config =
|
|
2153
|
+
const config = loadProjectConfig();
|
|
1989
2154
|
const providerConfig = config.project.providers.find((p) => p.name === name);
|
|
1990
2155
|
if (!providerConfig) throw new OmniNodeError("PROVIDER_NOT_FOUND", `No provider "${name}" is configured.`);
|
|
1991
2156
|
const { createProvider: createProvider2 } = await import("./providers-OYXBAQTX.js");
|
|
@@ -2018,7 +2183,7 @@ function registerProviderInspect(providerGroup) {
|
|
|
2018
2183
|
function registerProviderCommands(program) {
|
|
2019
2184
|
const provider = program.command("provider").description("Manage AI providers configured in omninode.yaml.");
|
|
2020
2185
|
provider.command("list").description("List configured providers.").option("--json", "Emit providers as JSON.").action(async (options) => {
|
|
2021
|
-
const config =
|
|
2186
|
+
const config = loadProjectConfig();
|
|
2022
2187
|
const providers = config.project.providers;
|
|
2023
2188
|
if (options.json) {
|
|
2024
2189
|
console.log(JSON.stringify(providers, null, 2));
|
|
@@ -2058,7 +2223,7 @@ function registerProviderCommands(program) {
|
|
|
2058
2223
|
registerProviderInspect(provider);
|
|
2059
2224
|
}
|
|
2060
2225
|
async function addProvider(options) {
|
|
2061
|
-
const configPath =
|
|
2226
|
+
const configPath = activeConfigPath();
|
|
2062
2227
|
if (!configPath) {
|
|
2063
2228
|
throw new OmniNodeError("CONFIG_NOT_FOUND", "No omninode.yaml found. Run `omninode init` first.");
|
|
2064
2229
|
}
|
|
@@ -2073,11 +2238,11 @@ async function addProvider(options) {
|
|
|
2073
2238
|
throw new OmniNodeError("CLI_USAGE", `Invalid provider options: ${issues}`);
|
|
2074
2239
|
}
|
|
2075
2240
|
const newProvider = candidate.data;
|
|
2076
|
-
const existing =
|
|
2241
|
+
const existing = loadProjectConfig();
|
|
2077
2242
|
if (existing.project.providers.some((p) => p.name === newProvider.name)) {
|
|
2078
2243
|
throw new OmniNodeError(
|
|
2079
2244
|
"CLI_USAGE",
|
|
2080
|
-
`Provider "${newProvider.name}" already exists in ${
|
|
2245
|
+
`Provider "${newProvider.name}" already exists in ${activeConfigPath() ?? "omninode.yaml"}.`
|
|
2081
2246
|
);
|
|
2082
2247
|
}
|
|
2083
2248
|
const writtenPath = appendToConfigList("providers", newProvider);
|
|
@@ -2091,11 +2256,11 @@ async function addProvider(options) {
|
|
|
2091
2256
|
}
|
|
2092
2257
|
}
|
|
2093
2258
|
function removeProvider(name) {
|
|
2094
|
-
const configPath =
|
|
2259
|
+
const configPath = activeConfigPath();
|
|
2095
2260
|
if (!configPath) {
|
|
2096
2261
|
throw new OmniNodeError("CONFIG_NOT_FOUND", "No omninode.yaml found.");
|
|
2097
2262
|
}
|
|
2098
|
-
const config =
|
|
2263
|
+
const config = loadProjectConfig();
|
|
2099
2264
|
if (!config.project.providers.some((p) => p.name === name)) {
|
|
2100
2265
|
throw new ProviderError("PROVIDER_NOT_FOUND", `No provider "${name}" is configured.`);
|
|
2101
2266
|
}
|
|
@@ -2111,7 +2276,7 @@ function removeProvider(name) {
|
|
|
2111
2276
|
console.log(`Removed provider "${name}" from ${configPath}.`);
|
|
2112
2277
|
}
|
|
2113
2278
|
async function testProvider(name, options = {}) {
|
|
2114
|
-
const config =
|
|
2279
|
+
const config = loadProjectConfig();
|
|
2115
2280
|
const providerConfig = config.project.providers.find((p) => p.name === name);
|
|
2116
2281
|
if (!providerConfig) {
|
|
2117
2282
|
const available = config.project.providers.map((p) => p.name).join(", ") || "(none)";
|
|
@@ -2493,7 +2658,7 @@ var collectArgs = (value, previous) => [...previous, value];
|
|
|
2493
2658
|
function registerAgentCommands(program) {
|
|
2494
2659
|
const agent = program.command("agent").description("Manage CLI agents registered with OmniNode.");
|
|
2495
2660
|
agent.command("list").description("List configured agents.").option("--json", "Emit agents as JSON.").action(async (options) => {
|
|
2496
|
-
const config =
|
|
2661
|
+
const config = loadProjectConfig();
|
|
2497
2662
|
const agents = config.project.agents;
|
|
2498
2663
|
if (options.json) {
|
|
2499
2664
|
console.log(JSON.stringify(config.project.agents, null, 2));
|
|
@@ -2522,7 +2687,7 @@ function registerAgentCommands(program) {
|
|
|
2522
2687
|
(value) => Number(value)
|
|
2523
2688
|
).action(async (options) => addAgent(options));
|
|
2524
2689
|
agent.command("run <name> <objective>").description("Run a single task through this agent immediately.").action(async (name, objective) => {
|
|
2525
|
-
const config =
|
|
2690
|
+
const config = loadProjectConfig();
|
|
2526
2691
|
const agentConfig = config.project.agents.find((a) => a.name === name);
|
|
2527
2692
|
if (!agentConfig) {
|
|
2528
2693
|
throw new AgentError("AGENT_NOT_FOUND", `No agent "${name}" is configured.`);
|
|
@@ -2559,7 +2724,7 @@ async function addAgent(options) {
|
|
|
2559
2724
|
throw new OmniNodeError("CLI_USAGE", `Invalid agent options: ${issues}`);
|
|
2560
2725
|
}
|
|
2561
2726
|
const newAgent = candidate.data;
|
|
2562
|
-
const existing =
|
|
2727
|
+
const existing = loadProjectConfig();
|
|
2563
2728
|
if (existing.project.agents.some((a) => a.name === newAgent.name)) {
|
|
2564
2729
|
throw new OmniNodeError(
|
|
2565
2730
|
"CLI_USAGE",
|
|
@@ -2571,7 +2736,7 @@ async function addAgent(options) {
|
|
|
2571
2736
|
console.log(`Run \`omninode agent test ${newAgent.name}\` to verify the connection.`);
|
|
2572
2737
|
}
|
|
2573
2738
|
async function testAgent(name) {
|
|
2574
|
-
const config =
|
|
2739
|
+
const config = loadProjectConfig();
|
|
2575
2740
|
const agentConfig = config.project.agents.find((a) => a.name === name);
|
|
2576
2741
|
if (!agentConfig) {
|
|
2577
2742
|
const available = config.project.agents.map((a) => a.name).join(", ") || "(none)";
|
|
@@ -2602,7 +2767,7 @@ async function testAgent(name) {
|
|
|
2602
2767
|
function registerRoleCommands(program) {
|
|
2603
2768
|
const role = program.command("role").description("Inspect roles defined in omninode.yaml.");
|
|
2604
2769
|
role.command("list").description("List configured roles.").option("--json", "Emit roles as JSON.").action(async (options) => {
|
|
2605
|
-
const config =
|
|
2770
|
+
const config = loadProjectConfig();
|
|
2606
2771
|
const roles = config.project.roles;
|
|
2607
2772
|
if (options.json) {
|
|
2608
2773
|
console.log(JSON.stringify(config.project.roles, null, 2));
|
|
@@ -2626,7 +2791,7 @@ var collectFiles = (value, previous) => [...previous, value];
|
|
|
2626
2791
|
function registerTaskCommands(program) {
|
|
2627
2792
|
const task = program.command("task").description("Create, run and inspect tasks (\xA713).");
|
|
2628
2793
|
task.command("create <objective>").description("Create a task. Assign a role and/or agent; run it with `omninode task run`.").option("-r, --role <roleId>", "Role id from project.roles (validated at creation).").option("-a, --agent <name>", "Agent name from project.agents (validated at creation).").option("-f, --file <path>", "File or glob relevant to the task (repeatable).", collectFiles).action(async (objective, options) => {
|
|
2629
|
-
const config =
|
|
2794
|
+
const config = loadProjectConfig();
|
|
2630
2795
|
const engine = createTaskEngine(config);
|
|
2631
2796
|
const created = await engine.create({
|
|
2632
2797
|
objective,
|
|
@@ -2643,7 +2808,7 @@ function registerTaskCommands(program) {
|
|
|
2643
2808
|
console.log(`Run \`omninode task run ${created.id}\` to execute it.`);
|
|
2644
2809
|
});
|
|
2645
2810
|
task.command("run <task-id>").description("Run a task through its assigned agent.").action(async (taskId) => {
|
|
2646
|
-
const config =
|
|
2811
|
+
const config = loadProjectConfig();
|
|
2647
2812
|
const engine = createTaskEngine(config);
|
|
2648
2813
|
console.log(`Running task ${taskId}...`);
|
|
2649
2814
|
const finished = await engine.run(taskId);
|
|
@@ -2651,7 +2816,7 @@ function registerTaskCommands(program) {
|
|
|
2651
2816
|
process.exitCode = exitCodeForStatus(finished.status);
|
|
2652
2817
|
});
|
|
2653
2818
|
task.command("status <task-id>").description("Show task details and current status.").action(async (taskId) => {
|
|
2654
|
-
const config =
|
|
2819
|
+
const config = loadProjectConfig();
|
|
2655
2820
|
const engine = createTaskEngine(config);
|
|
2656
2821
|
const found = await engine.get(taskId);
|
|
2657
2822
|
if (!found) {
|
|
@@ -2663,7 +2828,7 @@ function registerTaskCommands(program) {
|
|
|
2663
2828
|
printOutcome(found, { verbose: true });
|
|
2664
2829
|
});
|
|
2665
2830
|
task.command("list").description("List tasks in the project's task store.").option("-s, --status <status>", "Filter by status (created, queued, running, completed, failed, cancelled).").option("--json", "Emit tasks as JSON.").action(async (options) => {
|
|
2666
|
-
const config =
|
|
2831
|
+
const config = loadProjectConfig();
|
|
2667
2832
|
const engine = createTaskEngine(config);
|
|
2668
2833
|
const tasks = await engine.list(
|
|
2669
2834
|
options.status !== void 0 ? { status: options.status } : {}
|
|
@@ -2681,13 +2846,13 @@ function registerTaskCommands(program) {
|
|
|
2681
2846
|
}
|
|
2682
2847
|
});
|
|
2683
2848
|
task.command("cancel <task-id>").description("Cancel a task that has not finished yet.").action(async (taskId) => {
|
|
2684
|
-
const config =
|
|
2849
|
+
const config = loadProjectConfig();
|
|
2685
2850
|
const engine = createTaskEngine(config);
|
|
2686
2851
|
const cancelled = await engine.cancel(taskId);
|
|
2687
2852
|
console.log(`Task ${cancelled.id} is now "${cancelled.status}".`);
|
|
2688
2853
|
});
|
|
2689
2854
|
task.command("retry <task-id>").description("Reset a failed, timed out, unknown or cancelled task to created; combine with --run to execute it again.").option("--run", "Run the task again immediately after resetting it.").action(async (taskId, options) => {
|
|
2690
|
-
const config =
|
|
2855
|
+
const config = loadProjectConfig();
|
|
2691
2856
|
const engine = createTaskEngine(config);
|
|
2692
2857
|
const reset = await engine.retry(taskId);
|
|
2693
2858
|
console.log(`Task ${reset.id} reset to "${reset.status}".`);
|
|
@@ -4228,7 +4393,7 @@ function buildPipelineEngine(config, log = logger) {
|
|
|
4228
4393
|
function registerPipelineCommands(program) {
|
|
4229
4394
|
const pipeline = program.command("pipeline").description("Run multi-step AI pipelines defined in omninode.yaml (\xA714).");
|
|
4230
4395
|
pipeline.command("list").description("List configured pipelines.").option("--json", "Emit pipelines as JSON.").action(async (options) => {
|
|
4231
|
-
const config =
|
|
4396
|
+
const config = loadProjectConfig();
|
|
4232
4397
|
const pipelines = config.project.pipelines;
|
|
4233
4398
|
if (options.json) {
|
|
4234
4399
|
console.log(JSON.stringify(config.project.pipelines, null, 2));
|
|
@@ -4249,7 +4414,7 @@ function registerPipelineCommands(program) {
|
|
|
4249
4414
|
pipeline.command("retry <run-id> [objective]").description(
|
|
4250
4415
|
"Error recovery: re-run a recorded pipeline run as a new run, reusing its objective."
|
|
4251
4416
|
).action(async (runId, objective) => {
|
|
4252
|
-
const config =
|
|
4417
|
+
const config = loadProjectConfig();
|
|
4253
4418
|
const engine = buildPipelineEngine(config);
|
|
4254
4419
|
const previous = await engine.get(runId);
|
|
4255
4420
|
if (!previous) {
|
|
@@ -4268,7 +4433,7 @@ function registerPipelineCommands(program) {
|
|
|
4268
4433
|
await runPipelineAction(def.id, objective ?? previous.objective);
|
|
4269
4434
|
});
|
|
4270
4435
|
pipeline.command("cancel <run-id>").description("Request cancellation of a pipeline run (stops scheduling and cancels running tasks).").action(async (runId) => {
|
|
4271
|
-
const config =
|
|
4436
|
+
const config = loadProjectConfig();
|
|
4272
4437
|
const engine = buildPipelineEngine(config);
|
|
4273
4438
|
await engine.cancel(runId);
|
|
4274
4439
|
console.log(`Cancellation requested for run ${runId}.`);
|
|
@@ -4278,7 +4443,7 @@ function registerPipelineCommands(program) {
|
|
|
4278
4443
|
await createPipelineFromFile(id, options.from);
|
|
4279
4444
|
});
|
|
4280
4445
|
pipeline.command("validate <id>").description("Validate a pipeline definition before running it (\xA79.5).").action(async (id) => {
|
|
4281
|
-
const config =
|
|
4446
|
+
const config = loadProjectConfig();
|
|
4282
4447
|
const def = findPipeline(config, id);
|
|
4283
4448
|
const engine = buildPipelineEngine(config);
|
|
4284
4449
|
engine.validate(def);
|
|
@@ -4289,7 +4454,7 @@ function registerPipelineCommands(program) {
|
|
|
4289
4454
|
}
|
|
4290
4455
|
});
|
|
4291
4456
|
pipeline.command("runs <id>").description("List recorded runs of a pipeline.").action(async (id) => {
|
|
4292
|
-
const config =
|
|
4457
|
+
const config = loadProjectConfig();
|
|
4293
4458
|
const engine = buildPipelineEngine(config);
|
|
4294
4459
|
const runs = await engine.listRuns({ pipelineId: id });
|
|
4295
4460
|
if (runs.length === 0) {
|
|
@@ -4302,7 +4467,7 @@ function registerPipelineCommands(program) {
|
|
|
4302
4467
|
}
|
|
4303
4468
|
});
|
|
4304
4469
|
pipeline.command("status <run-id>").description("Show the recorded state of a pipeline run.").action(async (runId) => {
|
|
4305
|
-
const config =
|
|
4470
|
+
const config = loadProjectConfig();
|
|
4306
4471
|
const engine = buildPipelineEngine(config);
|
|
4307
4472
|
const run2 = await engine.get(runId);
|
|
4308
4473
|
if (!run2) {
|
|
@@ -4334,7 +4499,7 @@ function registerPipelineCommands(program) {
|
|
|
4334
4499
|
registerPipelineInspect(pipeline);
|
|
4335
4500
|
}
|
|
4336
4501
|
async function runPipelineAction(id, objective) {
|
|
4337
|
-
const config =
|
|
4502
|
+
const config = loadProjectConfig();
|
|
4338
4503
|
const def = findPipeline(config, id);
|
|
4339
4504
|
const engine = buildPipelineEngine(config);
|
|
4340
4505
|
console.log(
|
|
@@ -4366,7 +4531,7 @@ function registerRunAlias(program) {
|
|
|
4366
4531
|
program.command("run <id> [objective]").description("Alias of `omninode pipeline run` \u2014 execute a configured pipeline end to end.").action(async (id, objective) => runPipelineAction(id, objective));
|
|
4367
4532
|
}
|
|
4368
4533
|
async function createPipelineFromFile(id, file) {
|
|
4369
|
-
const configPath =
|
|
4534
|
+
const configPath = activeConfigPath();
|
|
4370
4535
|
if (!configPath) {
|
|
4371
4536
|
throw new OmniNodeError("CONFIG_NOT_FOUND", "No omninode.yaml found. Run `omninode init` first.");
|
|
4372
4537
|
}
|
|
@@ -4383,7 +4548,7 @@ async function createPipelineFromFile(id, file) {
|
|
|
4383
4548
|
const issues = candidate.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
4384
4549
|
throw new PipelineError("PIPELINE_INVALID", `Invalid pipeline definition: ${issues}`);
|
|
4385
4550
|
}
|
|
4386
|
-
const engine = buildPipelineEngine(
|
|
4551
|
+
const engine = buildPipelineEngine(loadProjectConfig());
|
|
4387
4552
|
engine.validate({ id, ...candidate.data });
|
|
4388
4553
|
const doc = parseDocument3(readFileSync4(configPath, "utf8"));
|
|
4389
4554
|
if (doc.getIn(["project", "pipelines"]) === void 0) {
|
|
@@ -4418,7 +4583,7 @@ function findPipeline(config, id) {
|
|
|
4418
4583
|
function registerMemoryCommands(program) {
|
|
4419
4584
|
const memory = program.command("memory").description("Inspect and manage persistent project memory (\xA719\u2013\xA720).");
|
|
4420
4585
|
memory.command("status").description("Show the active memory provider and how much is stored.").action(async () => {
|
|
4421
|
-
const config =
|
|
4586
|
+
const config = loadProjectConfig();
|
|
4422
4587
|
const configured = config.project.memory?.provider ?? "local (default \u2014 add a `memory:` section to opt in)";
|
|
4423
4588
|
const svc = new MemoryService(createMemoryProvider(config.project.memory));
|
|
4424
4589
|
const entries = await svc.providerRef.query({ limit: 1e4 });
|
|
@@ -4433,7 +4598,7 @@ function registerMemoryCommands(program) {
|
|
|
4433
4598
|
}
|
|
4434
4599
|
});
|
|
4435
4600
|
memory.command("query <text>").description("Search memory for entries relevant to the text.").option("-s, --scope <scope>", "Restrict to a scope: project, task, role or knowledge.").option("-n, --limit <count>", "Maximum entries to return.", (value) => Number(value), 10).option("--json", "Emit entries as JSON.").action(async (text, options) => {
|
|
4436
|
-
const config =
|
|
4601
|
+
const config = loadProjectConfig();
|
|
4437
4602
|
const svc = new MemoryService(createMemoryProvider(config.project.memory));
|
|
4438
4603
|
const entries = await svc.providerRef.query({
|
|
4439
4604
|
text,
|
|
@@ -4458,7 +4623,7 @@ function registerMemoryCommands(program) {
|
|
|
4458
4623
|
"Comma-separated tags.",
|
|
4459
4624
|
(value) => value.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0)
|
|
4460
4625
|
).option("-k, --key <key>", "Entry key (defaults to a generated note key).").action(async (text, options) => {
|
|
4461
|
-
const config =
|
|
4626
|
+
const config = loadProjectConfig();
|
|
4462
4627
|
const svc = new MemoryService(createMemoryProvider(config.project.memory));
|
|
4463
4628
|
const key = options.key ?? `note:${Date.now().toString(36)}`;
|
|
4464
4629
|
await svc.providerRef.write({
|
|
@@ -4541,7 +4706,7 @@ function countBy(items, key) {
|
|
|
4541
4706
|
}
|
|
4542
4707
|
function registerStatusCommand(program) {
|
|
4543
4708
|
program.command("status").description("Show the project's OmniNode state overview (config, tasks, runs, reports, plans, memory).").option("--json", "Emit machine-readable JSON (for scripts and CI).").action(async (options) => {
|
|
4544
|
-
const config =
|
|
4709
|
+
const config = loadProjectConfig();
|
|
4545
4710
|
const tasks = await new FileTaskStore().list();
|
|
4546
4711
|
const runs = await new FilePipelineRunStore().list();
|
|
4547
4712
|
const reportStore = new FileReportStore();
|
|
@@ -4622,8 +4787,8 @@ var STORE_FILES = [
|
|
|
4622
4787
|
async function migrateProjectStores(directory = `${process.cwd()}/.omninode`, options = {}) {
|
|
4623
4788
|
const reports = [];
|
|
4624
4789
|
for (const file of STORE_FILES) {
|
|
4625
|
-
const
|
|
4626
|
-
const state = await inspectDocument(
|
|
4790
|
+
const path2 = `${directory}/${file}`;
|
|
4791
|
+
const state = await inspectDocument(path2);
|
|
4627
4792
|
if (state.status === "missing") {
|
|
4628
4793
|
reports.push({ file, state: state.status, migrated: false });
|
|
4629
4794
|
continue;
|
|
@@ -4645,9 +4810,9 @@ async function migrateProjectStores(directory = `${process.cwd()}/.omninode`, op
|
|
|
4645
4810
|
reports.push({ file, state: state.status, migrated: false, note: state.detail });
|
|
4646
4811
|
continue;
|
|
4647
4812
|
}
|
|
4648
|
-
const items = await readDocument(
|
|
4813
|
+
const items = await readDocument(path2, void 0, false);
|
|
4649
4814
|
if (!options.checkOnly) {
|
|
4650
|
-
await writeFileAtomic(
|
|
4815
|
+
await writeFileAtomic(path2, `${JSON.stringify({ schemaVersion: SCHEMA_VERSION, items }, null, 2)}
|
|
4651
4816
|
`);
|
|
4652
4817
|
}
|
|
4653
4818
|
const beforeCount = items.length;
|
|
@@ -4690,7 +4855,7 @@ function registerSecurityCommand(program) {
|
|
|
4690
4855
|
console.log(`Unknown security action "${action}". Available: audit.`);
|
|
4691
4856
|
return;
|
|
4692
4857
|
}
|
|
4693
|
-
const config =
|
|
4858
|
+
const config = loadProjectConfig();
|
|
4694
4859
|
const findings = [];
|
|
4695
4860
|
for (const agent of config.project.agents) {
|
|
4696
4861
|
const policy = resolveEnvPolicy(agent);
|
|
@@ -4751,14 +4916,15 @@ Trust model: agents execute with the permissions of the OS user running OmniNode
|
|
|
4751
4916
|
|
|
4752
4917
|
// src/cli/commands/config.ts
|
|
4753
4918
|
import { readFileSync as readFileSync5 } from "fs";
|
|
4919
|
+
import { parse as parseYaml2 } from "yaml";
|
|
4754
4920
|
function registerConfigCommands(program) {
|
|
4755
4921
|
const config = program.command("config").description("Inspect the project configuration (omninode.yaml).");
|
|
4756
4922
|
config.command("path").description("Print the path of the configuration file in use.").action(() => {
|
|
4757
|
-
const
|
|
4758
|
-
console.log(
|
|
4923
|
+
const path2 = activeConfigPath();
|
|
4924
|
+
console.log(path2 ?? "no omninode.yaml found");
|
|
4759
4925
|
});
|
|
4760
4926
|
config.command("show").description("Print the effective configuration (credential references only, never values).").option("--json", "Emit the configuration as JSON.").action((options) => {
|
|
4761
|
-
const resolved =
|
|
4927
|
+
const resolved = loadConfigDetailed().config;
|
|
4762
4928
|
const payload = {
|
|
4763
4929
|
project: resolved.project.name,
|
|
4764
4930
|
providers: resolved.project.providers.map((p) => ({
|
|
@@ -4784,18 +4950,41 @@ function registerConfigCommands(program) {
|
|
|
4784
4950
|
console.log(`pipelines: ${payload.pipelines.join(", ") || "none"}`);
|
|
4785
4951
|
console.log(`memory: ${payload.memory ? `${payload.memory.provider}${payload.memory.required ? " (required)" : ""}` : "not configured"}`);
|
|
4786
4952
|
});
|
|
4953
|
+
config.command("profiles").description("List the configuration profiles defined in this project.").action(() => {
|
|
4954
|
+
const path2 = activeConfigPath();
|
|
4955
|
+
const raw = path2 ? parseYaml2(readFileSync5(path2, "utf8")) : {};
|
|
4956
|
+
const names = Object.keys(raw.profiles ?? {});
|
|
4957
|
+
const active = loadConfigDetailed().sources.profile;
|
|
4958
|
+
console.log(`active: ${active}`);
|
|
4959
|
+
console.log(`defined: ${names.length > 0 ? names.join(", ") : "none (add a `profiles:` section)"}`);
|
|
4960
|
+
});
|
|
4787
4961
|
config.command("validate").description("Validate the configuration file, including the inline-secret scan.").action(() => {
|
|
4788
|
-
const
|
|
4789
|
-
if (!
|
|
4790
|
-
const findings = scanForInlineSecrets(readFileSync5(
|
|
4962
|
+
const path2 = activeConfigPath();
|
|
4963
|
+
if (!path2) throw new OmniNodeError("CONFIG_NOT_FOUND", "No omninode.yaml found.");
|
|
4964
|
+
const findings = scanForInlineSecrets(readFileSync5(path2, "utf8"));
|
|
4791
4965
|
if (findings.length > 0) {
|
|
4792
4966
|
throw new OmniNodeError(
|
|
4793
4967
|
"CONFIG_INVALID",
|
|
4794
|
-
`${
|
|
4968
|
+
`${path2} contains ${findings.length} inline secret(s) (line ${findings.map((f) => f.line).join(", ")}). Use environment-variable references.`
|
|
4795
4969
|
);
|
|
4796
4970
|
}
|
|
4797
|
-
const resolved =
|
|
4798
|
-
console.log(
|
|
4971
|
+
const { config: resolved, sources } = loadConfigDetailed();
|
|
4972
|
+
console.log(
|
|
4973
|
+
`${path2} is valid \u2014 project "${resolved.project.name}" with ${resolved.project.providers.length} provider(s), ${resolved.project.agents.length} agent(s), ${resolved.project.pipelines.length} pipeline(s).`
|
|
4974
|
+
);
|
|
4975
|
+
console.log(` profile: ${sources.profile}`);
|
|
4976
|
+
if (sources.userFile) console.log(` user cfg: ${sources.userFile}`);
|
|
4977
|
+
if (sources.envOverrides.length > 0) {
|
|
4978
|
+
console.log(` env overrides: ${sources.envOverrides.join(", ")}`);
|
|
4979
|
+
}
|
|
4980
|
+
if (sources.defaults.length > 0) {
|
|
4981
|
+
console.log(` defaults applied: ${sources.defaults.join(", ")}`);
|
|
4982
|
+
}
|
|
4983
|
+
const diagnostics = configDiagnostics(resolved);
|
|
4984
|
+
if (diagnostics.length > 0) {
|
|
4985
|
+
console.log(" diagnostics:");
|
|
4986
|
+
for (const note of diagnostics) console.log(` - ${note}`);
|
|
4987
|
+
}
|
|
4799
4988
|
});
|
|
4800
4989
|
}
|
|
4801
4990
|
|
|
@@ -4821,7 +5010,7 @@ function registerModelGroup(program, name) {
|
|
|
4821
5010
|
`Unknown capability "${options.capability}". Valid capabilities: ${CAPABILITIES.join(", ")}.`
|
|
4822
5011
|
);
|
|
4823
5012
|
}
|
|
4824
|
-
const config =
|
|
5013
|
+
const config = loadProjectConfig();
|
|
4825
5014
|
const selected = config.project.providers.filter(
|
|
4826
5015
|
(p) => providerName ? p.name === providerName : true
|
|
4827
5016
|
);
|
|
@@ -4870,7 +5059,10 @@ function createProgram() {
|
|
|
4870
5059
|
const program = new Command();
|
|
4871
5060
|
program.name("omninode").description(
|
|
4872
5061
|
"Provider-agnostic multi-AI orchestration platform: coordinate CLI agents, AI providers, memory, roles, reports and planning in one execution system."
|
|
4873
|
-
).version(OMNINODE_VERSION, "-v, --version", "Print the OmniNode version.")
|
|
5062
|
+
).version(OMNINODE_VERSION, "-v, --version", "Print the OmniNode version.").option("-c, --config <file>", "Use a specific configuration file instead of ./omninode.yaml.").option("-p, --profile <name>", "Apply a named configuration profile (development/production/testing).").hook("preAction", (thisCommand) => {
|
|
5063
|
+
cliConfigOptions.path = thisCommand.opts().config;
|
|
5064
|
+
cliConfigOptions.profile = thisCommand.opts().profile;
|
|
5065
|
+
});
|
|
4874
5066
|
registerInitCommand(program);
|
|
4875
5067
|
registerProjectCommands(program);
|
|
4876
5068
|
registerProviderCommands(program);
|
|
@@ -4892,10 +5084,8 @@ function createProgram() {
|
|
|
4892
5084
|
}
|
|
4893
5085
|
function applyLoggingConfig() {
|
|
4894
5086
|
try {
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
if (logging) configureLogger(logging);
|
|
4898
|
-
}
|
|
5087
|
+
const resolved = loadProjectConfig();
|
|
5088
|
+
if (resolved.logging) configureLogger(resolved.logging);
|
|
4899
5089
|
} catch {
|
|
4900
5090
|
}
|
|
4901
5091
|
}
|