@lazyingart/agintiflow 0.20.187 → 0.20.188
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 +8 -1
- package/docs/model-selection.md +21 -0
- package/package.json +1 -1
- package/public/app.js +14 -4
- package/public/index.html +9 -2
- package/references/agintiflow-npm-publication-memory.md +11 -0
- package/scripts/smoke-auth.js +44 -5
- package/scripts/smoke-model-roles.js +14 -0
- package/scripts/smoke-web-api.js +12 -0
- package/scripts/smoke-web-ui.js +1 -1
- package/src/auth-onboarding.js +15 -4
- package/src/capabilities.js +3 -1
- package/src/cli.js +6 -5
- package/src/config.js +7 -5
- package/src/guardrails.js +3 -3
- package/src/i18n.js +11 -11
- package/src/interactive-cli.js +86 -8
- package/src/json-specialist.js +2 -2
- package/src/model-client.js +14 -3
- package/src/model-routing.js +316 -0
- package/src/project.js +31 -7
- package/src/redaction.js +2 -1
- package/web.js +3 -1
package/README.md
CHANGED
|
@@ -85,11 +85,12 @@ aginti init --template aaps
|
|
|
85
85
|
aginti init --template supervision
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
On first interactive use, AgInTiFlow opens an auth wizard if no main model key is found. Pick DeepSeek, OpenAI, Qwen, or Venice, paste the key, and it saves to the ignored project-local `.aginti/.env` file with restricted permissions. You can rerun setup any time:
|
|
88
|
+
On first interactive use, AgInTiFlow opens an auth wizard if no main model key is found. Pick DeepSeek, OpenAI, OpenRouter, Qwen, or Venice, paste the key, and it saves to the ignored project-local `.aginti/.env` file with restricted permissions. You can rerun setup any time:
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
91
|
aginti auth
|
|
92
92
|
aginti auth deepseek
|
|
93
|
+
aginti auth openrouter
|
|
93
94
|
aginti auth venice
|
|
94
95
|
aginti login grsai
|
|
95
96
|
```
|
|
@@ -99,6 +100,7 @@ Provider signup and key pages:
|
|
|
99
100
|
| Provider | Register / key page | API base URL used by AgInTiFlow |
|
|
100
101
|
| --- | --- | --- |
|
|
101
102
|
| DeepSeek | [https://platform.deepseek.com/api_keys](https://platform.deepseek.com/api_keys) | `https://api.deepseek.com` |
|
|
103
|
+
| OpenRouter | [https://openrouter.ai/settings/keys](https://openrouter.ai/settings/keys) | `https://openrouter.ai/api/v1` |
|
|
102
104
|
| Venice | [https://venice.ai/settings/api](https://venice.ai/settings/api) | `https://api.venice.ai/api/v1` |
|
|
103
105
|
| OpenAI | [https://platform.openai.com/api-keys](https://platform.openai.com/api-keys) | `https://api.openai.com/v1` |
|
|
104
106
|
| Qwen / DashScope | [https://bailian.console.aliyun.com/](https://bailian.console.aliyun.com/) | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` |
|
|
@@ -273,6 +275,7 @@ Useful selectors:
|
|
|
273
275
|
```
|
|
274
276
|
|
|
275
277
|
Venice routes can be used for optional uncensored or less restricted creative work. DeepSeek remains the economic default for normal engineering workflows. See [docs/model-selection.md](docs/model-selection.md) and [references/venice-model-reference.md](references/venice-model-reference.md).
|
|
278
|
+
OpenRouter is available as a first-class OpenAI-compatible provider with one key and company-grouped model buckets such as OpenRouter OpenAI, Anthropic, Google, DeepSeek, Qwen, Meta, Mistral, Moonshot, and xAI.
|
|
276
279
|
|
|
277
280
|
## AAPS And Large Workflows
|
|
278
281
|
|
|
@@ -348,6 +351,9 @@ Common environment variables:
|
|
|
348
351
|
```bash
|
|
349
352
|
DEEPSEEK_API_KEY=...
|
|
350
353
|
OPENAI_API_KEY=...
|
|
354
|
+
OPENROUTER_API_KEY=...
|
|
355
|
+
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
|
|
356
|
+
OPENROUTER_MODEL=openrouter/auto
|
|
351
357
|
QWEN_API_KEY=...
|
|
352
358
|
VENICE_API_KEY=...
|
|
353
359
|
GRSAI_API_KEY=...
|
|
@@ -365,6 +371,7 @@ Project-local keys:
|
|
|
365
371
|
```bash
|
|
366
372
|
aginti init
|
|
367
373
|
printf '%s' "$DEEPSEEK_API_KEY" | aginti keys set deepseek --stdin
|
|
374
|
+
printf '%s' "$OPENROUTER_API_KEY" | aginti keys set openrouter --stdin
|
|
368
375
|
printf '%s' "$VENICE_API_KEY" | aginti keys set venice --stdin
|
|
369
376
|
```
|
|
370
377
|
|
package/docs/model-selection.md
CHANGED
|
@@ -25,6 +25,8 @@ aginti --list-routes
|
|
|
25
25
|
|
|
26
26
|
# one-shot overrides
|
|
27
27
|
aginti --route-model deepseek-v4-flash --main-model deepseek-v4-pro "fix this project"
|
|
28
|
+
aginti --provider openrouter --model openrouter/auto --routing manual "try a gateway route"
|
|
29
|
+
aginti --provider openrouter --model anthropic/claude-sonnet-4.6 --routing manual "draft a design review"
|
|
28
30
|
aginti --provider venice --model venice-uncensored-1-2 --routing manual "draft a note"
|
|
29
31
|
aginti --spare-provider openai --spare-model gpt-5.4 --spare-reasoning medium "review this design"
|
|
30
32
|
aginti --allow-wrappers --wrapper codex --wrapper-model gpt-5.5 "patch this bug"
|
|
@@ -63,6 +65,13 @@ Provider families:
|
|
|
63
65
|
| DeepSeek | `deepseek-v4-flash`, `deepseek-v4-pro` |
|
|
64
66
|
| Venice | `venice-uncensored-1-2`, `venice-uncensored`, `gemma-4-uncensored` |
|
|
65
67
|
| OpenAI | `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex`, `gpt-5.3-codex-spark`, `gpt-5.2`; each has low/medium/high/xhigh reasoning |
|
|
68
|
+
| OpenRouter | `openrouter/auto`, `openrouter/pareto-code`, `openrouter/free` |
|
|
69
|
+
| OpenRouter OpenAI | `openai/gpt-5.5`, `openai/gpt-5.4`, `openai/gpt-5.4-mini`, `openai/gpt-4o` |
|
|
70
|
+
| OpenRouter Anthropic | Claude routes such as `anthropic/claude-sonnet-4.6`, `anthropic/claude-opus-4.7`, `anthropic/claude-opus-4.8-fast` |
|
|
71
|
+
| OpenRouter Google | Gemini/Gemma routes such as `google/gemini-3.5-flash`, `google/gemini-3.1-flash-lite`, `google/gemma-4-31b-it` |
|
|
72
|
+
| OpenRouter DeepSeek | `deepseek/deepseek-v4-flash`, `deepseek/deepseek-v4-pro`, `deepseek/deepseek-r1-0528` |
|
|
73
|
+
| OpenRouter Qwen | Qwen routes such as `qwen/qwen3.7-max`, `qwen/qwen3.6-flash`, `qwen/qwen3.6-plus` |
|
|
74
|
+
| OpenRouter Meta/Mistral/Moonshot/xAI | `meta-llama/llama-4-maverick`, `mistralai/mistral-medium-3-5`, `moonshotai/kimi-k2.6`, `x-ai/grok-4.3` |
|
|
66
75
|
| Venice GPT | GPT-family Venice routes such as `openai-gpt-55`, `openai-gpt-54`, `openai-gpt-54-mini`, `openai-gpt-53-codex`, `openai-gpt-52` |
|
|
67
76
|
| Venice Gemma | Gemma instruct routes such as `google-gemma-4-31b-it`, `google-gemma-4-26b-a4b-it`, `google-gemma-3-27b-it` |
|
|
68
77
|
| Venice Claude | Claude Sonnet/Opus routes such as `claude-sonnet-4-6`, `claude-opus-4-7`, `claude-opus-4-6` |
|
|
@@ -96,6 +105,16 @@ It keeps smart routing enabled. If the Venice key is missing, run `/auth venice`
|
|
|
96
105
|
| --- | --- | --- |
|
|
97
106
|
| `deepseek` | DeepSeek | Default route/main because V4 Flash and V4 Pro are cheap and strong. |
|
|
98
107
|
| `openai` | OpenAI | Spare/frontier checks, Codex-family work, and explicit manual routes. |
|
|
108
|
+
| `openrouter` | OpenRouter | OpenAI-compatible gateway with auto/free/code routers. |
|
|
109
|
+
| `openrouter-openai` | OpenRouter | OpenAI-family models through OpenRouter. |
|
|
110
|
+
| `openrouter-anthropic` | OpenRouter | Claude-family models through OpenRouter. |
|
|
111
|
+
| `openrouter-google` | OpenRouter | Gemini/Gemma models through OpenRouter. |
|
|
112
|
+
| `openrouter-deepseek` | OpenRouter | DeepSeek models through OpenRouter. |
|
|
113
|
+
| `openrouter-qwen` | OpenRouter | Qwen models through OpenRouter. |
|
|
114
|
+
| `openrouter-meta` | OpenRouter | Meta Llama models through OpenRouter. |
|
|
115
|
+
| `openrouter-mistral` | OpenRouter | Mistral models through OpenRouter. |
|
|
116
|
+
| `openrouter-moonshot` | OpenRouter | Moonshot/Kimi models through OpenRouter. |
|
|
117
|
+
| `openrouter-xai` | OpenRouter | xAI Grok models through OpenRouter. |
|
|
99
118
|
| `qwen` | Qwen | Chinese and general-purpose OpenAI-compatible tasks. |
|
|
100
119
|
| `venice` | Venice | Venice-native text shortcuts: Venice 1.2, Venice 1.1, and Gemma 4 Uncensored. |
|
|
101
120
|
| `venice-gpt` | Venice | GPT-family models through Venice. |
|
|
@@ -118,8 +137,10 @@ Keys are set per project and stored only in ignored `.aginti/.env`:
|
|
|
118
137
|
|
|
119
138
|
```bash
|
|
120
139
|
aginti auth
|
|
140
|
+
aginti auth openrouter
|
|
121
141
|
aginti auth venice
|
|
122
142
|
aginti keys status
|
|
143
|
+
printf '%s' "$OPENROUTER_API_KEY" | aginti keys set openrouter --stdin
|
|
123
144
|
printf '%s' "$VENICE_API_KEY" | aginti keys set venice --stdin
|
|
124
145
|
```
|
|
125
146
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.188",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/public/app.js
CHANGED
|
@@ -820,6 +820,7 @@ const ariaLabelNodes = [...document.querySelectorAll("[data-i18n-aria-label]")];
|
|
|
820
820
|
|
|
821
821
|
const defaults = {
|
|
822
822
|
openai: "gpt-5.4-mini",
|
|
823
|
+
openrouter: "openrouter/auto",
|
|
823
824
|
deepseek: "deepseek-v4-flash",
|
|
824
825
|
qwen: "qwen-plus",
|
|
825
826
|
venice: "venice-uncensored-1-2",
|
|
@@ -983,6 +984,7 @@ function renderKeyStatus(status = lastKeyStatus) {
|
|
|
983
984
|
if (!status) return;
|
|
984
985
|
const providers = [
|
|
985
986
|
["OpenAI", status.openai],
|
|
987
|
+
["OpenRouter", status.openrouter],
|
|
986
988
|
["DeepSeek", status.deepseek],
|
|
987
989
|
["Qwen", status.qwen],
|
|
988
990
|
["Venice", status.venice],
|
|
@@ -1009,7 +1011,7 @@ function renderKeyStatus(status = lastKeyStatus) {
|
|
|
1009
1011
|
})
|
|
1010
1012
|
);
|
|
1011
1013
|
}
|
|
1012
|
-
if (setupCardEl) setupCardEl.hidden = Boolean(status.openai || status.deepseek || status.qwen || status.venice);
|
|
1014
|
+
if (setupCardEl) setupCardEl.hidden = Boolean(status.openai || status.openrouter || status.deepseek || status.qwen || status.venice);
|
|
1013
1015
|
}
|
|
1014
1016
|
|
|
1015
1017
|
function renderProjectStatus(info = projectInfo) {
|
|
@@ -1611,6 +1613,7 @@ function fieldValue(field) {
|
|
|
1611
1613
|
const providerLabels = {
|
|
1612
1614
|
deepseek: "DeepSeek",
|
|
1613
1615
|
openai: "OpenAI",
|
|
1616
|
+
openrouter: "OpenRouter",
|
|
1614
1617
|
qwen: "Qwen",
|
|
1615
1618
|
venice: "Venice",
|
|
1616
1619
|
mock: "Mock local",
|
|
@@ -1634,14 +1637,14 @@ function setChoiceOptions(select, options, selectedValue = "", fallbackValue = "
|
|
|
1634
1637
|
}
|
|
1635
1638
|
|
|
1636
1639
|
function configuredTextProviders({ includeMock = true } = {}) {
|
|
1637
|
-
const base = ["deepseek", "openai", "qwen", "venice"];
|
|
1640
|
+
const base = ["deepseek", "openai", "openrouter", "qwen", "venice"];
|
|
1638
1641
|
if (includeMock) base.push("mock");
|
|
1639
1642
|
if (Object.keys(modelCatalog || {}).length === 0) return base;
|
|
1640
1643
|
return base.filter((provider) => provider === "mock" || Array.isArray(modelCatalog[provider]));
|
|
1641
1644
|
}
|
|
1642
1645
|
|
|
1643
1646
|
function refreshProviderDropdowns() {
|
|
1644
|
-
setChoiceOptions(setupProviderField, ["deepseek", "openai", "qwen", "venice", "grsai"], setupProviderField?.value || "deepseek", "deepseek");
|
|
1647
|
+
setChoiceOptions(setupProviderField, ["deepseek", "openai", "openrouter", "qwen", "venice", "grsai"], setupProviderField?.value || "deepseek", "deepseek");
|
|
1645
1648
|
setChoiceOptions(providerField, configuredTextProviders({ includeMock: true }), providerField?.value || "deepseek", "deepseek");
|
|
1646
1649
|
setChoiceOptions(routeProviderField, configuredTextProviders({ includeMock: true }), routeProviderField?.value || "deepseek", "deepseek");
|
|
1647
1650
|
setChoiceOptions(mainProviderField, configuredTextProviders({ includeMock: false }), mainProviderField?.value || "deepseek", "deepseek");
|
|
@@ -3874,7 +3877,13 @@ deleteSessionButton.addEventListener("click", () => {
|
|
|
3874
3877
|
});
|
|
3875
3878
|
|
|
3876
3879
|
providerField.addEventListener("change", () => {
|
|
3877
|
-
if (
|
|
3880
|
+
if (
|
|
3881
|
+
providerField.value === "mock" ||
|
|
3882
|
+
providerField.value === "venice" ||
|
|
3883
|
+
providerField.value === "openai" ||
|
|
3884
|
+
providerField.value === "openrouter" ||
|
|
3885
|
+
providerField.value === "qwen"
|
|
3886
|
+
) {
|
|
3878
3887
|
routingModeField.value = "manual";
|
|
3879
3888
|
}
|
|
3880
3889
|
|
|
@@ -4208,6 +4217,7 @@ async function loadConfig() {
|
|
|
4208
4217
|
taskProfiles = data.taskProfiles || [];
|
|
4209
4218
|
projectInfo = data.project || null;
|
|
4210
4219
|
defaults.openai = data.defaults?.openai?.model || defaults.openai;
|
|
4220
|
+
defaults.openrouter = data.defaults?.openrouter?.model || defaults.openrouter;
|
|
4211
4221
|
defaults.qwen = data.defaults?.qwen?.model || defaults.qwen;
|
|
4212
4222
|
defaults.venice = data.defaults?.venice?.model || defaults.venice;
|
|
4213
4223
|
defaults.deepseek = routingPresets.fast?.model || data.defaults?.deepseek?.model || defaults.deepseek;
|
package/public/index.html
CHANGED
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
<div>
|
|
66
66
|
<strong data-i18n="setupTitle">Provider setup</strong>
|
|
67
67
|
<p class="subtle" data-i18n="setupHelp">
|
|
68
|
-
DeepSeek/OpenAI/Qwen/Venice keys are missing. Use mock mode, export an env var, or save a project-local key.
|
|
68
|
+
DeepSeek/OpenAI/OpenRouter/Qwen/Venice keys are missing. Use mock mode, export an env var, or save a project-local key.
|
|
69
69
|
</p>
|
|
70
70
|
<p class="subtle" data-i18n="setupEnvHelp">
|
|
71
|
-
Env vars: DEEPSEEK_API_KEY, OPENAI_API_KEY, QWEN_API_KEY, VENICE_API_KEY, or LLM_API_KEY. Mock mode remains available for local tests.
|
|
71
|
+
Env vars: DEEPSEEK_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY, QWEN_API_KEY, VENICE_API_KEY, or LLM_API_KEY. Mock mode remains available for local tests.
|
|
72
72
|
</p>
|
|
73
73
|
<p class="subtle key-links">
|
|
74
74
|
<span data-i18n="setupKeyLinksLabel">Get keys:</span>
|
|
@@ -76,6 +76,8 @@
|
|
|
76
76
|
<span aria-hidden="true">·</span>
|
|
77
77
|
<a href="https://platform.openai.com/api-keys" target="_blank" rel="noreferrer" data-i18n="openaiKeyLink">OpenAI API keys</a>
|
|
78
78
|
<span aria-hidden="true">·</span>
|
|
79
|
+
<a href="https://openrouter.ai/settings/keys" target="_blank" rel="noreferrer">OpenRouter API keys</a>
|
|
80
|
+
<span aria-hidden="true">·</span>
|
|
79
81
|
<a href="https://venice.ai" target="_blank" rel="noreferrer">Venice API</a>
|
|
80
82
|
</p>
|
|
81
83
|
</div>
|
|
@@ -85,6 +87,7 @@
|
|
|
85
87
|
<select id="setup-provider">
|
|
86
88
|
<option value="deepseek">DeepSeek</option>
|
|
87
89
|
<option value="openai">OpenAI</option>
|
|
90
|
+
<option value="openrouter">OpenRouter</option>
|
|
88
91
|
<option value="qwen">Qwen</option>
|
|
89
92
|
<option value="venice">Venice</option>
|
|
90
93
|
<option value="grsai">GRS AI image</option>
|
|
@@ -132,6 +135,7 @@
|
|
|
132
135
|
<select id="provider" name="provider">
|
|
133
136
|
<option value="deepseek">DeepSeek</option>
|
|
134
137
|
<option value="openai">OpenAI</option>
|
|
138
|
+
<option value="openrouter">OpenRouter</option>
|
|
135
139
|
<option value="qwen">Qwen</option>
|
|
136
140
|
<option value="venice">Venice</option>
|
|
137
141
|
<option value="mock" data-i18n="mockProviderOption">Mock local</option>
|
|
@@ -414,6 +418,7 @@
|
|
|
414
418
|
<select id="routeProvider" name="routeProvider">
|
|
415
419
|
<option value="deepseek">DeepSeek</option>
|
|
416
420
|
<option value="openai">OpenAI</option>
|
|
421
|
+
<option value="openrouter">OpenRouter</option>
|
|
417
422
|
<option value="qwen">Qwen</option>
|
|
418
423
|
<option value="venice">Venice</option>
|
|
419
424
|
<option value="mock">Mock</option>
|
|
@@ -427,6 +432,7 @@
|
|
|
427
432
|
<select id="mainProvider" name="mainProvider">
|
|
428
433
|
<option value="deepseek">DeepSeek</option>
|
|
429
434
|
<option value="openai">OpenAI</option>
|
|
435
|
+
<option value="openrouter">OpenRouter</option>
|
|
430
436
|
<option value="qwen">Qwen</option>
|
|
431
437
|
<option value="venice">Venice</option>
|
|
432
438
|
</select>
|
|
@@ -438,6 +444,7 @@
|
|
|
438
444
|
<span>Spare</span>
|
|
439
445
|
<select id="spareProvider" name="spareProvider">
|
|
440
446
|
<option value="openai">OpenAI</option>
|
|
447
|
+
<option value="openrouter">OpenRouter</option>
|
|
441
448
|
<option value="deepseek">DeepSeek</option>
|
|
442
449
|
<option value="qwen">Qwen</option>
|
|
443
450
|
<option value="venice">Venice</option>
|
|
@@ -10,6 +10,17 @@ This note records the working publication route for AgInTiFlow so future release
|
|
|
10
10
|
|
|
11
11
|
Latest verified release:
|
|
12
12
|
|
|
13
|
+
- Version: `0.20.187`
|
|
14
|
+
- Commit: `9339b2e`
|
|
15
|
+
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26701125864`
|
|
16
|
+
- Workflow result: success
|
|
17
|
+
- npm registry check: `npm view @lazyingart/agintiflow version dist-tags.latest --registry=https://registry.npmjs.org` returned `0.20.187`.
|
|
18
|
+
- Installed verification: `npm install -g @lazyingart/agintiflow@0.20.187` then `aginti --version` returned `0.20.187`.
|
|
19
|
+
- Webapp verification: `aginti webapp restart --port 3210` then `curl -fsS http://127.0.0.1:3210/health` returned `version":"0.20.187"` from the global npm package path.
|
|
20
|
+
- Workspace explorer verification: `POST /api/workspace/snapshot` returned no `.aginti/`, `.aginti-work/`, `.sessions/`, or `.env` entries for the project root.
|
|
21
|
+
|
|
22
|
+
Previous verified release:
|
|
23
|
+
|
|
13
24
|
- Version: `0.20.186`
|
|
14
25
|
- Commit: `f32a748`
|
|
15
26
|
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26700782132`
|
package/scripts/smoke-auth.js
CHANGED
|
@@ -10,7 +10,16 @@ import { maskProviderKey, providerKeyPreview, providerKeyStatus, setProviderKey
|
|
|
10
10
|
|
|
11
11
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
12
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-auth-"));
|
|
13
|
-
const envKeys = [
|
|
13
|
+
const envKeys = [
|
|
14
|
+
"DEEPSEEK_API_KEY",
|
|
15
|
+
"OPENAI_API_KEY",
|
|
16
|
+
"OPENROUTER_API_KEY",
|
|
17
|
+
"LLM_API_KEY",
|
|
18
|
+
"QWEN_API_KEY",
|
|
19
|
+
"VENICE_API_KEY",
|
|
20
|
+
"GRSAI",
|
|
21
|
+
"GRSAI_API_KEY",
|
|
22
|
+
];
|
|
14
23
|
const originalEnv = Object.fromEntries(envKeys.map((key) => [key, process.env[key]]));
|
|
15
24
|
for (const key of envKeys) delete process.env[key];
|
|
16
25
|
|
|
@@ -55,57 +64,85 @@ async function runCli(args, stdin = "") {
|
|
|
55
64
|
|
|
56
65
|
try {
|
|
57
66
|
assert(normalizeAuthProvider("auxiliary") === "grsai", "auxiliary alias did not normalize to grsai");
|
|
67
|
+
assert(normalizeAuthProvider("or") === "openrouter", "OpenRouter alias did not normalize");
|
|
68
|
+
assert(normalizeAuthProvider("open-router") === "openrouter", "open-router alias did not normalize");
|
|
58
69
|
assert(normalizeAuthProvider("qwen") === "qwen", "qwen provider did not normalize");
|
|
59
70
|
assert(normalizeAuthProvider("venice") === "venice", "venice provider did not normalize");
|
|
60
71
|
assert(authProviderKeyUrl("deepseek") === "https://platform.deepseek.com/api_keys", "DeepSeek key URL is missing");
|
|
72
|
+
assert(authProviderKeyUrl("openrouter") === "https://openrouter.ai/settings/keys", "OpenRouter key URL is missing");
|
|
61
73
|
assert(authProviderKeyHelp("openai").includes("https://platform.openai.com/api-keys"), "OpenAI key help is missing");
|
|
62
74
|
assert(maskProviderKey("short") === "s…t (5 chars)", "short key mask was not compact");
|
|
63
75
|
assert(maskProviderKey("test-openai-key") === "test…-key (15 chars)", "long key mask did not preserve prefix/suffix");
|
|
64
76
|
const qwenDefaults = getProviderDefaults("qwen");
|
|
65
77
|
assert(qwenDefaults.provider === "qwen" && qwenDefaults.model, "qwen provider defaults are not available");
|
|
78
|
+
const openrouterDefaults = getProviderDefaults("openrouter");
|
|
79
|
+
assert(
|
|
80
|
+
openrouterDefaults.provider === "openrouter" &&
|
|
81
|
+
openrouterDefaults.baseURL === "https://openrouter.ai/api/v1" &&
|
|
82
|
+
openrouterDefaults.model === "openrouter/auto",
|
|
83
|
+
"openrouter provider defaults are not available"
|
|
84
|
+
);
|
|
66
85
|
const veniceDefaults = getProviderDefaults("venice");
|
|
67
86
|
assert(veniceDefaults.provider === "venice" && veniceDefaults.model === "venice-uncensored-1-2", "venice provider defaults are not available");
|
|
68
87
|
|
|
69
88
|
process.env.DEEPSEEK_API_KEY = "ambient-deepseek-key";
|
|
89
|
+
process.env.OPENROUTER_API_KEY = "ambient-openrouter-key";
|
|
70
90
|
process.env.VENICE_API_KEY = "ambient-venice-key";
|
|
71
91
|
process.env.GRSAI_API_KEY = "ambient-grsai-key";
|
|
72
92
|
let status = providerKeyStatus(tempRoot);
|
|
73
|
-
assert(status.deepseek && status.venice && status.grsai, "ambient provider keys were not detected");
|
|
93
|
+
assert(status.deepseek && status.openrouter && status.venice && status.grsai, "ambient provider keys were not detected");
|
|
74
94
|
assert(status.localEnv, "ambient provider keys were not persisted into local .aginti/.env");
|
|
75
95
|
const localEnv = await fs.readFile(path.join(tempRoot, ".aginti", ".env"), "utf8");
|
|
76
96
|
assert(localEnv.includes("DEEPSEEK_API_KEY="), "ambient DeepSeek key was not saved locally");
|
|
97
|
+
assert(localEnv.includes("OPENROUTER_API_KEY="), "ambient OpenRouter key was not saved locally");
|
|
77
98
|
assert(localEnv.includes("VENICE_API_KEY="), "ambient Venice key was not saved locally");
|
|
78
99
|
assert(localEnv.includes("GRSAI_API_KEY="), "ambient GRS AI key was not saved locally");
|
|
79
100
|
delete process.env.DEEPSEEK_API_KEY;
|
|
101
|
+
delete process.env.OPENROUTER_API_KEY;
|
|
80
102
|
delete process.env.VENICE_API_KEY;
|
|
81
103
|
delete process.env.GRSAI_API_KEY;
|
|
82
104
|
status = providerKeyStatus(tempRoot);
|
|
83
|
-
assert(status.deepseek && status.venice && status.grsai, "persisted provider keys were not reloaded after ambient env was cleared");
|
|
105
|
+
assert(status.deepseek && status.openrouter && status.venice && status.grsai, "persisted provider keys were not reloaded after ambient env was cleared");
|
|
84
106
|
|
|
85
107
|
await setProviderKey(tempRoot, "qwen", "test-qwen-key");
|
|
108
|
+
await setProviderKey(tempRoot, "openrouter", "test-openrouter-key");
|
|
86
109
|
await setProviderKey(tempRoot, "venice", "test-venice-key");
|
|
87
110
|
status = providerKeyStatus(tempRoot);
|
|
88
111
|
assert(status.qwen, "qwen key status was not detected");
|
|
112
|
+
assert(status.openrouter, "openrouter key status was not detected");
|
|
89
113
|
assert(status.venice, "venice key status was not detected");
|
|
90
114
|
assert(status.envVars.qwen.includes("QWEN_API_KEY"), "qwen env var name was not reported");
|
|
115
|
+
assert(status.envVars.openrouter.includes("OPENROUTER_API_KEY"), "openrouter env var name was not reported");
|
|
91
116
|
assert(status.envVars.venice.includes("VENICE_API_KEY"), "venice env var name was not reported");
|
|
92
117
|
const qwenPreview = providerKeyPreview(tempRoot, "qwen");
|
|
93
118
|
assert(qwenPreview.available && qwenPreview.preview === "test…-key (13 chars)", "qwen key preview was not masked correctly");
|
|
119
|
+
const openrouterPreview = providerKeyPreview(tempRoot, "openrouter");
|
|
120
|
+
assert(
|
|
121
|
+
openrouterPreview.available && openrouterPreview.preview === "test…-key (19 chars)",
|
|
122
|
+
"openrouter key preview was not masked correctly"
|
|
123
|
+
);
|
|
94
124
|
const venicePreview = providerKeyPreview(tempRoot, "venice");
|
|
95
125
|
assert(venicePreview.available && venicePreview.preview === "test…-key (15 chars)", "venice key preview was not masked correctly");
|
|
96
126
|
|
|
97
127
|
await runCli(["keys", "set", "openai", "--stdin"], "test-openai-key");
|
|
98
128
|
await runCli(["keys", "set", "grsai", "--stdin"], "test-grsai-key");
|
|
99
129
|
status = providerKeyStatus(tempRoot);
|
|
100
|
-
assert(status.openai && status.grsai && status.qwen && status.venice, "stored auth keys were not detected");
|
|
130
|
+
assert(status.openai && status.openrouter && status.grsai && status.qwen && status.venice, "stored auth keys were not detected");
|
|
101
131
|
const openaiPreview = providerKeyPreview(tempRoot, "openai");
|
|
102
132
|
assert(openaiPreview.preview === "test…-key (15 chars)", "openai key preview was not masked correctly");
|
|
103
133
|
assert(openaiPreview.preview !== "test-openai-key", "openai key preview leaked raw key");
|
|
104
134
|
|
|
105
135
|
const keysOutput = await runCli(["keys", "status"]);
|
|
136
|
+
assert(keysOutput.includes("openrouter=available"), "keys status did not include openrouter");
|
|
106
137
|
assert(keysOutput.includes("qwen=available"), "keys status did not include qwen");
|
|
107
138
|
assert(keysOutput.includes("venice=available"), "keys status did not include venice");
|
|
108
|
-
assert(
|
|
139
|
+
assert(
|
|
140
|
+
!keysOutput.includes("test-openai-key") &&
|
|
141
|
+
!keysOutput.includes("test-openrouter-key") &&
|
|
142
|
+
!keysOutput.includes("test-qwen-key") &&
|
|
143
|
+
!keysOutput.includes("test-venice-key"),
|
|
144
|
+
"keys status leaked a raw key"
|
|
145
|
+
);
|
|
109
146
|
|
|
110
147
|
console.log(
|
|
111
148
|
JSON.stringify(
|
|
@@ -118,9 +155,11 @@ try {
|
|
|
118
155
|
"provider-key-mask",
|
|
119
156
|
"provider-key-preview",
|
|
120
157
|
"qwen-defaults",
|
|
158
|
+
"openrouter-defaults",
|
|
121
159
|
"venice-defaults",
|
|
122
160
|
"ambient-key-autopersist",
|
|
123
161
|
"qwen-key-status",
|
|
162
|
+
"openrouter-key-status",
|
|
124
163
|
"venice-key-status",
|
|
125
164
|
"cli-key-status-redacted",
|
|
126
165
|
],
|
|
@@ -207,6 +207,14 @@ assert(modelsForProviderGroup("venice-claude").some((item) => item.id === "claud
|
|
|
207
207
|
assert(modelsForProviderGroup("venice-qwen").some((item) => item.id === "qwen3-coder-480b-a35b-instruct-turbo"), "venice-qwen bucket missing Qwen Coder");
|
|
208
208
|
assert(AUXILIARY_MODEL_CATALOG["venice-image"].some((item) => item.id === "gpt-image-2"), "Venice image catalog missing GPT Image 2");
|
|
209
209
|
assert(AUXILIARY_MODEL_CATALOG["venice-image"].some((item) => item.id === "wan-2-7-pro-edit"), "Venice image catalog missing Wan edit");
|
|
210
|
+
assert(MODEL_PROVIDER_GROUPS["openrouter"].provider === "openrouter", "openrouter group missing");
|
|
211
|
+
assert(MODEL_PROVIDER_GROUPS["openrouter-openai"].provider === "openrouter", "openrouter-openai group missing");
|
|
212
|
+
assert(modelsForProviderGroup("openrouter").some((item) => item.id === "openrouter/auto"), "openrouter group missing auto router");
|
|
213
|
+
assert(modelsForProviderGroup("openrouter-openai").some((item) => item.id === "openai/gpt-5.4-mini"), "openrouter OpenAI bucket missing GPT-5.4 Mini");
|
|
214
|
+
assert(modelsForProviderGroup("openrouter-anthropic").some((item) => item.id === "anthropic/claude-sonnet-4.6"), "openrouter Anthropic bucket missing Claude Sonnet");
|
|
215
|
+
assert(modelsForProviderGroup("openrouter-google").some((item) => item.id === "google/gemini-3.5-flash"), "openrouter Google bucket missing Gemini");
|
|
216
|
+
assert(modelsForProviderGroup("openrouter-deepseek").some((item) => item.id === "deepseek/deepseek-v4-pro"), "openrouter DeepSeek bucket missing V4 Pro");
|
|
217
|
+
assert(modelsForProviderGroup("openrouter-qwen").some((item) => item.id === "qwen/qwen3.7-max"), "openrouter Qwen bucket missing Qwen Max");
|
|
210
218
|
const routeChoices = modelRoleChoices("route").map((item) => `${item.provider}/${item.model}`);
|
|
211
219
|
const mainChoices = modelRoleChoices("main").map((item) => `${item.provider}/${item.model}`);
|
|
212
220
|
const spareChoices = modelRoleChoices("spare").map((item) => `${item.provider}/${item.model}`);
|
|
@@ -231,6 +239,12 @@ for (const expected of [
|
|
|
231
239
|
"openai/gpt-5.4-mini",
|
|
232
240
|
"openai/gpt-5.3-codex",
|
|
233
241
|
"openai/gpt-5.3-codex-spark",
|
|
242
|
+
"openrouter/openrouter/auto",
|
|
243
|
+
"openrouter/openai/gpt-5.4-mini",
|
|
244
|
+
"openrouter/anthropic/claude-sonnet-4.6",
|
|
245
|
+
"openrouter/google/gemini-3.5-flash",
|
|
246
|
+
"openrouter/deepseek/deepseek-v4-pro",
|
|
247
|
+
"openrouter/qwen/qwen3.7-max",
|
|
234
248
|
"qwen/qwen-plus",
|
|
235
249
|
"mock/mock-agent",
|
|
236
250
|
]) {
|
package/scripts/smoke-web-api.js
CHANGED
|
@@ -123,6 +123,18 @@ try {
|
|
|
123
123
|
|
|
124
124
|
const config = await fetchJson("/api/config");
|
|
125
125
|
if (!config.keyStatus?.mock) throw new Error("mock provider is not advertised by /api/config");
|
|
126
|
+
if (!Object.prototype.hasOwnProperty.call(config.keyStatus || {}, "openrouter")) {
|
|
127
|
+
throw new Error("OpenRouter key status is not advertised by /api/config");
|
|
128
|
+
}
|
|
129
|
+
if (config.defaults?.openrouter?.baseURL !== "https://openrouter.ai/api/v1") {
|
|
130
|
+
throw new Error(`OpenRouter defaults are missing from /api/config: ${JSON.stringify(config.defaults?.openrouter)}`);
|
|
131
|
+
}
|
|
132
|
+
if (!config.modelCatalog?.openrouter?.some((model) => model.id === "openrouter/auto")) {
|
|
133
|
+
throw new Error("OpenRouter model catalog is missing from /api/config");
|
|
134
|
+
}
|
|
135
|
+
if (!config.modelGroups?.["openrouter-openai"]) {
|
|
136
|
+
throw new Error("OpenRouter company model groups are missing from /api/config");
|
|
137
|
+
}
|
|
126
138
|
if (!config.workspace?.enabled) throw new Error("workspace file tools are not advertised by /api/config");
|
|
127
139
|
if (config.preferences?.preferredWrapper !== "codex") throw new Error("Codex is not the default preferred wrapper");
|
|
128
140
|
if (config.project?.root !== runtimeDir) throw new Error("web project root did not default to launch directory");
|
package/scripts/smoke-web-ui.js
CHANGED
|
@@ -297,7 +297,7 @@ try {
|
|
|
297
297
|
await page.click("#open-settings");
|
|
298
298
|
await page.waitForSelector("#settings-modal[open]");
|
|
299
299
|
const routeProviders = await optionValues(page, "#routeProvider");
|
|
300
|
-
for (const provider of ["deepseek", "openai", "qwen", "venice", "mock"]) {
|
|
300
|
+
for (const provider of ["deepseek", "openai", "openrouter", "qwen", "venice", "mock"]) {
|
|
301
301
|
if (!routeProviders.includes(provider)) throw new Error(`settings route provider dropdown is missing ${provider}`);
|
|
302
302
|
}
|
|
303
303
|
const wrapperOptions = await optionValues(page, "#preferredWrapper");
|
package/src/auth-onboarding.js
CHANGED
|
@@ -36,6 +36,13 @@ export const MAIN_AUTH_PROVIDERS = [
|
|
|
36
36
|
description: "OpenAI-compatible fallback",
|
|
37
37
|
keyUrl: "https://platform.openai.com/api-keys",
|
|
38
38
|
},
|
|
39
|
+
{
|
|
40
|
+
id: "openrouter",
|
|
41
|
+
label: "OpenRouter",
|
|
42
|
+
keyName: "OPENROUTER_API_KEY",
|
|
43
|
+
description: "OpenAI-compatible multi-provider gateway",
|
|
44
|
+
keyUrl: "https://openrouter.ai/settings/keys",
|
|
45
|
+
},
|
|
39
46
|
{
|
|
40
47
|
id: "qwen",
|
|
41
48
|
label: "Qwen",
|
|
@@ -69,6 +76,10 @@ const AUTH_ALIASES = {
|
|
|
69
76
|
deepseek: "deepseek",
|
|
70
77
|
ds: "deepseek",
|
|
71
78
|
openai: "openai",
|
|
79
|
+
openrouter: "openrouter",
|
|
80
|
+
"open-router": "openrouter",
|
|
81
|
+
or: "openrouter",
|
|
82
|
+
router: "openrouter",
|
|
72
83
|
qwen: "qwen",
|
|
73
84
|
venice: "venice",
|
|
74
85
|
v: "venice",
|
|
@@ -76,7 +87,7 @@ const AUTH_ALIASES = {
|
|
|
76
87
|
|
|
77
88
|
export function normalizeAuthProvider(provider = "", fallback = "deepseek") {
|
|
78
89
|
const normalized = AUTH_ALIASES[String(provider || "").trim().toLowerCase()] || String(provider || "").trim().toLowerCase();
|
|
79
|
-
return ["deepseek", "openai", "qwen", "venice", "grsai"].includes(normalized) ? normalized : fallback;
|
|
90
|
+
return ["deepseek", "openai", "openrouter", "qwen", "venice", "grsai"].includes(normalized) ? normalized : fallback;
|
|
80
91
|
}
|
|
81
92
|
|
|
82
93
|
function providerLabel(provider = "") {
|
|
@@ -396,11 +407,11 @@ export async function chooseAuthProvider({
|
|
|
396
407
|
|
|
397
408
|
export function shouldPromptForDeepSeek(args = {}, projectRoot = process.cwd()) {
|
|
398
409
|
const provider = String(args.provider || "").toLowerCase();
|
|
399
|
-
if (provider === "mock" || provider === "openai" || provider === "qwen" || provider === "venice") return false;
|
|
410
|
+
if (provider === "mock" || provider === "openai" || provider === "openrouter" || provider === "qwen" || provider === "venice") return false;
|
|
400
411
|
if (process.env.AGINTIFLOW_NO_AUTH_PROMPT === "1") return false;
|
|
401
412
|
if (!input.isTTY || !output.isTTY) return false;
|
|
402
413
|
const status = providerKeyStatus(projectRoot);
|
|
403
|
-
return !status.deepseek && !status.openai && !status.qwen && !status.venice;
|
|
414
|
+
return !status.deepseek && !status.openai && !status.openrouter && !status.qwen && !status.venice;
|
|
404
415
|
}
|
|
405
416
|
|
|
406
417
|
export async function promptAndSaveDeepSeekKey(projectRoot = process.cwd(), options = {}) {
|
|
@@ -422,7 +433,7 @@ export async function runAuthWizard(projectRoot = process.cwd(), options = {}) {
|
|
|
422
433
|
const status = providerKeyStatus(projectRoot);
|
|
423
434
|
const initialProvider = normalizeAuthProvider(options.provider || options.initialProvider || "deepseek", "deepseek");
|
|
424
435
|
const directProvider =
|
|
425
|
-
options.provider && ["deepseek", "openai", "qwen", "venice", "grsai"].includes(normalizeAuthProvider(options.provider, ""))
|
|
436
|
+
options.provider && ["deepseek", "openai", "openrouter", "qwen", "venice", "grsai"].includes(normalizeAuthProvider(options.provider, ""))
|
|
426
437
|
? normalizeAuthProvider(options.provider)
|
|
427
438
|
: "";
|
|
428
439
|
const mainProvider =
|
package/src/capabilities.js
CHANGED
|
@@ -164,6 +164,7 @@ export async function buildCapabilityReport(projectRoot, packageVersion, config)
|
|
|
164
164
|
capability("docker", Boolean(dockerStatus?.dockerAvailable), dockerStatus || {}),
|
|
165
165
|
capability("deepseek-key", keyStatus.deepseek, { envVars: keyStatus.envVars.deepseek }),
|
|
166
166
|
capability("openai-key", keyStatus.openai, { envVars: keyStatus.envVars.openai }),
|
|
167
|
+
capability("openrouter-key", keyStatus.openrouter, { envVars: keyStatus.envVars.openrouter }),
|
|
167
168
|
capability("qwen-key", keyStatus.qwen, { envVars: keyStatus.envVars.qwen }),
|
|
168
169
|
capability("venice-key", keyStatus.venice, { envVars: keyStatus.envVars.venice }),
|
|
169
170
|
capability("grsai-key", keyStatus.grsai, {
|
|
@@ -238,6 +239,7 @@ export async function buildCapabilityReport(projectRoot, packageVersion, config)
|
|
|
238
239
|
keys: {
|
|
239
240
|
deepseek: keyStatus.deepseek,
|
|
240
241
|
openai: keyStatus.openai,
|
|
242
|
+
openrouter: keyStatus.openrouter,
|
|
241
243
|
qwen: keyStatus.qwen,
|
|
242
244
|
venice: keyStatus.venice,
|
|
243
245
|
grsai: keyStatus.grsai,
|
|
@@ -321,7 +323,7 @@ export function printCapabilityReport(report) {
|
|
|
321
323
|
console.log(
|
|
322
324
|
`keys: deepseek=${report.keys.deepseek ? "available" : "missing"} openai=${
|
|
323
325
|
report.keys.openai ? "available" : "missing"
|
|
324
|
-
} qwen=${report.keys.qwen ? "available" : "missing"} venice=${
|
|
326
|
+
} openrouter=${report.keys.openrouter ? "available" : "missing"} qwen=${report.keys.qwen ? "available" : "missing"} venice=${
|
|
325
327
|
report.keys.venice ? "available" : "missing"
|
|
326
328
|
} grsai=${
|
|
327
329
|
report.keys.grsai ? "available" : "missing"
|
package/src/cli.js
CHANGED
|
@@ -762,7 +762,7 @@ function exitOnUnknownOptions(parsed) {
|
|
|
762
762
|
|
|
763
763
|
function printUsage() {
|
|
764
764
|
console.log(
|
|
765
|
-
'Usage: aginti [chat] OR aginti init [--template minimal|disciplined|coding|research|writing|design|aaps|supervision] OR aginti web [--port 3210] OR aginti docker [status|setup|install-host] OR aginti update OR aginti image [--json] [--dry-run] [--format png|webp|svg] "prompt" OR aginti models OR aginti aaps [status|init|files|validate|compile|check|run] OR aginti mcp [status|config|inspect|tools|resources|read|prompts|prompt|call|restart] OR aginti skills [query] OR aginti skillmesh [status|off|record|share|sync|serve|service] OR aginti housekeeping [--json] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti resume [--all-sessions] [latest|<session-id>] ["prompt"] OR aginti --remove-empty-sessions OR aginti --remove-sessions OR aginti queue <session-id> "message" OR aginti [--no-auto-update] [-s safe|normal|danger] [--language en|ja|zh-Hans|zh-Hant|ko|fr|es|ar|vi|de|ru] [--image] [--latex] [--scs|--scs auto|--no-scs] [--dynamic-steps auto|on|off] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--mcp|--no-mcp] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
|
|
765
|
+
'Usage: aginti [chat] OR aginti init [--template minimal|disciplined|coding|research|writing|design|aaps|supervision] OR aginti web [--port 3210] OR aginti docker [status|setup|install-host] OR aginti update OR aginti image [--json] [--dry-run] [--format png|webp|svg] "prompt" OR aginti models OR aginti aaps [status|init|files|validate|compile|check|run] OR aginti mcp [status|config|inspect|tools|resources|read|prompts|prompt|call|restart] OR aginti skills [query] OR aginti skillmesh [status|off|record|share|sync|serve|service] OR aginti housekeeping [--json] OR aginti auth [deepseek|openai|openrouter|qwen|venice|grsai] OR aginti resume [--all-sessions] [latest|<session-id>] ["prompt"] OR aginti --remove-empty-sessions OR aginti --remove-sessions OR aginti queue <session-id> "message" OR aginti [--no-auto-update] [-s safe|normal|danger] [--language en|ja|zh-Hans|zh-Hant|ko|fr|es|ar|vi|de|ru] [--image] [--latex] [--scs|--scs auto|--no-scs] [--dynamic-steps auto|on|off] [--routing smart|fast|complex|manual] [--provider deepseek|openai|openrouter|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--mcp|--no-mcp] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
|
|
766
766
|
);
|
|
767
767
|
console.log("Permission shortcuts: -s safe asks before writes/setup; -s normal allows current-project writes and Docker setup; -s danger enables trusted host/full-access mode.");
|
|
768
768
|
console.log(`Languages: ${["en", "ja", "zh-Hans", "zh-Hant", "ko", "fr", "es", "ar", "vi", "de", "ru"].map((code) => `${code}=${languageLabel(code)}`).join(", ")}`);
|
|
@@ -1157,6 +1157,7 @@ async function restartWebAppAfterUpdate({ commandCwd = process.cwd(), language =
|
|
|
1157
1157
|
function providerLabel(provider) {
|
|
1158
1158
|
const normalized = String(provider || "").toLowerCase();
|
|
1159
1159
|
if (normalized === "openai") return "OpenAI";
|
|
1160
|
+
if (normalized === "openrouter" || normalized === "or" || normalized === "open-router") return "OpenRouter";
|
|
1160
1161
|
if (normalized === "qwen") return "Qwen";
|
|
1161
1162
|
if (normalized === "venice") return "Venice";
|
|
1162
1163
|
if (normalized === "grsai" || normalized === "auxiliary") return "GRSAI";
|
|
@@ -1394,7 +1395,7 @@ async function readStdin() {
|
|
|
1394
1395
|
async function ensureDeepSeekKeyForOneShot(args) {
|
|
1395
1396
|
if (!shouldPromptForDeepSeek(args, process.cwd())) return true;
|
|
1396
1397
|
console.log("No main model API key is configured for this project.");
|
|
1397
|
-
console.log("Choose DeepSeek, OpenAI, Qwen, or Venice, then paste a key to save in `.aginti/.env` with 0600 permissions.");
|
|
1398
|
+
console.log("Choose DeepSeek, OpenAI, OpenRouter, Qwen, or Venice, then paste a key to save in `.aginti/.env` with 0600 permissions.");
|
|
1398
1399
|
const result = await runAuthWizard(process.cwd(), { provider: args.provider || "" });
|
|
1399
1400
|
printAuthWizardResult(result);
|
|
1400
1401
|
if (result.saved.some((item) => item.provider !== "grsai")) {
|
|
@@ -1413,9 +1414,9 @@ async function handleKeyCommand(argv) {
|
|
|
1413
1414
|
status.openai ? "available" : "missing"
|
|
1414
1415
|
} qwen=${status.qwen ? "available" : "missing"} venice=${
|
|
1415
1416
|
status.venice ? "available" : "missing"
|
|
1416
|
-
} grsai=${status.grsai ? "available" : "missing"} mock=available localEnv=${status.localEnv}`
|
|
1417
|
+
} openrouter=${status.openrouter ? "available" : "missing"} grsai=${status.grsai ? "available" : "missing"} mock=available localEnv=${status.localEnv}`
|
|
1417
1418
|
);
|
|
1418
|
-
console.log("env vars: DeepSeek=DEEPSEEK_API_KEY or LLM_API_KEY; OpenAI=OPENAI_API_KEY or LLM_API_KEY; Qwen=QWEN_API_KEY; Venice=VENICE_API_KEY; image=GRSAI or GRSAI_API_KEY");
|
|
1419
|
+
console.log("env vars: DeepSeek=DEEPSEEK_API_KEY or LLM_API_KEY; OpenAI=OPENAI_API_KEY or LLM_API_KEY; OpenRouter=OPENROUTER_API_KEY; Qwen=QWEN_API_KEY; Venice=VENICE_API_KEY; image=GRSAI or GRSAI_API_KEY");
|
|
1419
1420
|
return;
|
|
1420
1421
|
}
|
|
1421
1422
|
|
|
@@ -1431,7 +1432,7 @@ async function handleKeyCommand(argv) {
|
|
|
1431
1432
|
return;
|
|
1432
1433
|
}
|
|
1433
1434
|
|
|
1434
|
-
console.error("Usage: aginti keys status OR aginti keys set deepseek|openai|qwen|venice|grsai [--stdin]");
|
|
1435
|
+
console.error("Usage: aginti keys status OR aginti keys set deepseek|openai|openrouter|qwen|venice|grsai [--stdin]");
|
|
1435
1436
|
process.exit(1);
|
|
1436
1437
|
}
|
|
1437
1438
|
|
package/src/config.js
CHANGED
|
@@ -45,11 +45,13 @@ export function resolveRuntimeConfig(args, overrides = {}) {
|
|
|
45
45
|
? "deepseek"
|
|
46
46
|
: process.env.OPENAI_API_KEY
|
|
47
47
|
? "openai"
|
|
48
|
-
: process.env.
|
|
49
|
-
? "
|
|
50
|
-
: process.env.
|
|
51
|
-
? "
|
|
52
|
-
:
|
|
48
|
+
: process.env.OPENROUTER_API_KEY
|
|
49
|
+
? "openrouter"
|
|
50
|
+
: process.env.QWEN_API_KEY
|
|
51
|
+
? "qwen"
|
|
52
|
+
: process.env.VENICE_API_KEY
|
|
53
|
+
? "venice"
|
|
54
|
+
: "deepseek");
|
|
53
55
|
const routingMode = normalizeRoutingMode(overrides.routingMode || args.routingMode || process.env.AGENT_ROUTING_MODE || "smart");
|
|
54
56
|
const taskProfile = normalizeTaskProfile(overrides.taskProfile || args.taskProfile || process.env.AGINTI_TASK_PROFILE || "auto");
|
|
55
57
|
const language = resolveLanguage(overrides.language || args.language || process.env.AGINTI_LANGUAGE || "");
|
package/src/guardrails.js
CHANGED
|
@@ -285,7 +285,7 @@ export function checkToolUse({ toolName, args, snapshot, config }) {
|
|
|
285
285
|
const brief = String(args.writingBrief || args.brief || args.prompt || "").trim();
|
|
286
286
|
if (!brief) return { allowed: false, reason: "Writing specialist requires writingBrief.", category: "writing-specialist" };
|
|
287
287
|
const provider = String(args.provider || process.env.AGINTI_WRITING_PROVIDER || "").trim();
|
|
288
|
-
if (provider && !["deepseek", "openai", "qwen", "venice", "mock"].includes(provider)) {
|
|
288
|
+
if (provider && !["deepseek", "openai", "openrouter", "qwen", "venice", "mock"].includes(provider)) {
|
|
289
289
|
return { allowed: false, reason: `Unknown writing specialist provider: ${provider}`, category: "writing-specialist" };
|
|
290
290
|
}
|
|
291
291
|
const payloadBytes = Buffer.byteLength(
|
|
@@ -321,7 +321,7 @@ export function checkToolUse({ toolName, args, snapshot, config }) {
|
|
|
321
321
|
return { allowed: false, reason: "JSON specialist requires schema or schemaJson.", category: "json-specialist" };
|
|
322
322
|
}
|
|
323
323
|
const provider = String(args.provider || process.env.AGINTI_JSON_PROVIDER || "").trim();
|
|
324
|
-
if (provider && !["deepseek", "openai", "qwen", "venice", "mock"].includes(provider)) {
|
|
324
|
+
if (provider && !["deepseek", "openai", "openrouter", "qwen", "venice", "mock"].includes(provider)) {
|
|
325
325
|
return { allowed: false, reason: `Unknown JSON specialist provider: ${provider}`, category: "json-specialist" };
|
|
326
326
|
}
|
|
327
327
|
const payloadBytes = Buffer.byteLength(
|
|
@@ -360,7 +360,7 @@ export function checkToolUse({ toolName, args, snapshot, config }) {
|
|
|
360
360
|
return { allowed: false, reason: "JSON specialist batch concurrency is limited to 16.", category: "json-specialist" };
|
|
361
361
|
}
|
|
362
362
|
const provider = String(args.provider || args.defaults?.provider || process.env.AGINTI_JSON_PROVIDER || "").trim();
|
|
363
|
-
if (provider && !["deepseek", "openai", "qwen", "venice", "mock"].includes(provider)) {
|
|
363
|
+
if (provider && !["deepseek", "openai", "openrouter", "qwen", "venice", "mock"].includes(provider)) {
|
|
364
364
|
return { allowed: false, reason: `Unknown JSON specialist provider: ${provider}`, category: "json-specialist" };
|
|
365
365
|
}
|
|
366
366
|
const payloadBytes = Buffer.byteLength(JSON.stringify({ defaults: args.defaults || {}, items }), "utf8");
|