@alint-js/cli 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/README.md +29 -0
- package/dist/bin/index.mjs +1 -1
- package/dist/{cli-CP8pWPS7.mjs → cli-DFWnPI6m.mjs} +337 -50
- package/dist/index.mjs +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -244,6 +244,35 @@ thinking = { type = "disabled" }
|
|
|
244
244
|
|
|
245
245
|
`default_params` is merged into every chat request for that model. Use it for provider-specific request fields that are not covered by the rest of the model entry.
|
|
246
246
|
|
|
247
|
+
The CLI can also launch an ACP coding-agent command and expose it to rules as an ordinary model. Interactive `alint setup` provides presets for Claude Code, Codex, Gemini CLI, Kimi Code CLI, and OpenCode. Put a custom machine-specific command in the global setup config, or in `.alint/config.toml` when the repository standardizes the same command for every contributor:
|
|
248
|
+
|
|
249
|
+
```toml
|
|
250
|
+
version = 1
|
|
251
|
+
|
|
252
|
+
[[providers]]
|
|
253
|
+
id = "acp"
|
|
254
|
+
|
|
255
|
+
[[providers.models]]
|
|
256
|
+
id = "codex"
|
|
257
|
+
name = "Codex ACP"
|
|
258
|
+
driver = "acp"
|
|
259
|
+
aliases = [ "default" ]
|
|
260
|
+
capabilities = [ "tool-call" ]
|
|
261
|
+
command = "codex-acp"
|
|
262
|
+
args = []
|
|
263
|
+
cwd = "."
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Then select it like any other model:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
alint --model acp/codex src
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
`alint.config.ts` remains the shareable lint policy: it selects models, plugins, and rules but does not launch processes. The CLI merges global setup with `.alint/config.toml`, starts a loopback OpenAI-compatible adapter for the run, and shuts it down afterward. Each concurrent model request gets its own ACP process, so `--rule-concurrency` also bounds how many coding-agent processes a run can require.
|
|
273
|
+
|
|
274
|
+
The command inherits the environment that launched `alint`. Optional `[providers.models.env]` entries override individual variables, but setup TOML does not interpolate variables. Do not commit credentials into the table. Request tools require the ACP agent to support MCP over HTTP.
|
|
275
|
+
|
|
247
276
|
`alint` structured output forces a tool call (`tool_choice`). Some models, such as DeepSeek V4, reject that combination while thinking/reasoning is enabled. For those models, set `thinking = { type = "disabled" }` as above so structured output can run.
|
|
248
277
|
|
|
249
278
|
Note that `-N` stands for `--no-interactive`, which means this is not an interactive setup. TUI is not required, so you can ask Codex or Claude Code to run this command for you.
|
package/dist/bin/index.mjs
CHANGED
|
@@ -2,12 +2,13 @@ import process from "node:process";
|
|
|
2
2
|
import { inspect } from "node:util";
|
|
3
3
|
import { cac } from "cac";
|
|
4
4
|
import { appendFile, mkdir, opendir, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
5
|
-
import { addProviderModels, getGlobalSetupConfigPath, getProjectSetupConfigPath, getStatsDir, installStaticPlugins, loadAlintConfig, loadSetupConfig, mergeSetupConfigs, pruneProviderModels, removeProviderModels, replaceSetupProvider, setProviderEndpoint, setProviderHeader, unsetProviderHeader, writeSetupConfig } from "@alint-js/config";
|
|
5
|
+
import { addProviderModels, getGlobalSetupConfigPath, getProjectSetupConfigPath, getStatsDir, installStaticPlugins, isAcpModel, loadAlintConfig, loadSetupConfig, mergeSetupConfigs, normalizeSetupConfig, pruneProviderModels, removeProviderModels, replaceSetupProvider, setProviderEndpoint, setProviderHeader, unsetProviderHeader, writeSetupConfig } from "@alint-js/config";
|
|
6
6
|
import { AlintRunCancelledError, AlintRunError, benchmarkModels, hasDiscoveryFilePatterns, matchesDiscoveryFile, normalizeConfig, resolveConfigForDirectory, resolveConfigForFile, runAlint } from "@alint-js/core";
|
|
7
7
|
import { isAbsolute, join, relative, resolve } from "pathe";
|
|
8
8
|
import { errorMessageFrom } from "@moeru/std/error";
|
|
9
9
|
import { getBorderCharacters, table } from "table";
|
|
10
10
|
import { createColors } from "tinyrainbow";
|
|
11
|
+
import { createCommandModel, startGateway } from "@alint-js/model-adapter-acp";
|
|
11
12
|
import cliSpinners from "cli-spinners";
|
|
12
13
|
import fastStringWidth from "fast-string-width";
|
|
13
14
|
import { relative as relative$1 } from "node:path";
|
|
@@ -17,7 +18,7 @@ import Gitignore from "gitignore-fs";
|
|
|
17
18
|
import { Minimatch, minimatch } from "minimatch";
|
|
18
19
|
import { errorMessageFrom as errorMessageFrom$1 } from "@moeru/std";
|
|
19
20
|
//#region package.json
|
|
20
|
-
var version = "0.
|
|
21
|
+
var version = "0.4.0";
|
|
21
22
|
//#endregion
|
|
22
23
|
//#region src/cli/output.ts
|
|
23
24
|
function escapeLineValue(value) {
|
|
@@ -296,12 +297,14 @@ const providerSetupSources = [
|
|
|
296
297
|
benchmarkConcurrency: 2,
|
|
297
298
|
defaultEndpoint: "http://127.0.0.1:8317/v1",
|
|
298
299
|
defaultProviderId: "CLIProxyAPI",
|
|
300
|
+
kind: "openai-compatible",
|
|
299
301
|
label: "CLIProxyAPI",
|
|
300
302
|
probeModels: true,
|
|
301
303
|
value: "cliProxyApi"
|
|
302
304
|
},
|
|
303
305
|
{
|
|
304
306
|
benchmarkConcurrency: 2,
|
|
307
|
+
kind: "openai-compatible",
|
|
305
308
|
label: "Custom OpenAI-compatible provider",
|
|
306
309
|
probeModels: true,
|
|
307
310
|
value: "custom"
|
|
@@ -310,6 +313,7 @@ const providerSetupSources = [
|
|
|
310
313
|
benchmarkConcurrency: 20,
|
|
311
314
|
defaultEndpoint: "https://openrouter.ai/api/v1",
|
|
312
315
|
defaultProviderId: "openrouter",
|
|
316
|
+
kind: "openai-compatible",
|
|
313
317
|
label: "OpenRouter",
|
|
314
318
|
probeModels: true,
|
|
315
319
|
value: "openrouter"
|
|
@@ -318,6 +322,7 @@ const providerSetupSources = [
|
|
|
318
322
|
benchmarkConcurrency: 2,
|
|
319
323
|
defaultEndpoint: "https://api.cerebras.ai/v1",
|
|
320
324
|
defaultProviderId: "cerebras",
|
|
325
|
+
kind: "openai-compatible",
|
|
321
326
|
label: "Cerebras",
|
|
322
327
|
probeModels: true,
|
|
323
328
|
value: "cerebras"
|
|
@@ -326,6 +331,7 @@ const providerSetupSources = [
|
|
|
326
331
|
benchmarkConcurrency: 2,
|
|
327
332
|
defaultEndpoint: "https://api.groq.com/openai/v1",
|
|
328
333
|
defaultProviderId: "groq",
|
|
334
|
+
kind: "openai-compatible",
|
|
329
335
|
label: "Groq",
|
|
330
336
|
probeModels: true,
|
|
331
337
|
value: "groq"
|
|
@@ -333,15 +339,81 @@ const providerSetupSources = [
|
|
|
333
339
|
{
|
|
334
340
|
benchmarkConcurrency: 2,
|
|
335
341
|
defaultEndpoint: "http://localhost:11434/v1",
|
|
342
|
+
kind: "openai-compatible",
|
|
336
343
|
label: "Ollama",
|
|
337
344
|
probeModels: true,
|
|
338
345
|
value: "ollama"
|
|
339
346
|
},
|
|
340
347
|
{
|
|
341
348
|
benchmarkConcurrency: 2,
|
|
349
|
+
kind: "openai-compatible",
|
|
342
350
|
label: "Manual model entry",
|
|
343
351
|
probeModels: false,
|
|
344
352
|
value: "manual"
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
args: ["-y", "@agentclientprotocol/claude-agent-acp"],
|
|
356
|
+
command: "npx",
|
|
357
|
+
defaultProviderId: "claude-code",
|
|
358
|
+
kind: "acp",
|
|
359
|
+
label: "Claude Code (ACP)",
|
|
360
|
+
model: {
|
|
361
|
+
id: "claude-code",
|
|
362
|
+
name: "Claude Code"
|
|
363
|
+
},
|
|
364
|
+
value: "claudeCodeAcp"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
args: ["-y", "@agentclientprotocol/codex-acp"],
|
|
368
|
+
command: "npx",
|
|
369
|
+
defaultProviderId: "codex",
|
|
370
|
+
kind: "acp",
|
|
371
|
+
label: "Codex (ACP)",
|
|
372
|
+
model: {
|
|
373
|
+
id: "codex",
|
|
374
|
+
name: "Codex"
|
|
375
|
+
},
|
|
376
|
+
value: "codexAcp"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
args: [
|
|
380
|
+
"-y",
|
|
381
|
+
"@google/gemini-cli",
|
|
382
|
+
"--acp"
|
|
383
|
+
],
|
|
384
|
+
command: "npx",
|
|
385
|
+
defaultProviderId: "gemini-cli",
|
|
386
|
+
kind: "acp",
|
|
387
|
+
label: "Gemini CLI (ACP)",
|
|
388
|
+
model: {
|
|
389
|
+
id: "gemini-cli",
|
|
390
|
+
name: "Gemini CLI"
|
|
391
|
+
},
|
|
392
|
+
value: "geminiCliAcp"
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
args: ["acp"],
|
|
396
|
+
command: "kimi",
|
|
397
|
+
defaultProviderId: "kimi-code",
|
|
398
|
+
kind: "acp",
|
|
399
|
+
label: "Kimi Code CLI (ACP)",
|
|
400
|
+
model: {
|
|
401
|
+
id: "kimi-code",
|
|
402
|
+
name: "Kimi Code CLI"
|
|
403
|
+
},
|
|
404
|
+
value: "kimiCliAcp"
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
args: ["acp"],
|
|
408
|
+
command: "opencode",
|
|
409
|
+
defaultProviderId: "opencode",
|
|
410
|
+
kind: "acp",
|
|
411
|
+
label: "OpenCode (ACP)",
|
|
412
|
+
model: {
|
|
413
|
+
id: "opencode",
|
|
414
|
+
name: "OpenCode"
|
|
415
|
+
},
|
|
416
|
+
value: "openCodeAcp"
|
|
345
417
|
}
|
|
346
418
|
];
|
|
347
419
|
function buildModelsUrl(endpoint) {
|
|
@@ -379,7 +451,7 @@ function findProviderSetupSource(value) {
|
|
|
379
451
|
}
|
|
380
452
|
function findProviderSetupSourceByEndpoint(endpoint) {
|
|
381
453
|
const normalizedEndpoint = endpoint instanceof URL ? endpoint.toString() : new URL(endpoint).toString();
|
|
382
|
-
return providerSetupSources.find((source) => source.defaultEndpoint !== void 0 && new URL(source.defaultEndpoint).toString() === normalizedEndpoint);
|
|
454
|
+
return providerSetupSources.find((source) => source.kind === "openai-compatible" && source.defaultEndpoint !== void 0 && new URL(source.defaultEndpoint).toString() === normalizedEndpoint);
|
|
383
455
|
}
|
|
384
456
|
function flattenModels(config) {
|
|
385
457
|
return config.providers.flatMap((provider) => provider.models.map((model) => ({
|
|
@@ -520,16 +592,16 @@ function formatProviderList(config) {
|
|
|
520
592
|
"models"
|
|
521
593
|
], ...config.providers.map((provider) => [
|
|
522
594
|
provider.id,
|
|
523
|
-
provider.type,
|
|
524
|
-
provider.endpoint,
|
|
595
|
+
provider.type ?? "model-driven",
|
|
596
|
+
provider.endpoint ?? "-",
|
|
525
597
|
String(provider.models.length)
|
|
526
598
|
])]);
|
|
527
599
|
}
|
|
528
600
|
function formatProviderShow(provider) {
|
|
529
601
|
const lines = [
|
|
530
602
|
`id: ${provider.id}`,
|
|
531
|
-
`type: ${provider.type}`,
|
|
532
|
-
`endpoint: ${provider.endpoint}`,
|
|
603
|
+
`type: ${provider.type ?? "model-driven"}`,
|
|
604
|
+
`endpoint: ${provider.endpoint ?? "-"}`,
|
|
533
605
|
`models: ${provider.models.map((model) => model.id).join(", ")}`
|
|
534
606
|
];
|
|
535
607
|
const headerKeys = Object.keys(provider.headers ?? {});
|
|
@@ -560,7 +632,7 @@ async function probeModels(endpoint, headers = {}) {
|
|
|
560
632
|
return body.data.map((model) => model.id);
|
|
561
633
|
}
|
|
562
634
|
function resolveProviderBenchmarkConcurrency(config, overrides = []) {
|
|
563
|
-
const concurrency = Object.fromEntries(config.providers.map((provider) => [provider.id, findProviderSetupSourceByEndpoint(provider.endpoint)?.benchmarkConcurrency ?? defaultBenchmarkConcurrency]));
|
|
635
|
+
const concurrency = Object.fromEntries(config.providers.map((provider) => [provider.id, provider.endpoint === void 0 ? defaultBenchmarkConcurrency : findProviderSetupSourceByEndpoint(provider.endpoint)?.benchmarkConcurrency ?? defaultBenchmarkConcurrency]));
|
|
564
636
|
for (const override of overrides) {
|
|
565
637
|
const separatorIndex = override.indexOf("=");
|
|
566
638
|
if (separatorIndex <= 0 || separatorIndex === override.length - 1) throw new Error("Invalid benchmark concurrency override. Expected <provider-id>=<limit>.");
|
|
@@ -645,6 +717,89 @@ function stripTrailingZeros(value) {
|
|
|
645
717
|
return String(Number(value));
|
|
646
718
|
}
|
|
647
719
|
//#endregion
|
|
720
|
+
//#region src/cli/runtime/model-adapter.ts
|
|
721
|
+
/**
|
|
722
|
+
* Normalizes file configuration and starts its model adapters.
|
|
723
|
+
*
|
|
724
|
+
* Triggering workflow:
|
|
725
|
+
*
|
|
726
|
+
* `runLintCommand | runListModelsCommand`
|
|
727
|
+
* -> {@link startModelAdapters}
|
|
728
|
+
* -> {@link createAcpAdapter}
|
|
729
|
+
* -> {@link normalizeSetupConfig}
|
|
730
|
+
* -> `AcpAdapter.provider`
|
|
731
|
+
* -> {@link startGateway}
|
|
732
|
+
*
|
|
733
|
+
* Upstream:
|
|
734
|
+
* - `runLintCommand`
|
|
735
|
+
* - `runListModelsCommand`
|
|
736
|
+
*
|
|
737
|
+
* Downstream:
|
|
738
|
+
* - {@link normalizeSetupConfig}
|
|
739
|
+
* - {@link startGateway}
|
|
740
|
+
*/
|
|
741
|
+
async function startModelAdapters(config, io) {
|
|
742
|
+
const acp = createAcpAdapter(io);
|
|
743
|
+
try {
|
|
744
|
+
return {
|
|
745
|
+
setupConfig: await normalizeSetupConfig(config, { acp: acp.provider }),
|
|
746
|
+
shutdown: acp.shutdown
|
|
747
|
+
};
|
|
748
|
+
} catch (error) {
|
|
749
|
+
await acp.shutdown();
|
|
750
|
+
throw error;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
/** Owns the ACP processes and loopback gateways used by one CLI operation. */
|
|
754
|
+
function createAcpAdapter(io) {
|
|
755
|
+
const shutdowns = [];
|
|
756
|
+
return {
|
|
757
|
+
provider: async (provider) => {
|
|
758
|
+
const gateway = await startGateway({
|
|
759
|
+
cwd: io.cwd,
|
|
760
|
+
models: provider.models.map((model) => createCommandModel({
|
|
761
|
+
args: model.args,
|
|
762
|
+
command: model.command,
|
|
763
|
+
cwd: resolve(io.cwd, model.cwd ?? "."),
|
|
764
|
+
env: {
|
|
765
|
+
...io.env ?? process.env,
|
|
766
|
+
...model.env
|
|
767
|
+
},
|
|
768
|
+
id: model.id,
|
|
769
|
+
name: model.name ?? model.id,
|
|
770
|
+
onStderr: createStderrWriter(io, model.id)
|
|
771
|
+
}))
|
|
772
|
+
});
|
|
773
|
+
shutdowns.push(gateway.shutdown);
|
|
774
|
+
return {
|
|
775
|
+
endpoint: gateway.endpoint,
|
|
776
|
+
type: "openai-compatible"
|
|
777
|
+
};
|
|
778
|
+
},
|
|
779
|
+
shutdown: async () => {
|
|
780
|
+
await Promise.all(shutdowns.map((shutdown) => shutdown()));
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Creates the CLI stderr sink for non-protocol output emitted by an ACP command.
|
|
786
|
+
*
|
|
787
|
+
* Triggering workflow:
|
|
788
|
+
*
|
|
789
|
+
* {@link createCommandModel}
|
|
790
|
+
* -> `ChildProcess.stderr:data`
|
|
791
|
+
* -> {@link createStderrWriter}
|
|
792
|
+
*
|
|
793
|
+
* Upstream:
|
|
794
|
+
* - {@link createAcpAdapter}
|
|
795
|
+
*
|
|
796
|
+
* Downstream:
|
|
797
|
+
* - {@link CliIo.stderr}
|
|
798
|
+
*/
|
|
799
|
+
function createStderrWriter(io, modelId) {
|
|
800
|
+
return (text) => io.stderr.write(`[acp:${modelId}] ${text}`);
|
|
801
|
+
}
|
|
802
|
+
//#endregion
|
|
648
803
|
//#region src/cli/commands/config/setup-config.ts
|
|
649
804
|
function formatUnknownProvider(providerId, scope) {
|
|
650
805
|
const hint = scope === "local" ? " Remove --local to inspect global configuration." : " Add --local to inspect project-local configuration.";
|
|
@@ -820,19 +975,21 @@ async function runListModelsCommand(context, options) {
|
|
|
820
975
|
context.io.stderr.write(`${errorMessageFrom(error) ?? "Invalid benchmark concurrency override."}\n`);
|
|
821
976
|
return 2;
|
|
822
977
|
}
|
|
978
|
+
const runtime = await startModelAdapters(config, context.io);
|
|
823
979
|
const progress = context.io.stderr.isTTY === true ? createModelBenchmarkProgressDisplay({
|
|
824
980
|
color: true,
|
|
825
|
-
config,
|
|
981
|
+
config: runtime.setupConfig,
|
|
826
982
|
output: context.io.stderr
|
|
827
983
|
}) : void 0;
|
|
828
984
|
let benchmarks;
|
|
829
985
|
try {
|
|
830
|
-
benchmarks = await benchmarkModels(
|
|
986
|
+
benchmarks = await benchmarkModels(runtime.setupConfig, {
|
|
831
987
|
onProgress: (snapshot) => progress?.update(snapshot),
|
|
832
988
|
providerConcurrency
|
|
833
989
|
});
|
|
834
990
|
} finally {
|
|
835
991
|
progress?.finish();
|
|
992
|
+
await runtime.shutdown();
|
|
836
993
|
}
|
|
837
994
|
context.io.stdout.write(formatModelBenchmarkList(config, benchmarks, { color: context.io.stdout.isTTY === true }));
|
|
838
995
|
return 0;
|
|
@@ -941,15 +1098,21 @@ async function runPruneModelsCommand(context, options, confirm = confirmPruneMod
|
|
|
941
1098
|
const plans = [];
|
|
942
1099
|
let hadFailure = false;
|
|
943
1100
|
for (const provider of providers) {
|
|
1101
|
+
const endpoint = provider.endpoint;
|
|
1102
|
+
if (endpoint === void 0) {
|
|
1103
|
+
hadFailure = true;
|
|
1104
|
+
context.io.stderr.write(`cannot prune provider "${escapeLineValue(provider.id)}" because it has no remote endpoint.\n`);
|
|
1105
|
+
continue;
|
|
1106
|
+
}
|
|
944
1107
|
let remoteModelIds;
|
|
945
1108
|
try {
|
|
946
|
-
remoteModelIds = new Set(await probeModels(
|
|
1109
|
+
remoteModelIds = new Set(await probeModels(endpoint, provider.headers));
|
|
947
1110
|
} catch (error) {
|
|
948
1111
|
hadFailure = true;
|
|
949
|
-
context.io.stderr.write(`failed to probe provider "${escapeLineValue(provider.id)}" at "${escapeLineValue(
|
|
1112
|
+
context.io.stderr.write(`failed to probe provider "${escapeLineValue(provider.id)}" at "${escapeLineValue(endpoint)}": ${classifyProbeFailure(error)}.\n`);
|
|
950
1113
|
continue;
|
|
951
1114
|
}
|
|
952
|
-
const removedModels = provider.models.filter((model) => !remoteModelIds.has(model.id));
|
|
1115
|
+
const removedModels = provider.models.filter((model) => !isAcpModel(model) && !remoteModelIds.has(model.id));
|
|
953
1116
|
const defaultModel = removedModels.find((model) => (model.aliases ?? []).includes("default"));
|
|
954
1117
|
if (defaultModel !== void 0) {
|
|
955
1118
|
hadFailure = true;
|
|
@@ -1669,6 +1832,7 @@ function textResult(prompts, result) {
|
|
|
1669
1832
|
*/
|
|
1670
1833
|
async function runProviderEditor(input, promptPort) {
|
|
1671
1834
|
const prompts = promptPort ?? createProviderEditorPrompts(await import("@clack/prompts"));
|
|
1835
|
+
if (input.source.kind === "acp") return runAcpProviderEditor(input, input.source, prompts);
|
|
1672
1836
|
const existingProvider = input.mode === "update" ? input.existingProvider : void 0;
|
|
1673
1837
|
const configuredHeaders = { ...existingProvider?.headers };
|
|
1674
1838
|
const initialHeaders = applyHeaderSelection(configuredHeaders, Object.keys(configuredHeaders), {}) ?? {};
|
|
@@ -1800,6 +1964,30 @@ async function runProviderEditor(input, promptPort) {
|
|
|
1800
1964
|
};
|
|
1801
1965
|
}
|
|
1802
1966
|
}
|
|
1967
|
+
function createAcpProvider(source, providerId) {
|
|
1968
|
+
return {
|
|
1969
|
+
id: providerId,
|
|
1970
|
+
models: [{
|
|
1971
|
+
...source.args === void 0 ? {} : { args: [...source.args] },
|
|
1972
|
+
capabilities: ["tool-call"],
|
|
1973
|
+
command: source.command,
|
|
1974
|
+
driver: "acp",
|
|
1975
|
+
...source.model
|
|
1976
|
+
}]
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
function createAcpSummary(input, provider, defaultAliasTarget) {
|
|
1980
|
+
const model = provider.models[0];
|
|
1981
|
+
return [
|
|
1982
|
+
`${input.mode === "create" ? "Create" : "Update"} provider?`,
|
|
1983
|
+
`Provider: ${escapeLineValue(provider.id)}`,
|
|
1984
|
+
"Driver: ACP",
|
|
1985
|
+
`Command: ${escapeLineValue("command" in model ? model.command : "-")}`,
|
|
1986
|
+
`Arguments: ${"args" in model ? formatSummaryValues(model.args ?? []) : "(none)"}`,
|
|
1987
|
+
`Model: ${escapeLineValue(model.id)}`,
|
|
1988
|
+
`Default: ${defaultAliasTarget === void 0 ? "unchanged" : escapeLineValue(`${defaultAliasTarget.providerId}/${defaultAliasTarget.modelId}`)}`
|
|
1989
|
+
].join("\n");
|
|
1990
|
+
}
|
|
1803
1991
|
function createProvider(input, draft) {
|
|
1804
1992
|
return {
|
|
1805
1993
|
...input.existingProvider,
|
|
@@ -1819,7 +2007,7 @@ function createSummary(input, provider, defaultAliasTarget) {
|
|
|
1819
2007
|
return [
|
|
1820
2008
|
`${input.mode === "create" ? "Create" : "Update"} provider?`,
|
|
1821
2009
|
`Provider: ${escapeLineValue(provider.id)}`,
|
|
1822
|
-
`Endpoint: ${escapeLineValue(provider.endpoint)}`,
|
|
2010
|
+
`Endpoint: ${escapeLineValue(provider.endpoint ?? "-")}`,
|
|
1823
2011
|
`Headers: ${formatSummaryValues(Object.keys(provider.headers ?? {}))}`,
|
|
1824
2012
|
`Added models: ${formatSummaryValues(additions)}`,
|
|
1825
2013
|
`Removed models: ${formatSummaryValues(removals)}`,
|
|
@@ -1832,6 +2020,87 @@ function effectiveHeaders(draft) {
|
|
|
1832
2020
|
function formatSummaryValues(values) {
|
|
1833
2021
|
return values.length > 0 ? values.map(escapeLineValue).join(", ") : "(none)";
|
|
1834
2022
|
}
|
|
2023
|
+
/**
|
|
2024
|
+
* Runs the preset-backed ACP branch of the provider editor.
|
|
2025
|
+
*
|
|
2026
|
+
* Triggering workflow:
|
|
2027
|
+
*
|
|
2028
|
+
* {@link runInteractiveSetup}
|
|
2029
|
+
* -> {@link runProviderEditor}
|
|
2030
|
+
* -> `provider-editor.acp-preset`
|
|
2031
|
+
* -> {@link runAcpProviderEditor}
|
|
2032
|
+
*
|
|
2033
|
+
* Upstream:
|
|
2034
|
+
* - {@link runProviderEditor}
|
|
2035
|
+
*
|
|
2036
|
+
* Downstream:
|
|
2037
|
+
* - {@link ProviderEditorResult} and the caller-owned setup-config write
|
|
2038
|
+
*/
|
|
2039
|
+
async function runAcpProviderEditor(input, source, prompts) {
|
|
2040
|
+
let defaultAliasTarget;
|
|
2041
|
+
let providerId = input.existingProvider?.id ?? source.defaultProviderId;
|
|
2042
|
+
let step = "providerId";
|
|
2043
|
+
while (true) {
|
|
2044
|
+
if (step === "providerId") {
|
|
2045
|
+
const result = await prompts.providerId(providerId, input.mode === "create");
|
|
2046
|
+
if (result.status === "cancelled") return { status: "cancelled" };
|
|
2047
|
+
if (result.status === "back") return { status: "back" };
|
|
2048
|
+
providerId = result.value.trim();
|
|
2049
|
+
step = "defaultAction";
|
|
2050
|
+
continue;
|
|
2051
|
+
}
|
|
2052
|
+
const provider = createAcpProvider(source, providerId);
|
|
2053
|
+
if (step === "defaultAction") {
|
|
2054
|
+
const result = await prompts.defaultAction(source.model.id);
|
|
2055
|
+
if (result.status === "cancelled") return { status: "cancelled" };
|
|
2056
|
+
if (result.status === "back") {
|
|
2057
|
+
step = "providerId";
|
|
2058
|
+
continue;
|
|
2059
|
+
}
|
|
2060
|
+
defaultAliasTarget = result.value === "yes" ? {
|
|
2061
|
+
modelId: source.model.id,
|
|
2062
|
+
providerId
|
|
2063
|
+
} : void 0;
|
|
2064
|
+
step = result.value === "selectAnother" ? "defaultModel" : "confirm";
|
|
2065
|
+
continue;
|
|
2066
|
+
}
|
|
2067
|
+
if (step === "defaultModel") {
|
|
2068
|
+
const candidates = createDefaultModelCandidates(mergeSetupConfigs(input.config, {
|
|
2069
|
+
providers: [provider],
|
|
2070
|
+
version: 1
|
|
2071
|
+
}), provider.id, [source.model.id]);
|
|
2072
|
+
const selectedDefault = await prompts.defaultModel(candidates.map((candidate) => ({
|
|
2073
|
+
hint: candidate.isCurrentDefault ? "current default" : candidate.isNew ? "new" : void 0,
|
|
2074
|
+
label: candidate.label,
|
|
2075
|
+
value: candidate.value
|
|
2076
|
+
})));
|
|
2077
|
+
if (selectedDefault.status === "cancelled") return { status: "cancelled" };
|
|
2078
|
+
if (selectedDefault.status === "back") {
|
|
2079
|
+
step = "defaultAction";
|
|
2080
|
+
continue;
|
|
2081
|
+
}
|
|
2082
|
+
const candidate = candidates.find((item) => item.value === selectedDefault.value);
|
|
2083
|
+
if (candidate === void 0) return { status: "cancelled" };
|
|
2084
|
+
defaultAliasTarget = {
|
|
2085
|
+
modelId: candidate.modelId,
|
|
2086
|
+
providerId: candidate.providerId
|
|
2087
|
+
};
|
|
2088
|
+
step = "confirm";
|
|
2089
|
+
continue;
|
|
2090
|
+
}
|
|
2091
|
+
const confirmed = await prompts.confirm(createAcpSummary(input, provider, defaultAliasTarget));
|
|
2092
|
+
if (confirmed.status === "cancelled" || confirmed.status === "submitted" && confirmed.value === "no") return { status: "cancelled" };
|
|
2093
|
+
if (confirmed.status === "back") {
|
|
2094
|
+
step = "defaultAction";
|
|
2095
|
+
continue;
|
|
2096
|
+
}
|
|
2097
|
+
return {
|
|
2098
|
+
...defaultAliasTarget === void 0 ? {} : { defaultAliasTarget },
|
|
2099
|
+
provider,
|
|
2100
|
+
status: "confirmed"
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
1835
2104
|
function splitInput(value) {
|
|
1836
2105
|
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
1837
2106
|
}
|
|
@@ -1885,6 +2154,11 @@ function mergeProviderHeaders(existing, replacements) {
|
|
|
1885
2154
|
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
1886
2155
|
}
|
|
1887
2156
|
async function runNonInteractiveUpdate(context, options, providerEndpoint, existingProvider, config, path) {
|
|
2157
|
+
const endpoint = providerEndpoint ?? existingProvider.endpoint;
|
|
2158
|
+
if (endpoint === void 0) {
|
|
2159
|
+
context.io.stderr.write(`provider "${existingProvider.id}" has no remote endpoint to update.\n`);
|
|
2160
|
+
return 2;
|
|
2161
|
+
}
|
|
1888
2162
|
let parsedHeaders;
|
|
1889
2163
|
try {
|
|
1890
2164
|
parsedHeaders = parseHeaderList(toArray$1(options.providerHeader));
|
|
@@ -1894,12 +2168,12 @@ async function runNonInteractiveUpdate(context, options, providerEndpoint, exist
|
|
|
1894
2168
|
}
|
|
1895
2169
|
const draft = {
|
|
1896
2170
|
...existingProvider,
|
|
1897
|
-
endpoint
|
|
2171
|
+
endpoint,
|
|
1898
2172
|
headers: mergeProviderHeaders(existingProvider.headers, parsedHeaders)
|
|
1899
2173
|
};
|
|
1900
2174
|
let remoteModelIds;
|
|
1901
2175
|
try {
|
|
1902
|
-
remoteModelIds = await probeModels(
|
|
2176
|
+
remoteModelIds = await probeModels(endpoint, draft.headers);
|
|
1903
2177
|
} catch (error) {
|
|
1904
2178
|
context.io.stderr.write(`failed to probe provider: ${errorMessageFrom(error) ?? "Unknown error."}\n`);
|
|
1905
2179
|
return 2;
|
|
@@ -1947,13 +2221,21 @@ async function runUpdateProviderCommand(context, options) {
|
|
|
1947
2221
|
context.io.stderr.write(formatUnknownProvider(providerId, scope));
|
|
1948
2222
|
return 2;
|
|
1949
2223
|
}
|
|
2224
|
+
const endpoint = providerEndpoint ?? existingProvider.endpoint;
|
|
2225
|
+
if (endpoint === void 0) {
|
|
2226
|
+
context.io.stderr.write(`provider "${existingProvider.id}" has no remote endpoint to update.\n`);
|
|
2227
|
+
return 2;
|
|
2228
|
+
}
|
|
1950
2229
|
if (context.setupNoInteractive) return runNonInteractiveUpdate(context, options, providerEndpoint, existingProvider, config, path);
|
|
1951
2230
|
const result = await runProviderEditor({
|
|
1952
2231
|
config,
|
|
1953
|
-
existingProvider
|
|
2232
|
+
existingProvider: {
|
|
2233
|
+
...existingProvider,
|
|
2234
|
+
endpoint
|
|
2235
|
+
},
|
|
1954
2236
|
io: context.io,
|
|
1955
2237
|
mode: "update",
|
|
1956
|
-
source: providerUpdateSource(
|
|
2238
|
+
source: providerUpdateSource(endpoint)
|
|
1957
2239
|
});
|
|
1958
2240
|
if (result.status !== "confirmed") return 0;
|
|
1959
2241
|
const replaced = replaceSetupProvider(config, result.provider);
|
|
@@ -3470,43 +3752,48 @@ async function runLintCommand(files, options, io, interceptConsoleOutput) {
|
|
|
3470
3752
|
const writeResult = (runResult) => {
|
|
3471
3753
|
io.stdout.write(formatDiagnostics(options.format, runResult, { color: io.stdout.isTTY === true }));
|
|
3472
3754
|
};
|
|
3755
|
+
const runtime = await startModelAdapters(setupConfig, io);
|
|
3473
3756
|
let result;
|
|
3474
3757
|
try {
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3758
|
+
try {
|
|
3759
|
+
result = await runAlint({
|
|
3760
|
+
cacheOnly: options.cacheOnly,
|
|
3761
|
+
config,
|
|
3762
|
+
cwd: io.cwd,
|
|
3763
|
+
defaultModel,
|
|
3764
|
+
directories: lintTargets.directories,
|
|
3765
|
+
files: lintTargets.files,
|
|
3766
|
+
modelOverride: options.model,
|
|
3767
|
+
outputLanguage: options.outputLanguage,
|
|
3768
|
+
progress: mergeProgressReporters(progress?.reporter, statsCollector?.reporter),
|
|
3769
|
+
runner,
|
|
3770
|
+
setupConfig: runtime.setupConfig
|
|
3771
|
+
});
|
|
3772
|
+
} catch (error) {
|
|
3773
|
+
restoreProgressConsole?.();
|
|
3774
|
+
progress?.dispose();
|
|
3775
|
+
if (error instanceof AlintRunError) {
|
|
3776
|
+
await persistStats(error.result);
|
|
3777
|
+
writeResult(error.result);
|
|
3778
|
+
io.stderr.write(formatRunError(error, io.stderr.isTTY === true));
|
|
3779
|
+
return 2;
|
|
3780
|
+
}
|
|
3781
|
+
if (error instanceof AlintRunCancelledError) {
|
|
3782
|
+
await persistStats(error.result);
|
|
3783
|
+
writeResult(error.result);
|
|
3784
|
+
io.stderr.write(formatCancelledError(error, io.stderr.isTTY === true));
|
|
3785
|
+
return 2;
|
|
3786
|
+
}
|
|
3787
|
+
throw error;
|
|
3788
|
+
}
|
|
3489
3789
|
restoreProgressConsole?.();
|
|
3490
3790
|
progress?.dispose();
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
}
|
|
3497
|
-
if (error instanceof AlintRunCancelledError) {
|
|
3498
|
-
await persistStats(error.result);
|
|
3499
|
-
writeResult(error.result);
|
|
3500
|
-
io.stderr.write(formatCancelledError(error, io.stderr.isTTY === true));
|
|
3501
|
-
return 2;
|
|
3502
|
-
}
|
|
3503
|
-
throw error;
|
|
3791
|
+
await persistStats(result);
|
|
3792
|
+
writeResult(result);
|
|
3793
|
+
return result.diagnostics.some((diagnostic) => diagnostic.severity === "error") ? 1 : 0;
|
|
3794
|
+
} finally {
|
|
3795
|
+
await runtime.shutdown();
|
|
3504
3796
|
}
|
|
3505
|
-
restoreProgressConsole?.();
|
|
3506
|
-
progress?.dispose();
|
|
3507
|
-
await persistStats(result);
|
|
3508
|
-
writeResult(result);
|
|
3509
|
-
return result.diagnostics.some((diagnostic) => diagnostic.severity === "error") ? 1 : 0;
|
|
3510
3797
|
}
|
|
3511
3798
|
function shouldEnableProgress(options, io) {
|
|
3512
3799
|
if (options.progress !== void 0) return options.progress;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-
|
|
1
|
+
import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-DFWnPI6m.mjs";
|
|
2
2
|
import { ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated } from "@alint-js/config";
|
|
3
3
|
import { defineConfig } from "@alint-js/core";
|
|
4
4
|
export { defineConfig, executeCli, formatDiagnostics, formatJson, formatStylish, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alint-js/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.mts",
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
"pathe": "^2.0.3",
|
|
29
29
|
"table": "^6.9.0",
|
|
30
30
|
"tinyrainbow": "^3.1.1",
|
|
31
|
-
"@alint-js/
|
|
32
|
-
"@alint-js/
|
|
31
|
+
"@alint-js/model-adapter-acp": "0.4.0",
|
|
32
|
+
"@alint-js/core": "0.4.0",
|
|
33
|
+
"@alint-js/config": "0.4.0"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@pnpm/find-workspace-dir": "^1000.1.5",
|