longer-agent 0.1.0 → 0.1.2
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 +84 -43
- package/README.zh-CN.md +83 -42
- package/agent_templates/main/agent.yaml +1 -0
- package/dist/auth/openai-oauth.d.ts.map +1 -1
- package/dist/auth/openai-oauth.js +6 -10
- package/dist/auth/openai-oauth.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +81 -60
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +6 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +115 -26
- package/dist/commands.js.map +1 -1
- package/dist/config.d.ts +19 -26
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +80 -120
- package/dist/config.js.map +1 -1
- package/dist/dotenv.d.ts +18 -0
- package/dist/dotenv.d.ts.map +1 -0
- package/dist/dotenv.js +91 -0
- package/dist/dotenv.js.map +1 -0
- package/dist/home-path.d.ts +3 -0
- package/dist/home-path.d.ts.map +1 -0
- package/dist/home-path.js +7 -0
- package/dist/home-path.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/init-wizard.d.ts +2 -3
- package/dist/init-wizard.d.ts.map +1 -1
- package/dist/init-wizard.js +349 -145
- package/dist/init-wizard.js.map +1 -1
- package/dist/managed-provider-credentials.d.ts +23 -0
- package/dist/managed-provider-credentials.d.ts.map +1 -0
- package/dist/managed-provider-credentials.js +56 -0
- package/dist/managed-provider-credentials.js.map +1 -0
- package/dist/mcp-client.d.ts.map +1 -1
- package/dist/mcp-client.js +2 -1
- package/dist/mcp-client.js.map +1 -1
- package/dist/mcp-config.d.ts +13 -0
- package/dist/mcp-config.d.ts.map +1 -0
- package/dist/mcp-config.js +64 -0
- package/dist/mcp-config.js.map +1 -0
- package/dist/model-discovery.d.ts +20 -0
- package/dist/model-discovery.d.ts.map +1 -0
- package/dist/model-discovery.js +47 -0
- package/dist/model-discovery.js.map +1 -0
- package/dist/model-selection.d.ts +1 -1
- package/dist/model-selection.d.ts.map +1 -1
- package/dist/model-selection.js +20 -10
- package/dist/model-selection.js.map +1 -1
- package/dist/persistence.d.ts +12 -0
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +8 -6
- package/dist/persistence.js.map +1 -1
- package/dist/provider-credential-flow.d.ts +28 -0
- package/dist/provider-credential-flow.d.ts.map +1 -0
- package/dist/provider-credential-flow.js +112 -0
- package/dist/provider-credential-flow.js.map +1 -0
- package/dist/provider-presets.d.ts +4 -0
- package/dist/provider-presets.d.ts.map +1 -1
- package/dist/provider-presets.js +26 -9
- package/dist/provider-presets.js.map +1 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +3 -2
- package/dist/providers/registry.js.map +1 -1
- package/dist/session.d.ts +6 -15
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +34 -60
- package/dist/session.js.map +1 -1
- package/dist/settings.d.ts +8 -27
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +4 -108
- package/dist/settings.js.map +1 -1
- package/dist/tui/app.d.ts.map +1 -1
- package/dist/tui/app.js +116 -2
- package/dist/tui/app.js.map +1 -1
- package/dist/tui/components/command-prompt-panel.d.ts +22 -0
- package/dist/tui/components/command-prompt-panel.d.ts.map +1 -0
- package/dist/tui/components/command-prompt-panel.js +26 -0
- package/dist/tui/components/command-prompt-panel.js.map +1 -0
- package/dist/tui/components/logo-panel.d.ts.map +1 -1
- package/dist/tui/components/logo-panel.js +2 -4
- package/dist/tui/components/logo-panel.js.map +1 -1
- package/dist/update-check.d.ts +19 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +116 -0
- package/dist/update-check.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +7 -3
- package/configExample.yaml +0 -83
package/dist/init-wizard.js
CHANGED
|
@@ -2,67 +2,183 @@
|
|
|
2
2
|
* Initialization wizard for LongerAgent.
|
|
3
3
|
*
|
|
4
4
|
* Provides an interactive first-run setup experience using @inquirer/prompts.
|
|
5
|
+
* Saves provider configuration to ~/.longeragent/tui-preferences.json.
|
|
5
6
|
* Supports Ctrl+C / ESC to go back to the previous step.
|
|
6
7
|
*/
|
|
7
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
8
9
|
import { join } from "node:path";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
10
|
import { checkbox, select, input, confirm } from "@inquirer/prompts";
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { getLongerAgentHomeDir } from "./home-path.js";
|
|
12
|
+
import { PROVIDER_PRESETS, } from "./provider-presets.js";
|
|
13
|
+
import { fetchModelsFromServer } from "./model-discovery.js";
|
|
14
|
+
import { setDotenvKey } from "./dotenv.js";
|
|
15
|
+
import { detectManagedCredentialCandidates, hasAnyManagedCredential, hasManagedCredential, isManagedProvider, } from "./managed-provider-credentials.js";
|
|
16
|
+
import { ensureManagedProviderCredential, } from "./provider-credential-flow.js";
|
|
14
17
|
// ------------------------------------------------------------------
|
|
15
18
|
// Helpers
|
|
16
19
|
// ------------------------------------------------------------------
|
|
17
20
|
function isUserCancel(err) {
|
|
18
21
|
if (!err || typeof err !== "object")
|
|
19
22
|
return false;
|
|
20
|
-
// @inquirer/prompts throws ExitPromptError on Ctrl+C
|
|
21
23
|
return err.name === "ExitPromptError" ||
|
|
22
24
|
err.code === "ERR_USE_AFTER_CLOSE";
|
|
23
25
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
""
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Load existing preferences from tui-preferences.json (if present).
|
|
28
|
+
*/
|
|
29
|
+
function loadExistingPreferences(homeDir) {
|
|
30
|
+
const prefsPath = join(homeDir, "tui-preferences.json");
|
|
31
|
+
if (!existsSync(prefsPath))
|
|
32
|
+
return null;
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(readFileSync(prefsPath, "utf-8"));
|
|
35
|
+
return {
|
|
36
|
+
version: raw.version ?? 1,
|
|
37
|
+
modelConfigName: raw.model_config_name ?? undefined,
|
|
38
|
+
modelProvider: raw.model_provider ?? undefined,
|
|
39
|
+
modelSelectionKey: raw.model_selection_key ?? undefined,
|
|
40
|
+
modelId: raw.model_id ?? undefined,
|
|
41
|
+
thinkingLevel: raw.thinking_level ?? "default",
|
|
42
|
+
cacheHitEnabled: raw.cache_hit_enabled ?? true,
|
|
43
|
+
accentColor: raw.accent_color ?? undefined,
|
|
44
|
+
disabledSkills: Array.isArray(raw.disabled_skills) ? raw.disabled_skills : undefined,
|
|
45
|
+
providerEnvVars: raw.provider_env_vars ?? undefined,
|
|
46
|
+
localProviders: raw.local_providers ?? undefined,
|
|
47
|
+
contextRatio: typeof raw.context_ratio === "number" ? raw.context_ratio : undefined,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Save preferences to tui-preferences.json (atomic write).
|
|
56
|
+
*/
|
|
57
|
+
function savePreferences(homeDir, prefs) {
|
|
58
|
+
mkdirSync(homeDir, { recursive: true });
|
|
59
|
+
const file = join(homeDir, "tui-preferences.json");
|
|
60
|
+
const tmp = file + ".tmp";
|
|
61
|
+
writeFileSync(tmp, JSON.stringify({
|
|
62
|
+
version: prefs.version ?? 1,
|
|
63
|
+
model_config_name: prefs.modelConfigName ?? null,
|
|
64
|
+
model_provider: prefs.modelProvider ?? null,
|
|
65
|
+
model_selection_key: prefs.modelSelectionKey ?? null,
|
|
66
|
+
model_id: prefs.modelId ?? null,
|
|
67
|
+
thinking_level: prefs.thinkingLevel ?? "default",
|
|
68
|
+
cache_hit_enabled: prefs.cacheHitEnabled ?? true,
|
|
69
|
+
accent_color: prefs.accentColor ?? null,
|
|
70
|
+
disabled_skills: prefs.disabledSkills ?? null,
|
|
71
|
+
provider_env_vars: prefs.providerEnvVars ?? null,
|
|
72
|
+
local_providers: prefs.localProviders ?? null,
|
|
73
|
+
context_ratio: prefs.contextRatio ?? null,
|
|
74
|
+
}, null, 2));
|
|
75
|
+
renameSync(tmp, file);
|
|
37
76
|
}
|
|
38
77
|
// ------------------------------------------------------------------
|
|
39
|
-
// Step functions
|
|
78
|
+
// Step functions
|
|
40
79
|
// ------------------------------------------------------------------
|
|
80
|
+
/**
|
|
81
|
+
* Build a deduplicated top-level choice list.
|
|
82
|
+
* Grouped presets (kimi, minimax, glm) appear once using their groupLabel.
|
|
83
|
+
* The value is either a preset ID or a group key prefixed with "group:".
|
|
84
|
+
*/
|
|
85
|
+
function buildProviderChoices() {
|
|
86
|
+
const choices = [];
|
|
87
|
+
const seenGroups = new Set();
|
|
88
|
+
for (const p of PROVIDER_PRESETS) {
|
|
89
|
+
if (p.group) {
|
|
90
|
+
if (seenGroups.has(p.group))
|
|
91
|
+
continue;
|
|
92
|
+
seenGroups.add(p.group);
|
|
93
|
+
// Check if any preset in this group has a detected key
|
|
94
|
+
const groupPresets = PROVIDER_PRESETS.filter((pp) => pp.group === p.group);
|
|
95
|
+
const anyKeyDetected = groupPresets.some((pp) => isManagedProvider(pp.id)
|
|
96
|
+
? hasManagedCredential(pp.id) || detectManagedCredentialCandidates(pp.id).length > 0
|
|
97
|
+
: Boolean(process.env[pp.envVar]));
|
|
98
|
+
const suffix = anyKeyDetected ? " ✓ key detected" : "";
|
|
99
|
+
choices.push({
|
|
100
|
+
name: `${p.groupLabel ?? p.group}${suffix}`,
|
|
101
|
+
value: `group:${p.group}`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const suffix = p.localServer
|
|
106
|
+
? ""
|
|
107
|
+
: (isManagedProvider(p.id)
|
|
108
|
+
? (hasManagedCredential(p.id) || detectManagedCredentialCandidates(p.id).length > 0)
|
|
109
|
+
: Boolean(process.env[p.envVar]))
|
|
110
|
+
? " ✓ key detected"
|
|
111
|
+
: "";
|
|
112
|
+
choices.push({
|
|
113
|
+
name: `${p.name}${suffix}`,
|
|
114
|
+
value: p.id,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return choices;
|
|
119
|
+
}
|
|
41
120
|
async function stepSelectProviders() {
|
|
42
|
-
|
|
121
|
+
const topLevel = await checkbox({
|
|
43
122
|
message: "Select providers (space to toggle, enter to confirm)",
|
|
44
|
-
choices:
|
|
45
|
-
const envSet = process.env[p.envVar] ? " ✓ key detected" : "";
|
|
46
|
-
return {
|
|
47
|
-
name: `${p.name}${envSet}`,
|
|
48
|
-
value: p.id,
|
|
49
|
-
};
|
|
50
|
-
}),
|
|
123
|
+
choices: buildProviderChoices(),
|
|
51
124
|
required: true,
|
|
52
125
|
});
|
|
126
|
+
// Expand group selections into concrete preset IDs via sub-select
|
|
127
|
+
const resolved = [];
|
|
128
|
+
for (const selection of topLevel) {
|
|
129
|
+
if (selection.startsWith("group:")) {
|
|
130
|
+
const groupKey = selection.slice("group:".length);
|
|
131
|
+
const members = PROVIDER_PRESETS.filter((p) => p.group === groupKey);
|
|
132
|
+
if (members.length === 1) {
|
|
133
|
+
resolved.push(members[0].id);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const subChoice = await checkbox({
|
|
137
|
+
message: `${members[0].groupLabel ?? groupKey}: Select variants`,
|
|
138
|
+
choices: members.map((m) => ({
|
|
139
|
+
name: `${m.subLabel ?? m.name}${isManagedProvider(m.id)
|
|
140
|
+
? (hasManagedCredential(m.id) || detectManagedCredentialCandidates(m.id).length > 0)
|
|
141
|
+
? " ✓ key detected"
|
|
142
|
+
: ""
|
|
143
|
+
: process.env[m.envVar]
|
|
144
|
+
? " ✓ key detected"
|
|
145
|
+
: ""}`,
|
|
146
|
+
value: m.id,
|
|
147
|
+
})),
|
|
148
|
+
required: true,
|
|
149
|
+
});
|
|
150
|
+
resolved.push(...subChoice);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
resolved.push(selection);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return resolved;
|
|
53
158
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
159
|
+
function createInitPromptAdapter() {
|
|
160
|
+
return {
|
|
161
|
+
select: async (request) => {
|
|
162
|
+
return select({
|
|
163
|
+
message: request.message,
|
|
164
|
+
choices: request.options.map((option) => ({
|
|
165
|
+
name: option.description ? `${option.label} — ${option.description}` : option.label,
|
|
166
|
+
value: option.value,
|
|
167
|
+
})),
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
secret: async (request) => {
|
|
171
|
+
const value = await input({
|
|
172
|
+
message: request.message,
|
|
173
|
+
});
|
|
174
|
+
if (!request.allowEmpty && value.trim() === "")
|
|
175
|
+
return "";
|
|
176
|
+
return value;
|
|
177
|
+
},
|
|
178
|
+
};
|
|
62
179
|
}
|
|
63
180
|
async function stepConfigureProvider(provider) {
|
|
64
|
-
|
|
65
|
-
// ── OpenAI Codex (OAuth) — browser or device code login ──
|
|
181
|
+
// ── OpenAI Codex (OAuth) ──
|
|
66
182
|
if (provider.id === "openai-codex") {
|
|
67
183
|
console.log(` ${provider.name}: Logging in with your ChatGPT account...\n`);
|
|
68
184
|
const { browserLogin, deviceCodeLogin, saveOAuthTokens, hasOAuthTokens } = await import("./auth/openai-oauth.js");
|
|
@@ -96,126 +212,167 @@ async function stepConfigureProvider(provider) {
|
|
|
96
212
|
saveOAuthTokens(tokens);
|
|
97
213
|
console.log("\n Login successful!\n");
|
|
98
214
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const selectedModelId = await select({
|
|
215
|
+
// Select model
|
|
216
|
+
const selectedModelKey = await select({
|
|
102
217
|
message: `${provider.name}: Select model`,
|
|
103
218
|
choices: provider.models.map((m) => ({
|
|
104
219
|
name: m.label,
|
|
105
220
|
value: m.key,
|
|
106
221
|
})),
|
|
107
222
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
223
|
+
return {
|
|
224
|
+
providerId: provider.id,
|
|
225
|
+
envVar: "_OPENAI_CODEX_OAUTH",
|
|
226
|
+
defaultModelConfigName: `${provider.id}:${selectedModelKey}`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// ── Local inference servers (oMLX, LM Studio) ──
|
|
230
|
+
if (provider.localServer && provider.defaultBaseUrl) {
|
|
231
|
+
console.log(` Default: ${provider.defaultBaseUrl} (press Enter to use)\n`);
|
|
232
|
+
const baseUrl = await input({
|
|
233
|
+
message: `${provider.name}: Server URL`,
|
|
234
|
+
default: provider.defaultBaseUrl,
|
|
235
|
+
});
|
|
236
|
+
console.log(` Connecting to ${baseUrl} ...`);
|
|
237
|
+
const discovered = await fetchModelsFromServer(baseUrl);
|
|
238
|
+
let modelId;
|
|
239
|
+
let contextLength;
|
|
240
|
+
if (discovered.length > 0) {
|
|
241
|
+
console.log(` Found ${discovered.length} model(s)\n`);
|
|
242
|
+
modelId = await select({
|
|
243
|
+
message: `${provider.name}: Select model`,
|
|
244
|
+
choices: discovered.map((m) => ({
|
|
245
|
+
name: m.contextLength
|
|
246
|
+
? `${m.id} (${Math.round(m.contextLength / 1024)}K ctx)`
|
|
247
|
+
: m.id,
|
|
248
|
+
value: m.id,
|
|
249
|
+
})),
|
|
250
|
+
});
|
|
251
|
+
contextLength = discovered.find((m) => m.id === modelId)?.contextLength;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
console.log(" Could not reach server or no models loaded.\n" +
|
|
255
|
+
" Please make sure the server is running and has at least one model loaded.\n");
|
|
256
|
+
modelId = await input({
|
|
257
|
+
message: `${provider.name}: Enter model name manually`,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (!contextLength) {
|
|
261
|
+
const ctxInput = await input({
|
|
262
|
+
message: `${provider.name}: Context length (tokens, e.g. 32768)`,
|
|
263
|
+
default: "32768",
|
|
264
|
+
});
|
|
265
|
+
contextLength = parseInt(ctxInput, 10) || 32768;
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
providerId: provider.id,
|
|
269
|
+
localProvider: { baseUrl, model: modelId, contextLength },
|
|
270
|
+
defaultModelConfigName: `${provider.id}:${modelId}`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
if (isManagedProvider(provider.id)) {
|
|
274
|
+
const result = await ensureManagedProviderCredential(provider.id, createInitPromptAdapter(), { mode: "init", allowReplaceExisting: true });
|
|
275
|
+
if (result.status === "skipped") {
|
|
276
|
+
return { providerId: provider.id, skipped: true };
|
|
277
|
+
}
|
|
278
|
+
console.log(` ✓ Saved to ~/.longeragent/.env as ${result.envVar}\n`);
|
|
279
|
+
let defaultModelConfigName;
|
|
280
|
+
if (provider.models.length > 0) {
|
|
281
|
+
const selectedModelKey = await select({
|
|
282
|
+
message: `${provider.name}: Select default model`,
|
|
283
|
+
choices: provider.models.map((m) => ({
|
|
284
|
+
name: m.label,
|
|
285
|
+
value: m.key,
|
|
286
|
+
})),
|
|
287
|
+
});
|
|
288
|
+
defaultModelConfigName = `${provider.id}:${selectedModelKey}`;
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
providerId: provider.id,
|
|
292
|
+
envVar: result.envVar,
|
|
293
|
+
defaultModelConfigName,
|
|
294
|
+
};
|
|
114
295
|
}
|
|
115
296
|
// ── Standard API key providers ──
|
|
116
|
-
const
|
|
297
|
+
const envVarName = provider.envVar;
|
|
298
|
+
const envValue = process.env[envVarName];
|
|
117
299
|
if (envValue) {
|
|
118
300
|
const choice = await select({
|
|
119
|
-
message: `${provider.name}: ${
|
|
301
|
+
message: `${provider.name}: ${envVarName} detected in environment`,
|
|
120
302
|
choices: [
|
|
121
303
|
{ name: "Use it", value: "use" },
|
|
122
|
-
{ name: "
|
|
304
|
+
{ name: "Paste a different key for LongerAgent", value: "paste" },
|
|
123
305
|
],
|
|
124
306
|
});
|
|
125
|
-
if (choice === "
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
307
|
+
if (choice === "paste") {
|
|
308
|
+
const key = await input({ message: `${provider.name}: Paste API key` });
|
|
309
|
+
if (key.trim()) {
|
|
310
|
+
setDotenvKey(envVarName, key.trim());
|
|
311
|
+
console.log(` ✓ Saved to ~/.longeragent/.env\n`);
|
|
312
|
+
}
|
|
130
313
|
}
|
|
131
314
|
}
|
|
132
315
|
else {
|
|
133
|
-
|
|
134
|
-
message: `${provider.name}: Paste API key (
|
|
316
|
+
const key = await input({
|
|
317
|
+
message: `${provider.name}: Paste API key (Enter to skip, set ${envVarName} later)`,
|
|
135
318
|
});
|
|
136
|
-
if (
|
|
137
|
-
|
|
319
|
+
if (key.trim()) {
|
|
320
|
+
setDotenvKey(envVarName, key.trim());
|
|
321
|
+
console.log(` ✓ Saved to ~/.longeragent/.env\n`);
|
|
138
322
|
}
|
|
139
323
|
}
|
|
140
|
-
// Model selection
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
324
|
+
// Model selection
|
|
325
|
+
let defaultModelConfigName;
|
|
326
|
+
if (provider.models.length > 0) {
|
|
327
|
+
const selectedModelKey = await select({
|
|
328
|
+
message: `${provider.name}: Select default model`,
|
|
144
329
|
choices: provider.models.map((m) => ({
|
|
145
330
|
name: m.label,
|
|
146
331
|
value: m.key,
|
|
147
332
|
})),
|
|
148
|
-
required: true,
|
|
149
333
|
});
|
|
150
|
-
|
|
151
|
-
configName: openRouterConfigName(provider.models.find((model) => model.key === modelId)),
|
|
152
|
-
rawConfig: buildProviderPresetRawConfig(provider.id, provider.models.find((model) => model.key === modelId), apiKey),
|
|
153
|
-
}));
|
|
334
|
+
defaultModelConfigName = `${provider.id}:${selectedModelKey}`;
|
|
154
335
|
}
|
|
155
|
-
|
|
156
|
-
const selectedModelId = await select({
|
|
157
|
-
message: `${provider.name}: Select model`,
|
|
158
|
-
choices: provider.models.map((m) => ({
|
|
159
|
-
name: m.label,
|
|
160
|
-
value: m.key,
|
|
161
|
-
})),
|
|
162
|
-
});
|
|
163
|
-
const selectedModel = provider.models.find((model) => model.key === selectedModelId);
|
|
164
|
-
const configName = `my-${provider.id.replace(/[^a-z0-9]/g, "")}`;
|
|
165
|
-
return [{
|
|
166
|
-
configName,
|
|
167
|
-
rawConfig: buildProviderPresetRawConfig(provider.id, selectedModel, apiKey),
|
|
168
|
-
}];
|
|
169
|
-
}
|
|
170
|
-
async function stepSelectDefault(models) {
|
|
171
|
-
if (models.length <= 1)
|
|
172
|
-
return models[0]?.configName ?? "";
|
|
173
|
-
return await select({
|
|
174
|
-
message: "Select default model",
|
|
175
|
-
choices: models.map((m) => ({
|
|
176
|
-
name: `${m.configName} (${m.rawConfig["provider"]}/${m.rawConfig["model"]})`,
|
|
177
|
-
value: m.configName,
|
|
178
|
-
})),
|
|
179
|
-
});
|
|
336
|
+
return { providerId: provider.id, envVar: envVarName, defaultModelConfigName };
|
|
180
337
|
}
|
|
181
338
|
export async function runInitWizard() {
|
|
182
|
-
const homeDir =
|
|
183
|
-
const
|
|
339
|
+
const homeDir = getLongerAgentHomeDir();
|
|
340
|
+
const existing = loadExistingPreferences(homeDir);
|
|
341
|
+
const hasLegacyCloudProviders = Boolean(existing?.providerEnvVars
|
|
342
|
+
&& Object.keys(existing.providerEnvVars).some((providerId) => !isManagedProvider(providerId)));
|
|
343
|
+
const hasExisting = existing &&
|
|
344
|
+
(hasLegacyCloudProviders
|
|
345
|
+
|| (existing.localProviders && Object.keys(existing.localProviders).length > 0)
|
|
346
|
+
|| hasAnyManagedCredential());
|
|
184
347
|
console.log();
|
|
185
348
|
console.log(" ╔══════════════════════════════════════╗");
|
|
186
349
|
console.log(" ║ Welcome to LongerAgent Setup! ║");
|
|
187
350
|
console.log(" ╚══════════════════════════════════════╝");
|
|
188
351
|
console.log(" (Ctrl+C to go back, double Ctrl+C to quit)\n");
|
|
189
|
-
let step =
|
|
352
|
+
let step = hasExisting ? 0 /* Step.CHECK_EXISTING */ : 1 /* Step.SELECT_PROVIDERS */;
|
|
190
353
|
let selectedProviderIds = [];
|
|
191
|
-
let
|
|
354
|
+
let results = [];
|
|
192
355
|
let providerIdx = 0;
|
|
193
|
-
let
|
|
194
|
-
// Track how many models each provider step added (for correct Ctrl+C rollback)
|
|
195
|
-
const modelsPerStep = [];
|
|
356
|
+
let defaultModelConfigName = "";
|
|
196
357
|
while (step !== 4 /* Step.WRITE */) {
|
|
197
358
|
try {
|
|
198
359
|
switch (step) {
|
|
199
360
|
case 0 /* Step.CHECK_EXISTING */: {
|
|
200
|
-
console.log(` Existing
|
|
361
|
+
console.log(` Existing configuration found.`);
|
|
201
362
|
const useExisting = await confirm({
|
|
202
363
|
message: "Use existing configuration?",
|
|
203
364
|
default: true,
|
|
204
365
|
});
|
|
205
366
|
if (useExisting) {
|
|
206
367
|
console.log("\n ✓ Using existing configuration.\n");
|
|
207
|
-
return {
|
|
208
|
-
configPath,
|
|
209
|
-
templatesPath: join(homeDir, "agent_templates"),
|
|
210
|
-
skillsPath: join(homeDir, "skills"),
|
|
211
|
-
};
|
|
368
|
+
return { homeDir };
|
|
212
369
|
}
|
|
213
370
|
step = 1 /* Step.SELECT_PROVIDERS */;
|
|
214
371
|
break;
|
|
215
372
|
}
|
|
216
373
|
case 1 /* Step.SELECT_PROVIDERS */: {
|
|
217
374
|
selectedProviderIds = await stepSelectProviders();
|
|
218
|
-
|
|
375
|
+
results = [];
|
|
219
376
|
providerIdx = 0;
|
|
220
377
|
step = 2 /* Step.CONFIGURE_PROVIDERS */;
|
|
221
378
|
break;
|
|
@@ -227,18 +384,28 @@ export async function runInitWizard() {
|
|
|
227
384
|
break;
|
|
228
385
|
}
|
|
229
386
|
console.log();
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
modelsPerStep.push(results.length);
|
|
387
|
+
const result = await stepConfigureProvider(selectedProviders[providerIdx]);
|
|
388
|
+
results.push(result);
|
|
233
389
|
providerIdx++;
|
|
234
|
-
// Stay in CONFIGURE_PROVIDERS until all done
|
|
235
390
|
if (providerIdx >= selectedProviders.length) {
|
|
236
391
|
step = 3 /* Step.SELECT_DEFAULT */;
|
|
237
392
|
}
|
|
238
393
|
break;
|
|
239
394
|
}
|
|
240
395
|
case 3 /* Step.SELECT_DEFAULT */: {
|
|
241
|
-
|
|
396
|
+
// Collect all possible default model config names
|
|
397
|
+
const candidates = results
|
|
398
|
+
.filter((r) => r.defaultModelConfigName)
|
|
399
|
+
.map((r) => r.defaultModelConfigName);
|
|
400
|
+
if (candidates.length <= 1) {
|
|
401
|
+
defaultModelConfigName = candidates[0] ?? "";
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
defaultModelConfigName = await select({
|
|
405
|
+
message: "Select default model",
|
|
406
|
+
choices: candidates.map((c) => ({ name: c, value: c })),
|
|
407
|
+
});
|
|
408
|
+
}
|
|
242
409
|
step = 4 /* Step.WRITE */;
|
|
243
410
|
break;
|
|
244
411
|
}
|
|
@@ -247,14 +414,12 @@ export async function runInitWizard() {
|
|
|
247
414
|
catch (err) {
|
|
248
415
|
if (!isUserCancel(err))
|
|
249
416
|
throw err;
|
|
250
|
-
// Go back one step
|
|
251
417
|
switch (step) {
|
|
252
418
|
case 0 /* Step.CHECK_EXISTING */:
|
|
253
|
-
// Can't go further back — quit
|
|
254
419
|
console.log("\n Setup cancelled.\n");
|
|
255
420
|
throw err;
|
|
256
421
|
case 1 /* Step.SELECT_PROVIDERS */:
|
|
257
|
-
if (
|
|
422
|
+
if (hasExisting) {
|
|
258
423
|
step = 0 /* Step.CHECK_EXISTING */;
|
|
259
424
|
}
|
|
260
425
|
else {
|
|
@@ -264,20 +429,15 @@ export async function runInitWizard() {
|
|
|
264
429
|
break;
|
|
265
430
|
case 2 /* Step.CONFIGURE_PROVIDERS */:
|
|
266
431
|
if (providerIdx > 0) {
|
|
267
|
-
|
|
268
|
-
const popCount = modelsPerStep.pop() ?? 1;
|
|
269
|
-
models.splice(models.length - popCount, popCount);
|
|
432
|
+
results.pop();
|
|
270
433
|
providerIdx--;
|
|
271
434
|
}
|
|
272
435
|
else {
|
|
273
|
-
// Go back to provider selection
|
|
274
436
|
step = 1 /* Step.SELECT_PROVIDERS */;
|
|
275
437
|
}
|
|
276
438
|
break;
|
|
277
439
|
case 3 /* Step.SELECT_DEFAULT */: {
|
|
278
|
-
|
|
279
|
-
const popCount = modelsPerStep.pop() ?? 1;
|
|
280
|
-
models.splice(models.length - popCount, popCount);
|
|
440
|
+
results.pop();
|
|
281
441
|
providerIdx = Math.max(0, providerIdx - 1);
|
|
282
442
|
step = 2 /* Step.CONFIGURE_PROVIDERS */;
|
|
283
443
|
break;
|
|
@@ -286,43 +446,87 @@ export async function runInitWizard() {
|
|
|
286
446
|
console.log();
|
|
287
447
|
}
|
|
288
448
|
}
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
449
|
+
// Build preferences from results
|
|
450
|
+
const providerEnvVars = {};
|
|
451
|
+
const localProviders = {};
|
|
452
|
+
for (const r of results) {
|
|
453
|
+
if (r.localProvider) {
|
|
454
|
+
localProviders[r.providerId] = r.localProvider;
|
|
455
|
+
}
|
|
456
|
+
else if (r.envVar && !isManagedProvider(r.providerId)) {
|
|
457
|
+
providerEnvVars[r.providerId] = r.envVar;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Parse default model config name into provider + model
|
|
461
|
+
let modelProvider;
|
|
462
|
+
let modelSelectionKey;
|
|
463
|
+
let modelId;
|
|
464
|
+
if (defaultModelConfigName) {
|
|
465
|
+
const colonIdx = defaultModelConfigName.indexOf(":");
|
|
466
|
+
if (colonIdx > 0) {
|
|
467
|
+
modelProvider = defaultModelConfigName.slice(0, colonIdx);
|
|
468
|
+
modelSelectionKey = defaultModelConfigName.slice(colonIdx + 1);
|
|
469
|
+
modelId = modelSelectionKey;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// Merge with existing preferences (keep accent color, thinking level, etc.)
|
|
473
|
+
const prefs = {
|
|
474
|
+
version: 1,
|
|
475
|
+
thinkingLevel: existing?.thinkingLevel ?? "default",
|
|
476
|
+
cacheHitEnabled: existing?.cacheHitEnabled ?? true,
|
|
477
|
+
accentColor: existing?.accentColor,
|
|
478
|
+
disabledSkills: existing?.disabledSkills,
|
|
479
|
+
providerEnvVars,
|
|
480
|
+
localProviders: Object.keys(localProviders).length > 0 ? localProviders : undefined,
|
|
481
|
+
contextRatio: existing?.contextRatio,
|
|
482
|
+
modelConfigName: defaultModelConfigName || undefined,
|
|
483
|
+
modelProvider,
|
|
484
|
+
modelSelectionKey,
|
|
485
|
+
modelId,
|
|
486
|
+
};
|
|
487
|
+
// Save preferences
|
|
488
|
+
savePreferences(homeDir, prefs);
|
|
489
|
+
// Ensure user override directories
|
|
490
|
+
mkdirSync(join(homeDir, "agent_templates"), { recursive: true });
|
|
491
|
+
mkdirSync(join(homeDir, "skills"), { recursive: true });
|
|
298
492
|
// Summary
|
|
299
493
|
console.log();
|
|
300
494
|
console.log(" ✓ Configuration saved");
|
|
301
|
-
console.log(`
|
|
495
|
+
console.log(` Preferences: ${join(homeDir, "tui-preferences.json")}`);
|
|
302
496
|
console.log();
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
if (
|
|
309
|
-
console.log(`
|
|
497
|
+
if (defaultModelConfigName) {
|
|
498
|
+
console.log(` Default model: ${defaultModelConfigName}`);
|
|
499
|
+
}
|
|
500
|
+
for (const r of results) {
|
|
501
|
+
const preset = PROVIDER_PRESETS.find((p) => p.id === r.providerId);
|
|
502
|
+
if (r.skipped) {
|
|
503
|
+
console.log(` - ${preset?.name ?? r.providerId} (skipped)`);
|
|
504
|
+
continue;
|
|
310
505
|
}
|
|
311
|
-
|
|
312
|
-
console.log(`
|
|
313
|
-
|
|
506
|
+
if (r.localProvider) {
|
|
507
|
+
console.log(` ✓ ${preset?.name ?? r.providerId} (no API key needed)`);
|
|
508
|
+
}
|
|
509
|
+
else if (isManagedProvider(r.providerId) && r.envVar && process.env[r.envVar]) {
|
|
510
|
+
console.log(` ✓ ${preset?.name ?? r.providerId} (${r.envVar})`);
|
|
511
|
+
}
|
|
512
|
+
else if (r.envVar && process.env[r.envVar]) {
|
|
513
|
+
console.log(` ✓ ${r.envVar}`);
|
|
514
|
+
}
|
|
515
|
+
else if (r.envVar) {
|
|
516
|
+
console.log(` ✗ ${r.envVar} (not set)`);
|
|
314
517
|
}
|
|
315
518
|
}
|
|
519
|
+
const missing = results.filter((r) => r.envVar && !r.localProvider && !r.skipped && !process.env[r.envVar]);
|
|
316
520
|
if (missing.length > 0) {
|
|
317
521
|
console.log();
|
|
318
522
|
console.log(" Set the missing keys before starting:");
|
|
319
|
-
for (const
|
|
320
|
-
console.log(` export ${
|
|
523
|
+
for (const r of missing) {
|
|
524
|
+
console.log(` export ${r.envVar}="your-key-here"`);
|
|
321
525
|
}
|
|
322
526
|
}
|
|
323
527
|
console.log();
|
|
324
528
|
console.log(" Run 'longeragent' to start.");
|
|
325
529
|
console.log();
|
|
326
|
-
return {
|
|
530
|
+
return { homeDir };
|
|
327
531
|
}
|
|
328
532
|
//# sourceMappingURL=init-wizard.js.map
|