@alint-js/config 0.3.2 → 0.4.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/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { AlintConfig, AlintConfig as AlintConfig$1, AlintConfigItem, ModelSize, PluginDefinition, ProviderDefinition, ProviderDefinition as ProviderDefinition$1, ProviderType, RunnerConfig, RunnerStatsConfig, SetupConfig, SetupConfig as SetupConfig$1, SetupModelDefinition } from "@alint-js/core";
1
+ import { AlintConfig, AlintConfig as AlintConfig$1, AlintConfigItem, ModelSize, PluginDefinition, ProviderDefinition, ProviderType, ProviderType as ProviderType$1, RunnerConfig, RunnerConfig as RunnerConfig$1, RunnerStatsConfig, SetupConfig as SetupConfig$1, SetupModelDefinition, SetupModelDefinition as SetupModelDefinition$1 } from "@alint-js/core";
2
2
  //#region src/plugins/spec.d.ts
3
3
  interface DirectoryPluginSpecifier {
4
4
  directory: string;
@@ -232,25 +232,66 @@ declare function parsePluginLockFile(value: unknown, options: {
232
232
  }): ParsedPluginLockFile;
233
233
  declare function writePluginLockFile(cwd: string, lockFile: PluginLockFile): Promise<void>;
234
234
  //#endregion
235
+ //#region src/setup/types.d.ts
236
+ interface AcpModelConfig extends SetupModelDefinition$1 {
237
+ args?: string[];
238
+ command: string;
239
+ cwd?: string;
240
+ driver: 'acp';
241
+ env?: Record<string, string>;
242
+ }
243
+ type ModelConfig = AcpModelConfig | SetupModelDefinition$1;
244
+ /** Raw setup-file provider. CLI model drivers are materialized before core receives it. */
245
+ interface ProviderConfig {
246
+ endpoint?: string;
247
+ headers?: Record<string, string>;
248
+ id: string;
249
+ models: ModelConfig[];
250
+ type?: ProviderType$1;
251
+ }
252
+ interface SetupConfig {
253
+ providers: ProviderConfig[];
254
+ runner?: RunnerConfig$1;
255
+ version: 1;
256
+ }
257
+ declare function isAcpModel(model: ModelConfig): model is AcpModelConfig;
258
+ declare function modelFromAcpModel(model: AcpModelConfig): SetupModelDefinition$1;
259
+ //#endregion
235
260
  //#region src/setup/load.d.ts
236
- declare const emptySetupConfig: SetupConfig$1;
237
- declare function loadSetupConfig(filePath: string): Promise<SetupConfig$1>;
238
- declare function mergeSetupConfigs(...configs: SetupConfig$1[]): SetupConfig$1;
261
+ declare const emptySetupConfig: SetupConfig;
262
+ declare function loadSetupConfig(filePath: string): Promise<SetupConfig>;
263
+ declare function mergeSetupConfigs(...configs: SetupConfig[]): SetupConfig;
239
264
  //#endregion
240
265
  //#region src/setup/mutate.d.ts
241
- declare function addProviderModels(config: SetupConfig$1, providerId: string, modelIds: readonly string[]): SetupConfig$1;
242
- declare function pruneProviderModels(config: SetupConfig$1, providerId: string, remoteModelIds: ReadonlySet<string>): SetupConfig$1;
243
- declare function removeProviderModels(config: SetupConfig$1, providerId: string, modelIds: ReadonlySet<string>): SetupConfig$1;
244
- declare function replaceSetupProvider(config: SetupConfig$1, replacement: ProviderDefinition$1): SetupConfig$1;
245
- declare function setProviderEndpoint(config: SetupConfig$1, providerId: string, endpoint: string): SetupConfig$1;
246
- declare function setProviderHeader(config: SetupConfig$1, providerId: string, name: string, value: string): SetupConfig$1;
247
- declare function unsetProviderHeader(config: SetupConfig$1, providerId: string, name: string): SetupConfig$1;
266
+ declare function addProviderModels(config: SetupConfig, providerId: string, modelIds: readonly string[]): SetupConfig;
267
+ declare function pruneProviderModels(config: SetupConfig, providerId: string, remoteModelIds: ReadonlySet<string>): SetupConfig;
268
+ declare function removeProviderModels(config: SetupConfig, providerId: string, modelIds: ReadonlySet<string>): SetupConfig;
269
+ declare function replaceSetupProvider(config: SetupConfig, replacement: ProviderConfig): SetupConfig;
270
+ declare function setProviderEndpoint(config: SetupConfig, providerId: string, endpoint: string): SetupConfig;
271
+ declare function setProviderHeader(config: SetupConfig, providerId: string, name: string, value: string): SetupConfig;
272
+ declare function unsetProviderHeader(config: SetupConfig, providerId: string, name: string): SetupConfig;
273
+ //#endregion
274
+ //#region src/setup/normalize.d.ts
275
+ interface AcpProviderConfig {
276
+ id: string;
277
+ models: AcpModelConfig[];
278
+ }
279
+ interface ModelAdapterProvider {
280
+ endpoint: string;
281
+ headers?: Record<string, string>;
282
+ type: ProviderDefinition['type'];
283
+ }
284
+ interface SetupModelAdapters {
285
+ acp: (provider: AcpProviderConfig) => Promise<ModelAdapterProvider>;
286
+ }
287
+ /** Normalizes setup-file providers while delegating only model-driven transports. */
288
+ declare function normalizeSetupConfig(config: SetupConfig, adapters: SetupModelAdapters): Promise<SetupConfig$1>;
248
289
  //#endregion
249
290
  //#region src/setup/toml.d.ts
250
- declare function parseSetupConfigToml(toml: string): SetupConfig$1;
251
- declare function stringifySetupConfigToml(config: SetupConfig$1): string;
291
+ declare function parseSetupConfigToml(toml: string): SetupConfig;
292
+ declare function stringifySetupConfigToml(config: SetupConfig): string;
252
293
  //#endregion
253
294
  //#region src/setup/write.d.ts
254
- declare function writeSetupConfig(filePath: string, config: SetupConfig$1): Promise<void>;
295
+ declare function writeSetupConfig(filePath: string, config: SetupConfig): Promise<void>;
255
296
  //#endregion
256
- export { type AlintConfig, BuiltInAgentName, CreateDeclarativePluginOptions, CreateReportScopeOptions, DeclarativeFinding, DeclarativeFindingResponse, DeclarativeRuleDefinition, type DirectoryPluginLockEntry, type GlobalSetupConfigPathOptions, LoadDeclarativeRulesOptions, type ModelSize, type ParseStaticConfigOptions, type ParsedDirectoryPluginLockEntry, type ParsedPluginLockEntry, type ParsedPluginLockFile, type ParsedPluginSpecifier, type ParsedRegistryPluginLockEntry, type ParsedStaticConfig, type ParsedStaticConfigGroup, type PluginLockEntry, type PluginLockFile, type ProviderDefinition, type ProviderType, type RegistryPluginLockEntry, ReportScope, type RunnerConfig, type RunnerStatsConfig, type SetupConfig, type SetupModelDefinition, type StaticConfigInput, type StaticConfigItem, type StaticPluginInstallOptions, type StaticPluginInstallResult, type StaticPluginReference, type StaticPluginResolver, type ToAlintConfigOptions, addProviderModels, builtInAgentNames, createDeclarativePlugin, createEmptyPluginLockFile, createReportScope, declarativeFindingResponseSchema, declarativeFindingSchema, declarativeRuleFilePattern, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, hasDeclarativeRuleFiles, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, isBuiltInAgentName, listMissing, listUnresolved, loadAlintConfig, loadDeclarativeRules, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, pruneProviderModels, removeProviderModels, replaceSetupProvider, setProviderEndpoint, setProviderHeader, stringifySetupConfigToml, toAlintConfig, unsetProviderHeader, writePluginLockFile, writeSetupConfig };
297
+ export { type AcpModelConfig, type AcpProviderConfig, type AlintConfig, BuiltInAgentName, CreateDeclarativePluginOptions, CreateReportScopeOptions, DeclarativeFinding, DeclarativeFindingResponse, DeclarativeRuleDefinition, type DirectoryPluginLockEntry, type GlobalSetupConfigPathOptions, LoadDeclarativeRulesOptions, type ModelAdapterProvider, type ModelConfig, type ModelSize, type ParseStaticConfigOptions, type ParsedDirectoryPluginLockEntry, type ParsedPluginLockEntry, type ParsedPluginLockFile, type ParsedPluginSpecifier, type ParsedRegistryPluginLockEntry, type ParsedStaticConfig, type ParsedStaticConfigGroup, type PluginLockEntry, type PluginLockFile, type ProviderConfig, type ProviderConfig as ProviderDefinition, type ProviderType, type RegistryPluginLockEntry, ReportScope, type RunnerConfig, type RunnerStatsConfig, type SetupConfig, type SetupModelAdapters, type SetupModelDefinition, type StaticConfigInput, type StaticConfigItem, type StaticPluginInstallOptions, type StaticPluginInstallResult, type StaticPluginReference, type StaticPluginResolver, type ToAlintConfigOptions, addProviderModels, builtInAgentNames, createDeclarativePlugin, createEmptyPluginLockFile, createReportScope, declarativeFindingResponseSchema, declarativeFindingSchema, declarativeRuleFilePattern, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, hasDeclarativeRuleFiles, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, isAcpModel, isBuiltInAgentName, listMissing, listUnresolved, loadAlintConfig, loadDeclarativeRules, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, modelFromAcpModel, normalizeSetupConfig, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, pruneProviderModels, removeProviderModels, replaceSetupProvider, setProviderEndpoint, setProviderHeader, stringifySetupConfigToml, toAlintConfig, unsetProviderHeader, writePluginLockFile, writeSetupConfig };
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import { loadConfig } from "c12";
3
3
  import { createJiti } from "jiti/static";
4
4
  import { createHash, randomUUID } from "node:crypto";
5
5
  import { access, mkdir, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
6
- import { array, boolean, check, description, literal, looseObject, number, object, optional, parse, picklist, pipe, record, strictObject, string, tupleWithRest, union, unknown, variant } from "valibot";
6
+ import { array, boolean, check, description, finite, integer, literal, looseObject, minValue, nonEmpty, number, object, optional, parse, picklist, pipe, record, strictObject, string, transform, tupleWithRest, undefined_, union, unknown, variant } from "valibot";
7
7
  import process from "node:process";
8
8
  import { homedir } from "node:os";
9
9
  import { dirname as dirname$1, extname, join as join$1, normalize as normalize$1 } from "pathe";
@@ -23,6 +23,7 @@ import tar from "tar-stream";
23
23
  import { ofetch } from "ofetch";
24
24
  import { pathToFileURL } from "node:url";
25
25
  import { isPlainObject } from "es-toolkit/compat";
26
+ import { merge } from "@moeru/std/merge";
26
27
  import { parse as parse$1, stringify } from "smol-toml";
27
28
  //#region src/paths.ts
28
29
  function getGlobalSetupConfigPath(env = process.env, options = {}) {
@@ -156,12 +157,12 @@ async function findDeclarativeRuleFiles(root) {
156
157
  await collectDeclarativeRuleFiles(root, files);
157
158
  return files.sort((left, right) => left.localeCompare(right));
158
159
  }
159
- function isPlainObject$3(value) {
160
+ function isPlainObject$2(value) {
160
161
  return typeof value === "object" && value !== null && !Array.isArray(value);
161
162
  }
162
163
  async function loadDeclarativeRuleFile(filePath) {
163
164
  const input = await loadDeclarativeRuleFileContent(filePath);
164
- if (!isPlainObject$3(input)) throw new Error(`Declarative rule file ${filePath} must contain an object.`);
165
+ if (!isPlainObject$2(input)) throw new Error(`Declarative rule file ${filePath} must contain an object.`);
165
166
  const parsed = parseDeclarativeRuleFileInput(input, filePath);
166
167
  return {
167
168
  builtInAgent: parsed.builtInAgent,
@@ -1086,7 +1087,7 @@ async function toAlintConfig(config, options) {
1086
1087
  const pluginCache = /* @__PURE__ */ new Map();
1087
1088
  return Promise.all(config.groups.map((group) => resolveStaticPlugins(group, options, pluginCache)));
1088
1089
  }
1089
- function isPlainObject$2(value) {
1090
+ function isPlainObject$1(value) {
1090
1091
  return typeof value === "object" && value !== null && !Array.isArray(value);
1091
1092
  }
1092
1093
  function isTomlConfig(configFile) {
@@ -1104,14 +1105,14 @@ function parseStaticConfigItems(value) {
1104
1105
  return value.map(parseStaticConfigItem);
1105
1106
  }
1106
1107
  function readConfigGroup(value) {
1107
- if (!isPlainObject$2(value)) return;
1108
+ if (!isPlainObject$1(value)) return;
1108
1109
  const wrapper = value;
1109
1110
  if (wrapper.config === void 0) return;
1110
- if (!isPlainObject$2(wrapper.config) || !Array.isArray(wrapper.config.group)) throw new TypeError("Static config field \"config.group\" must be an array.");
1111
+ if (!isPlainObject$1(wrapper.config) || !Array.isArray(wrapper.config.group)) throw new TypeError("Static config field \"config.group\" must be an array.");
1111
1112
  return wrapper.config.group;
1112
1113
  }
1113
1114
  function readStaticPluginReferences(item, pluginsByAlias, options) {
1114
- if (!isPlainObject$2(item.plugins)) return [];
1115
+ if (!isPlainObject$1(item.plugins)) return [];
1115
1116
  const references = [];
1116
1117
  for (const [alias, plugin] of Object.entries(item.plugins)) {
1117
1118
  if (typeof plugin !== "string") continue;
@@ -1298,22 +1299,90 @@ async function installStaticPlugins(options) {
1298
1299
  };
1299
1300
  }
1300
1301
  //#endregion
1302
+ //#region src/setup/types.ts
1303
+ function isAcpModel(model) {
1304
+ return "driver" in model && model.driver === "acp";
1305
+ }
1306
+ function modelFromAcpModel(model) {
1307
+ const { args: _, command: _command, cwd: _cwd, driver: _driver, env: _env, ...definition } = model;
1308
+ return definition;
1309
+ }
1310
+ //#endregion
1301
1311
  //#region src/setup/toml.ts
1302
- const modelSizes = /* @__PURE__ */ new Set([
1303
- "large",
1304
- "medium",
1305
- "small"
1306
- ]);
1312
+ const modelEntries = {
1313
+ aliases: optional(array(string())),
1314
+ capabilities: optional(array(string())),
1315
+ context_window: optional(number()),
1316
+ default_params: optional(record(string(), unknown())),
1317
+ driver: optional(undefined_()),
1318
+ id: pipe(string(), nonEmpty("Model id must be a non-empty string.")),
1319
+ name: optional(string()),
1320
+ size: optional(picklist([
1321
+ "large",
1322
+ "medium",
1323
+ "small"
1324
+ ]))
1325
+ };
1326
+ const providerModelSchema = object(modelEntries);
1327
+ const acpModelSchema = object({
1328
+ ...modelEntries,
1329
+ args: optional(array(string())),
1330
+ command: pipe(string(), nonEmpty("ACP model command must be a non-empty string.")),
1331
+ cwd: optional(pipe(string(), nonEmpty("ACP model cwd must be a non-empty string."))),
1332
+ driver: literal("acp"),
1333
+ env: optional(record(string(), string()))
1334
+ });
1335
+ const modelSchema = pipe(variant("driver", [acpModelSchema, providerModelSchema]), transform(({ context_window: contextWindow, default_params: defaultParams, ...model }) => ({
1336
+ ...model,
1337
+ ...contextWindow === void 0 ? {} : { contextWindow },
1338
+ ...defaultParams === void 0 ? {} : { defaultParams }
1339
+ })));
1340
+ const providerSchema = pipe(object({
1341
+ endpoint: optional(pipe(string(), nonEmpty("Provider endpoint must be a non-empty string."))),
1342
+ headers: optional(record(string(), string())),
1343
+ id: pipe(string(), nonEmpty("Provider id must be a non-empty string.")),
1344
+ models: array(modelSchema),
1345
+ type: optional(literal("openai-compatible"))
1346
+ }), check((provider) => provider.models.every(isAcpModel) || provider.type === "openai-compatible", (issue) => `Invalid provider "${issue.input.id}": type is required for models without a driver.`), check((provider) => provider.models.every(isAcpModel) || provider.endpoint !== void 0, (issue) => `Invalid provider "${issue.input.id}": endpoint is required for models without a driver.`));
1347
+ const runnerCacheSchema = union([boolean(), object({
1348
+ enabled: optional(boolean()),
1349
+ location: optional(pipe(string("Invalid runner cache location: must be a string."), nonEmpty("Invalid runner cache location: must be a non-empty string.")))
1350
+ })]);
1351
+ const positiveIntegerSchemas = {
1352
+ retentionMonths: pipe(number("Invalid runner stats retention_months: must be a positive integer."), integer("Invalid runner stats retention_months: must be a positive integer."), minValue(1, "Invalid runner stats retention_months: must be a positive integer.")),
1353
+ ruleConcurrency: pipe(number("Invalid runner rule_concurrency: must be a positive integer."), integer("Invalid runner rule_concurrency: must be a positive integer."), minValue(1, "Invalid runner rule_concurrency: must be a positive integer.")),
1354
+ timeoutMs: pipe(number("Invalid runner timeout_ms: must be a positive integer."), integer("Invalid runner timeout_ms: must be a positive integer."), minValue(1, "Invalid runner timeout_ms: must be a positive integer."))
1355
+ };
1356
+ const runnerStatsSchema = union([boolean(), pipe(object({
1357
+ enabled: optional(boolean()),
1358
+ location: optional(pipe(string("Invalid runner stats location: must be a string."), nonEmpty("Invalid runner stats location: must be a non-empty string."))),
1359
+ retention_months: optional(positiveIntegerSchemas.retentionMonths)
1360
+ }), transform(({ retention_months: retentionMonths, ...stats }) => ({
1361
+ ...stats,
1362
+ ...retentionMonths === void 0 ? {} : { retentionMonths }
1363
+ })))]);
1364
+ const runnerSchema = pipe(object({
1365
+ agent_retries: optional(pipe(number("Invalid runner agent_retries: must be a finite number."), finite("Invalid runner agent_retries: must be a finite number."))),
1366
+ cache: optional(runnerCacheSchema),
1367
+ rule_concurrency: optional(positiveIntegerSchemas.ruleConcurrency),
1368
+ stats: optional(runnerStatsSchema),
1369
+ timeout_ms: optional(positiveIntegerSchemas.timeoutMs)
1370
+ }), transform(({ agent_retries: agentRetries, rule_concurrency: ruleConcurrency, timeout_ms: timeoutMs, ...runner }) => ({
1371
+ ...runner,
1372
+ ...agentRetries === void 0 ? {} : { agentRetries },
1373
+ ...ruleConcurrency === void 0 ? {} : { ruleConcurrency },
1374
+ ...timeoutMs === void 0 ? {} : { timeoutMs }
1375
+ })));
1376
+ const setupConfigSchema = pipe(object({
1377
+ providers: optional(array(providerSchema), []),
1378
+ runner: optional(runnerSchema),
1379
+ version: literal(1, "Invalid setup config: version must be 1.")
1380
+ }), transform(({ runner, ...config }) => ({
1381
+ ...config,
1382
+ ...runner === void 0 ? {} : { runner }
1383
+ })));
1307
1384
  function parseSetupConfigToml(toml) {
1308
- const rawConfig = parse$1(toml);
1309
- if (rawConfig.version !== 1) throw new Error("Invalid setup config: version must be 1.");
1310
- if (!Array.isArray(rawConfig.providers)) throw new TypeError("Invalid setup config: providers must be an array.");
1311
- const parsedConfig = {
1312
- providers: rawConfig.providers.map((provider) => parseProvider(provider)),
1313
- version: 1
1314
- };
1315
- if (rawConfig.runner !== void 0) parsedConfig.runner = parseRunner(rawConfig.runner);
1316
- return parsedConfig;
1385
+ return parse(setupConfigSchema, parse$1(toml));
1317
1386
  }
1318
1387
  function stringifySetupConfigToml(config) {
1319
1388
  const stringifiableConfig = {
@@ -1323,103 +1392,15 @@ function stringifySetupConfigToml(config) {
1323
1392
  if (config.runner !== void 0) stringifiableConfig.runner = toTomlRunner(config.runner);
1324
1393
  return stringify(stringifiableConfig);
1325
1394
  }
1326
- function isModelSize(value) {
1327
- return typeof value === "string" && modelSizes.has(value);
1328
- }
1329
- function isPlainObject$1(value) {
1330
- return typeof value === "object" && value !== null && !Array.isArray(value);
1331
- }
1332
- function parseModel(providerId, model) {
1333
- const id = readNonEmptyString(model.id, `provider "${providerId}" model id`);
1334
- const parsedModel = { id };
1335
- if (model.name !== void 0) parsedModel.name = readString(model.name, `model "${id}" name`);
1336
- if (model.aliases !== void 0) parsedModel.aliases = readStringArray(model.aliases, `model "${id}" aliases`);
1337
- if (model.capabilities !== void 0) parsedModel.capabilities = readStringArray(model.capabilities, `model "${id}" capabilities`);
1338
- if (model.size !== void 0) {
1339
- if (!isModelSize(model.size)) throw new Error(`Invalid model "${id}": size must be "small", "medium", or "large".`);
1340
- parsedModel.size = model.size;
1341
- }
1342
- if (model.context_window !== void 0) parsedModel.contextWindow = readFiniteNumber(model.context_window, `model "${id}" context_window`);
1343
- if (model.default_params !== void 0) parsedModel.defaultParams = readRecord(model.default_params, `model "${id}" default_params`);
1344
- return parsedModel;
1345
- }
1346
- function parsePositiveInteger(value, label) {
1347
- if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) throw new TypeError(`Invalid ${label}: must be a positive integer.`);
1348
- return value;
1349
- }
1350
- function parseProvider(provider) {
1351
- const id = readNonEmptyString(provider.id, "provider id");
1352
- if (provider.type !== "openai-compatible") throw new Error(`Invalid provider "${id}": type must be "openai-compatible".`);
1353
- const endpoint = readNonEmptyString(provider.endpoint, `provider "${id}" endpoint`);
1354
- if (!Array.isArray(provider.models)) throw new TypeError(`Invalid provider "${id}": models must be an array.`);
1355
- const parsedProvider = {
1356
- endpoint,
1357
- id,
1358
- models: provider.models.map((model) => parseModel(id, model)),
1359
- type: provider.type
1395
+ function toTomlAcpModel(model) {
1396
+ return {
1397
+ ...toTomlModel(model),
1398
+ args: model.args,
1399
+ command: model.command,
1400
+ cwd: model.cwd,
1401
+ driver: "acp",
1402
+ env: model.env
1360
1403
  };
1361
- if (provider.headers !== void 0) parsedProvider.headers = readStringMap(provider.headers, `provider "${id}" headers`);
1362
- return parsedProvider;
1363
- }
1364
- function parseRunner(runner) {
1365
- const parsedRunner = {};
1366
- if (runner.agent_retries !== void 0) parsedRunner.agentRetries = readFiniteNumber(runner.agent_retries, "runner agent_retries");
1367
- if (runner.cache !== void 0) parsedRunner.cache = parseRunnerCache(runner.cache);
1368
- if (runner.rule_concurrency !== void 0) parsedRunner.ruleConcurrency = parsePositiveInteger(runner.rule_concurrency, "runner rule_concurrency");
1369
- if (runner.stats !== void 0) parsedRunner.stats = parseRunnerStats(runner.stats);
1370
- if (runner.timeout_ms !== void 0) parsedRunner.timeoutMs = parsePositiveInteger(runner.timeout_ms, "runner timeout_ms");
1371
- return parsedRunner;
1372
- }
1373
- function parseRunnerCache(cache) {
1374
- if (typeof cache === "boolean") return cache;
1375
- if (!isPlainObject$1(cache)) throw new TypeError("Invalid runner cache: must be a boolean or table.");
1376
- const tomlCache = cache;
1377
- const parsedCache = {};
1378
- if (tomlCache.enabled !== void 0) {
1379
- if (typeof tomlCache.enabled !== "boolean") throw new TypeError("Invalid runner cache enabled: must be a boolean.");
1380
- parsedCache.enabled = tomlCache.enabled;
1381
- }
1382
- if (tomlCache.location !== void 0) parsedCache.location = readNonEmptyString(tomlCache.location, "runner cache location");
1383
- return parsedCache;
1384
- }
1385
- function parseRunnerStats(stats) {
1386
- if (typeof stats === "boolean") return stats;
1387
- if (!isPlainObject$1(stats)) throw new TypeError("Invalid runner stats: must be a boolean or an object.");
1388
- const tomlStats = stats;
1389
- const parsedStats = {};
1390
- if (tomlStats.enabled !== void 0) {
1391
- if (typeof tomlStats.enabled !== "boolean") throw new TypeError("Invalid runner stats enabled: must be a boolean.");
1392
- parsedStats.enabled = tomlStats.enabled;
1393
- }
1394
- if (tomlStats.location !== void 0) parsedStats.location = readNonEmptyString(tomlStats.location, "runner stats location");
1395
- if (tomlStats.retention_months !== void 0) parsedStats.retentionMonths = parsePositiveInteger(tomlStats.retention_months, "runner stats retention_months");
1396
- return parsedStats;
1397
- }
1398
- function readFiniteNumber(value, label) {
1399
- if (typeof value !== "number" || !Number.isFinite(value)) throw new TypeError(`Invalid ${label}: must be a finite number.`);
1400
- return value;
1401
- }
1402
- function readNonEmptyString(value, label) {
1403
- const stringValue = readString(value, label);
1404
- if (stringValue.length === 0) throw new Error(`Invalid ${label}: must be a non-empty string.`);
1405
- return stringValue;
1406
- }
1407
- function readRecord(value, label) {
1408
- if (!isPlainObject$1(value)) throw new Error(`Invalid ${label}: must be a table.`);
1409
- return value;
1410
- }
1411
- function readString(value, label) {
1412
- if (typeof value !== "string") throw new TypeError(`Invalid ${label}: must be a string.`);
1413
- return value;
1414
- }
1415
- function readStringArray(value, label) {
1416
- if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) throw new Error(`Invalid ${label}: must be an array of strings.`);
1417
- return value;
1418
- }
1419
- function readStringMap(value, label) {
1420
- const record = readRecord(value, label);
1421
- if (!Object.values(record).every((item) => typeof item === "string")) throw new Error(`Invalid ${label}: must be a string map.`);
1422
- return record;
1423
1404
  }
1424
1405
  function toTomlModel(model) {
1425
1406
  const tomlModel = { id: model.id };
@@ -1435,7 +1416,7 @@ function toTomlProvider(provider) {
1435
1416
  const tomlProvider = {
1436
1417
  endpoint: provider.endpoint,
1437
1418
  id: provider.id,
1438
- models: provider.models.map(toTomlModel),
1419
+ models: provider.models.map((model) => "driver" in model && model.driver === "acp" ? toTomlAcpModel(model) : toTomlModel(model)),
1439
1420
  type: provider.type
1440
1421
  };
1441
1422
  if (provider.headers !== void 0) tomlProvider.headers = provider.headers;
@@ -1494,8 +1475,8 @@ function mergeSetupConfigs(...configs) {
1494
1475
  configProviders.push(mergedProvider);
1495
1476
  continue;
1496
1477
  }
1497
- existingProvider.endpoint = provider.endpoint;
1498
- existingProvider.type = provider.type;
1478
+ if (provider.endpoint !== void 0) existingProvider.endpoint = provider.endpoint;
1479
+ if (provider.type !== void 0) existingProvider.type = provider.type;
1499
1480
  configProviders.push(existingProvider);
1500
1481
  if (provider.headers !== void 0) existingProvider.headers = {
1501
1482
  ...existingProvider.headers,
@@ -1505,7 +1486,7 @@ function mergeSetupConfigs(...configs) {
1505
1486
  for (const incomingModel of provider.models) {
1506
1487
  const existingModelIndex = existingProvider.models.findIndex((model) => model.id === incomingModel.id);
1507
1488
  if (existingModelIndex === -1) {
1508
- nextModels.push(cloneModel$1(incomingModel));
1489
+ nextModels.push(cloneModel$2(incomingModel));
1509
1490
  continue;
1510
1491
  }
1511
1492
  const existingModel = existingProvider.models[existingModelIndex];
@@ -1523,8 +1504,12 @@ function mergeSetupConfigs(...configs) {
1523
1504
  if (runner !== void 0) mergedConfig.runner = runner;
1524
1505
  return mergedConfig;
1525
1506
  }
1526
- function cloneModel$1(model) {
1527
- const clonedModel = { ...model };
1507
+ function cloneModel$2(model) {
1508
+ const clonedModel = isAcpModel(model) ? {
1509
+ ...model,
1510
+ args: model.args ? [...model.args] : void 0,
1511
+ env: model.env ? { ...model.env } : void 0
1512
+ } : { ...model };
1528
1513
  if (model.aliases !== void 0) clonedModel.aliases = [...model.aliases];
1529
1514
  if (model.capabilities !== void 0) clonedModel.capabilities = [...model.capabilities];
1530
1515
  if (model.defaultParams !== void 0) clonedModel.defaultParams = { ...model.defaultParams };
@@ -1533,7 +1518,7 @@ function cloneModel$1(model) {
1533
1518
  function cloneProvider$1(provider) {
1534
1519
  const clonedProvider = {
1535
1520
  ...provider,
1536
- models: provider.models.map(cloneModel$1)
1521
+ models: provider.models.map(cloneModel$2)
1537
1522
  };
1538
1523
  if (provider.headers !== void 0) clonedProvider.headers = { ...provider.headers };
1539
1524
  return clonedProvider;
@@ -1545,15 +1530,9 @@ function createEmptySetupConfig() {
1545
1530
  };
1546
1531
  }
1547
1532
  function mergeModel(existingModel, incomingModel) {
1548
- const existing = cloneModel$1(existingModel);
1549
- const incoming = cloneModel$1(incomingModel);
1550
- return {
1551
- ...existing,
1552
- ...incoming,
1553
- aliases: incoming.aliases ?? existing.aliases,
1554
- capabilities: incoming.capabilities ?? existing.capabilities,
1555
- defaultParams: incoming.defaultParams ?? existing.defaultParams
1556
- };
1533
+ const existing = cloneModel$2(existingModel);
1534
+ const incoming = cloneModel$2(incomingModel);
1535
+ return merge(existing, incoming);
1557
1536
  }
1558
1537
  function prioritizeProviders(providers, prioritizedProviders) {
1559
1538
  const prioritizedProviderIds = new Set(prioritizedProviders.map((provider) => provider.id));
@@ -1582,7 +1561,7 @@ function addProviderModels(config, providerId, modelIds) {
1582
1561
  function pruneProviderModels(config, providerId, remoteModelIds) {
1583
1562
  return updateProvider(config, providerId, (provider) => ({
1584
1563
  ...provider,
1585
- models: provider.models.filter((model) => remoteModelIds.has(model.id))
1564
+ models: provider.models.filter((model) => isAcpModel(model) || remoteModelIds.has(model.id))
1586
1565
  }));
1587
1566
  }
1588
1567
  function removeProviderModels(config, providerId, modelIds) {
@@ -1619,7 +1598,7 @@ function unsetProviderHeader(config, providerId, name) {
1619
1598
  };
1620
1599
  });
1621
1600
  }
1622
- function cloneModel(model) {
1601
+ function cloneModel$1(model) {
1623
1602
  return {
1624
1603
  ...model,
1625
1604
  aliases: model.aliases === void 0 ? void 0 : [...model.aliases],
@@ -1631,7 +1610,7 @@ function cloneProvider(provider) {
1631
1610
  return {
1632
1611
  ...provider,
1633
1612
  headers: provider.headers === void 0 ? void 0 : { ...provider.headers },
1634
- models: provider.models.map(cloneModel)
1613
+ models: provider.models.map(cloneModel$1)
1635
1614
  };
1636
1615
  }
1637
1616
  function headersWithoutName(headers, name) {
@@ -1650,10 +1629,94 @@ function updateProvider(config, providerId, update) {
1650
1629
  };
1651
1630
  }
1652
1631
  //#endregion
1632
+ //#region src/setup/normalize.ts
1633
+ /** Normalizes setup-file providers while delegating only model-driven transports. */
1634
+ async function normalizeSetupConfig(config, adapters) {
1635
+ const providers = [];
1636
+ for (const provider of config.providers) {
1637
+ const acpModels = provider.models.filter(isAcpModel);
1638
+ if (acpModels.length === 0) {
1639
+ if (provider.models.length > 0) {
1640
+ const normalizedProvider = providerFromConfig(provider);
1641
+ normalizedProvider.models.push(...provider.models.map(cloneModel));
1642
+ providers.push(normalizedProvider);
1643
+ }
1644
+ continue;
1645
+ }
1646
+ const acpProvider = await adapters.acp({
1647
+ id: provider.id,
1648
+ models: acpModels.map(cloneAcpModel)
1649
+ });
1650
+ let activeDriver;
1651
+ let activeProvider;
1652
+ for (const model of provider.models) {
1653
+ const driver = isAcpModel(model) ? "acp" : "provider";
1654
+ const normalizedModel = cloneModel(isAcpModel(model) ? modelFromAcpModel(model) : model);
1655
+ if (driver === activeDriver && activeProvider !== void 0) {
1656
+ activeProvider.models.push(normalizedModel);
1657
+ continue;
1658
+ }
1659
+ activeDriver = driver;
1660
+ activeProvider = driver === "acp" ? providerFromAdapter(provider.id, acpProvider) : providerFromConfig(provider);
1661
+ activeProvider.models.push(normalizedModel);
1662
+ providers.push(activeProvider);
1663
+ }
1664
+ }
1665
+ return {
1666
+ providers,
1667
+ ...config.runner === void 0 ? {} : { runner: cloneRunner(config.runner) },
1668
+ version: 1
1669
+ };
1670
+ }
1671
+ function cloneAcpModel(model) {
1672
+ return {
1673
+ ...model,
1674
+ aliases: model.aliases ? [...model.aliases] : void 0,
1675
+ args: model.args ? [...model.args] : void 0,
1676
+ capabilities: model.capabilities ? [...model.capabilities] : void 0,
1677
+ defaultParams: model.defaultParams ? { ...model.defaultParams } : void 0,
1678
+ env: model.env ? { ...model.env } : void 0
1679
+ };
1680
+ }
1681
+ function cloneModel(model) {
1682
+ return {
1683
+ ...model,
1684
+ aliases: model.aliases ? [...model.aliases] : void 0,
1685
+ capabilities: model.capabilities ? [...model.capabilities] : void 0,
1686
+ defaultParams: model.defaultParams ? { ...model.defaultParams } : void 0
1687
+ };
1688
+ }
1689
+ function cloneRunner(runner) {
1690
+ return {
1691
+ ...runner,
1692
+ cache: typeof runner.cache === "object" ? { ...runner.cache } : runner.cache,
1693
+ stats: typeof runner.stats === "object" ? { ...runner.stats } : runner.stats
1694
+ };
1695
+ }
1696
+ function providerFromAdapter(id, provider) {
1697
+ return {
1698
+ ...provider,
1699
+ headers: provider.headers ? { ...provider.headers } : void 0,
1700
+ id,
1701
+ models: []
1702
+ };
1703
+ }
1704
+ function providerFromConfig(provider) {
1705
+ if (provider.endpoint === void 0) throw new Error(`Provider "${provider.id}" requires endpoint for models without a driver.`);
1706
+ if (provider.type === void 0) throw new Error(`Provider "${provider.id}" requires type for models without a driver.`);
1707
+ return {
1708
+ endpoint: provider.endpoint,
1709
+ headers: provider.headers ? { ...provider.headers } : void 0,
1710
+ id: provider.id,
1711
+ models: [],
1712
+ type: provider.type
1713
+ };
1714
+ }
1715
+ //#endregion
1653
1716
  //#region src/setup/write.ts
1654
1717
  async function writeSetupConfig(filePath, config) {
1655
1718
  await mkdir(dirname$1(filePath), { recursive: true });
1656
1719
  await writeFile(filePath, stringifySetupConfigToml(config), "utf8");
1657
1720
  }
1658
1721
  //#endregion
1659
- export { addProviderModels, builtInAgentNames, createDeclarativePlugin, createEmptyPluginLockFile, createReportScope, declarativeFindingResponseSchema, declarativeFindingSchema, declarativeRuleFilePattern, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, hasDeclarativeRuleFiles, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, isBuiltInAgentName, listMissing, listUnresolved, loadAlintConfig, loadDeclarativeRules, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, pruneProviderModels, removeProviderModels, replaceSetupProvider, setProviderEndpoint, setProviderHeader, stringifySetupConfigToml, toAlintConfig, unsetProviderHeader, writePluginLockFile, writeSetupConfig };
1722
+ export { addProviderModels, builtInAgentNames, createDeclarativePlugin, createEmptyPluginLockFile, createReportScope, declarativeFindingResponseSchema, declarativeFindingSchema, declarativeRuleFilePattern, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, hasDeclarativeRuleFiles, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, isAcpModel, isBuiltInAgentName, listMissing, listUnresolved, loadAlintConfig, loadDeclarativeRules, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, modelFromAcpModel, normalizeSetupConfig, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, pruneProviderModels, removeProviderModels, replaceSetupProvider, setProviderEndpoint, setProviderHeader, stringifySetupConfigToml, toAlintConfig, unsetProviderHeader, writePluginLockFile, writeSetupConfig };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alint-js/config",
3
3
  "type": "module",
4
- "version": "0.3.2",
4
+ "version": "0.4.0",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.mts",
@@ -28,8 +28,8 @@
28
28
  "tar-stream": "^3.2.0",
29
29
  "valibot": "^1.4.2",
30
30
  "xdg-basedir": "^5.1.0",
31
- "@alint-js/core": "0.3.2",
32
- "@alint-js/tools-fs": "0.3.2"
31
+ "@alint-js/core": "0.4.0",
32
+ "@alint-js/tools-fs": "0.4.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/tar-stream": "^3.1.4",