@sonnechasser/ntrp 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1933 -623
- package/dist/index.js.map +1 -1
- package/dist/investigation/verbosity-cli.js +903 -205
- package/dist/investigation/verbosity-cli.js.map +1 -1
- package/dist/mcp/server.js +928 -225
- package/dist/mcp/server.js.map +1 -1
- package/dist/whimsy/time-bank-smoke.js +1810 -495
- package/dist/whimsy/time-bank-smoke.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1755,7 +1755,7 @@ function migrateUsageIfNeeded(state) {
|
|
|
1755
1755
|
if (state.usage?.first_active_at) return { state, changed: false };
|
|
1756
1756
|
const fromCredits = rebuildFromCredits(state.credits);
|
|
1757
1757
|
const prior = state.usage;
|
|
1758
|
-
const
|
|
1758
|
+
const usage3 = {
|
|
1759
1759
|
sessions_closed: prior?.sessions_closed ?? 0,
|
|
1760
1760
|
llm_calls: prior?.llm_calls ?? 0,
|
|
1761
1761
|
input_tokens: prior?.input_tokens ?? 0,
|
|
@@ -1763,7 +1763,7 @@ function migrateUsageIfNeeded(state) {
|
|
|
1763
1763
|
...fromCredits,
|
|
1764
1764
|
weekly: mergeWeekly(prior?.weekly ?? [], fromCredits.weekly)
|
|
1765
1765
|
};
|
|
1766
|
-
return { state: { ...state, usage:
|
|
1766
|
+
return { state: { ...state, usage: usage3 }, changed: true };
|
|
1767
1767
|
}
|
|
1768
1768
|
var init_usage_backfill = __esm({
|
|
1769
1769
|
"src/whimsy/usage-backfill.ts"() {
|
|
@@ -2287,48 +2287,48 @@ function bumpWeekly2(weekly, patch) {
|
|
|
2287
2287
|
}
|
|
2288
2288
|
function touchUsage(state, patch) {
|
|
2289
2289
|
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
2290
|
-
const
|
|
2290
|
+
const usage3 = ensureUsage(state);
|
|
2291
2291
|
return {
|
|
2292
2292
|
...state,
|
|
2293
2293
|
usage: {
|
|
2294
|
-
...
|
|
2294
|
+
...usage3,
|
|
2295
2295
|
...patch,
|
|
2296
|
-
first_active_at:
|
|
2296
|
+
first_active_at: usage3.first_active_at ?? now2,
|
|
2297
2297
|
last_active_at: now2,
|
|
2298
|
-
weekly: patch.weekly ??
|
|
2298
|
+
weekly: patch.weekly ?? usage3.weekly
|
|
2299
2299
|
}
|
|
2300
2300
|
};
|
|
2301
2301
|
}
|
|
2302
2302
|
function recordUsageFromCredit(action, minutes) {
|
|
2303
2303
|
if (minutes <= 0) return;
|
|
2304
2304
|
let state = loadProgress();
|
|
2305
|
-
const
|
|
2306
|
-
const weekly = bumpWeekly2(
|
|
2305
|
+
const usage3 = ensureUsage(state);
|
|
2306
|
+
const weekly = bumpWeekly2(usage3.weekly, { minutes_saved: minutes, actions: 1 });
|
|
2307
2307
|
const counters = { weekly };
|
|
2308
|
-
if (action === "diagnose" || action === "diagnose_findings") counters.diagnoses =
|
|
2309
|
-
if (action === "metrics" || action === "metrics_findings") counters.metrics_runs =
|
|
2310
|
-
if (action === "deliverable" || action === "deliverable_deck") counters.deliverables =
|
|
2311
|
-
if (action === "nl_answer") counters.nl_exchanges =
|
|
2308
|
+
if (action === "diagnose" || action === "diagnose_findings") counters.diagnoses = usage3.diagnoses + 1;
|
|
2309
|
+
if (action === "metrics" || action === "metrics_findings") counters.metrics_runs = usage3.metrics_runs + 1;
|
|
2310
|
+
if (action === "deliverable" || action === "deliverable_deck") counters.deliverables = usage3.deliverables + 1;
|
|
2311
|
+
if (action === "nl_answer") counters.nl_exchanges = usage3.nl_exchanges + 1;
|
|
2312
2312
|
state = touchUsage(state, counters);
|
|
2313
2313
|
saveProgress(state);
|
|
2314
2314
|
}
|
|
2315
2315
|
function recordSessionClosed() {
|
|
2316
2316
|
let state = loadProgress();
|
|
2317
|
-
const
|
|
2317
|
+
const usage3 = ensureUsage(state);
|
|
2318
2318
|
state = touchUsage(state, {
|
|
2319
|
-
sessions_closed:
|
|
2320
|
-
weekly: bumpWeekly2(
|
|
2319
|
+
sessions_closed: usage3.sessions_closed + 1,
|
|
2320
|
+
weekly: bumpWeekly2(usage3.weekly, { actions: 1 })
|
|
2321
2321
|
});
|
|
2322
2322
|
saveProgress(state);
|
|
2323
2323
|
}
|
|
2324
2324
|
function recordLlmUsage(tokenUsage) {
|
|
2325
2325
|
let state = loadProgress();
|
|
2326
|
-
const
|
|
2327
|
-
const weekly = bumpWeekly2(
|
|
2326
|
+
const usage3 = ensureUsage(state);
|
|
2327
|
+
const weekly = bumpWeekly2(usage3.weekly, { llm_calls: 1 });
|
|
2328
2328
|
state = touchUsage(state, {
|
|
2329
|
-
llm_calls:
|
|
2330
|
-
input_tokens:
|
|
2331
|
-
output_tokens:
|
|
2329
|
+
llm_calls: usage3.llm_calls + 1,
|
|
2330
|
+
input_tokens: usage3.input_tokens + (tokenUsage?.input_tokens ?? 0),
|
|
2331
|
+
output_tokens: usage3.output_tokens + (tokenUsage?.output_tokens ?? 0),
|
|
2332
2332
|
weekly
|
|
2333
2333
|
});
|
|
2334
2334
|
saveProgress(state);
|
|
@@ -3109,10 +3109,232 @@ var init_context2 = __esm({
|
|
|
3109
3109
|
}
|
|
3110
3110
|
});
|
|
3111
3111
|
|
|
3112
|
+
// src/ai/llm/providers.ts
|
|
3113
|
+
var providers_exports = {};
|
|
3114
|
+
__export(providers_exports, {
|
|
3115
|
+
findSpecByConfigKey: () => findSpecByConfigKey,
|
|
3116
|
+
getProviderSpec: () => getProviderSpec,
|
|
3117
|
+
isEndpointEnabled: () => isEndpointEnabled,
|
|
3118
|
+
keyConfigNameFor: () => keyConfigNameFor,
|
|
3119
|
+
listProviderSpecs: () => listProviderSpecs,
|
|
3120
|
+
loadCustomProviders: () => loadCustomProviders,
|
|
3121
|
+
modelsUrl: () => modelsUrl,
|
|
3122
|
+
providerLabel: () => providerLabel,
|
|
3123
|
+
removeCustomProvider: () => removeCustomProvider,
|
|
3124
|
+
resetProvidersCache: () => resetProvidersCache,
|
|
3125
|
+
saveCustomProvider: () => saveCustomProvider
|
|
3126
|
+
});
|
|
3127
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
3128
|
+
import { join as join7 } from "path";
|
|
3129
|
+
function providersPath() {
|
|
3130
|
+
return join7(ntrpHome(), "providers.json");
|
|
3131
|
+
}
|
|
3132
|
+
function loadCustomProviders() {
|
|
3133
|
+
if (cachedEntries) return cachedEntries;
|
|
3134
|
+
const path = providersPath();
|
|
3135
|
+
if (!existsSync7(path)) {
|
|
3136
|
+
cachedEntries = [];
|
|
3137
|
+
return cachedEntries;
|
|
3138
|
+
}
|
|
3139
|
+
try {
|
|
3140
|
+
const parsed = JSON.parse(readFileSync6(path, "utf-8"));
|
|
3141
|
+
cachedEntries = Array.isArray(parsed.providers) ? parsed.providers : [];
|
|
3142
|
+
} catch {
|
|
3143
|
+
cachedEntries = [];
|
|
3144
|
+
}
|
|
3145
|
+
return cachedEntries;
|
|
3146
|
+
}
|
|
3147
|
+
function saveCustomProvider(entry) {
|
|
3148
|
+
const entries = loadCustomProviders().filter((e) => e.id !== entry.id);
|
|
3149
|
+
entries.push(entry);
|
|
3150
|
+
writeFileSync6(providersPath(), JSON.stringify({ version: 1, providers: entries }, null, 2) + "\n");
|
|
3151
|
+
cachedEntries = entries;
|
|
3152
|
+
}
|
|
3153
|
+
function removeCustomProvider(id) {
|
|
3154
|
+
const entries = loadCustomProviders().filter((e) => e.id !== id);
|
|
3155
|
+
writeFileSync6(providersPath(), JSON.stringify({ version: 1, providers: entries }, null, 2) + "\n");
|
|
3156
|
+
cachedEntries = entries;
|
|
3157
|
+
}
|
|
3158
|
+
function resetProvidersCache() {
|
|
3159
|
+
cachedEntries = null;
|
|
3160
|
+
}
|
|
3161
|
+
function customEntryToSpec(entry) {
|
|
3162
|
+
return {
|
|
3163
|
+
id: entry.id,
|
|
3164
|
+
label: entry.label ?? entry.id,
|
|
3165
|
+
api: "openai-compat",
|
|
3166
|
+
base_url: entry.base_url.replace(/\/+$/, ""),
|
|
3167
|
+
key_prefixes: [],
|
|
3168
|
+
shared_prefixes: [],
|
|
3169
|
+
key_config_name: keyConfigNameFor(entry.id),
|
|
3170
|
+
requires_key: entry.requires_key ?? false,
|
|
3171
|
+
custom: true
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
function keyConfigNameFor(providerId) {
|
|
3175
|
+
return providerId === "anthropic" ? "api-key" : `${providerId}-api-key`;
|
|
3176
|
+
}
|
|
3177
|
+
function listProviderSpecs() {
|
|
3178
|
+
const customs = loadCustomProviders();
|
|
3179
|
+
const customById = new Map(customs.map((e) => [e.id, e]));
|
|
3180
|
+
const specs = BUILTIN_SPECS.map((spec) => {
|
|
3181
|
+
const override = customById.get(spec.id);
|
|
3182
|
+
if (override?.base_url) {
|
|
3183
|
+
return { ...spec, base_url: override.base_url.replace(/\/+$/, "") };
|
|
3184
|
+
}
|
|
3185
|
+
return spec;
|
|
3186
|
+
});
|
|
3187
|
+
for (const entry of customs) {
|
|
3188
|
+
if (!BUILTIN_SPECS.some((s) => s.id === entry.id)) {
|
|
3189
|
+
specs.push(customEntryToSpec(entry));
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
return specs;
|
|
3193
|
+
}
|
|
3194
|
+
function getProviderSpec(id) {
|
|
3195
|
+
return listProviderSpecs().find((s) => s.id === id);
|
|
3196
|
+
}
|
|
3197
|
+
function findSpecByConfigKey(configKey) {
|
|
3198
|
+
return listProviderSpecs().find((s) => s.key_config_name === configKey);
|
|
3199
|
+
}
|
|
3200
|
+
function isEndpointEnabled(id) {
|
|
3201
|
+
const entry = loadCustomProviders().find((e) => e.id === id);
|
|
3202
|
+
return !!entry && entry.enabled !== false;
|
|
3203
|
+
}
|
|
3204
|
+
function modelsUrl(spec) {
|
|
3205
|
+
if (spec.api === "anthropic") return `${spec.base_url}/v1/models?limit=100`;
|
|
3206
|
+
return `${spec.base_url}/models`;
|
|
3207
|
+
}
|
|
3208
|
+
function providerLabel(id) {
|
|
3209
|
+
return getProviderSpec(id)?.label ?? id;
|
|
3210
|
+
}
|
|
3211
|
+
var BUILTIN_SPECS, cachedEntries;
|
|
3212
|
+
var init_providers = __esm({
|
|
3213
|
+
"src/ai/llm/providers.ts"() {
|
|
3214
|
+
"use strict";
|
|
3215
|
+
init_store();
|
|
3216
|
+
BUILTIN_SPECS = [
|
|
3217
|
+
{
|
|
3218
|
+
id: "anthropic",
|
|
3219
|
+
label: "Anthropic",
|
|
3220
|
+
api: "anthropic",
|
|
3221
|
+
base_url: "https://api.anthropic.com",
|
|
3222
|
+
key_prefixes: ["sk-ant-"],
|
|
3223
|
+
shared_prefixes: [],
|
|
3224
|
+
key_config_name: "api-key",
|
|
3225
|
+
requires_key: true
|
|
3226
|
+
},
|
|
3227
|
+
{
|
|
3228
|
+
id: "openai",
|
|
3229
|
+
label: "OpenAI",
|
|
3230
|
+
api: "openai-compat",
|
|
3231
|
+
base_url: "https://api.openai.com/v1",
|
|
3232
|
+
key_prefixes: ["sk-proj-", "sk-svcacct-", "sk-admin-"],
|
|
3233
|
+
shared_prefixes: ["sk-"],
|
|
3234
|
+
key_config_name: "openai-api-key",
|
|
3235
|
+
env_var: "OPENAI_API_KEY",
|
|
3236
|
+
requires_key: true
|
|
3237
|
+
},
|
|
3238
|
+
{
|
|
3239
|
+
id: "google",
|
|
3240
|
+
label: "Google Gemini",
|
|
3241
|
+
api: "openai-compat",
|
|
3242
|
+
base_url: "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
3243
|
+
key_prefixes: ["AIza"],
|
|
3244
|
+
shared_prefixes: [],
|
|
3245
|
+
key_config_name: "google-api-key",
|
|
3246
|
+
requires_key: true
|
|
3247
|
+
},
|
|
3248
|
+
{
|
|
3249
|
+
id: "groq",
|
|
3250
|
+
label: "Groq",
|
|
3251
|
+
api: "openai-compat",
|
|
3252
|
+
base_url: "https://api.groq.com/openai/v1",
|
|
3253
|
+
key_prefixes: ["gsk_"],
|
|
3254
|
+
shared_prefixes: [],
|
|
3255
|
+
key_config_name: "groq-api-key",
|
|
3256
|
+
requires_key: true
|
|
3257
|
+
},
|
|
3258
|
+
{
|
|
3259
|
+
id: "mistral",
|
|
3260
|
+
label: "Mistral",
|
|
3261
|
+
api: "openai-compat",
|
|
3262
|
+
base_url: "https://api.mistral.ai/v1",
|
|
3263
|
+
key_prefixes: [],
|
|
3264
|
+
shared_prefixes: [],
|
|
3265
|
+
key_config_name: "mistral-api-key",
|
|
3266
|
+
requires_key: true
|
|
3267
|
+
},
|
|
3268
|
+
{
|
|
3269
|
+
id: "deepseek",
|
|
3270
|
+
label: "DeepSeek",
|
|
3271
|
+
api: "openai-compat",
|
|
3272
|
+
base_url: "https://api.deepseek.com/v1",
|
|
3273
|
+
key_prefixes: [],
|
|
3274
|
+
shared_prefixes: ["sk-"],
|
|
3275
|
+
key_config_name: "deepseek-api-key",
|
|
3276
|
+
requires_key: true
|
|
3277
|
+
},
|
|
3278
|
+
{
|
|
3279
|
+
id: "xai",
|
|
3280
|
+
label: "xAI",
|
|
3281
|
+
api: "openai-compat",
|
|
3282
|
+
base_url: "https://api.x.ai/v1",
|
|
3283
|
+
key_prefixes: ["xai-"],
|
|
3284
|
+
shared_prefixes: [],
|
|
3285
|
+
key_config_name: "xai-api-key",
|
|
3286
|
+
requires_key: true
|
|
3287
|
+
},
|
|
3288
|
+
{
|
|
3289
|
+
id: "openrouter",
|
|
3290
|
+
label: "OpenRouter",
|
|
3291
|
+
api: "openai-compat",
|
|
3292
|
+
base_url: "https://openrouter.ai/api/v1",
|
|
3293
|
+
key_prefixes: ["sk-or-"],
|
|
3294
|
+
shared_prefixes: [],
|
|
3295
|
+
key_config_name: "openrouter-api-key",
|
|
3296
|
+
requires_key: true
|
|
3297
|
+
},
|
|
3298
|
+
{
|
|
3299
|
+
id: "together",
|
|
3300
|
+
label: "Together AI",
|
|
3301
|
+
api: "openai-compat",
|
|
3302
|
+
base_url: "https://api.together.xyz/v1",
|
|
3303
|
+
key_prefixes: [],
|
|
3304
|
+
shared_prefixes: [],
|
|
3305
|
+
key_config_name: "together-api-key",
|
|
3306
|
+
requires_key: true
|
|
3307
|
+
},
|
|
3308
|
+
{
|
|
3309
|
+
id: "fireworks",
|
|
3310
|
+
label: "Fireworks AI",
|
|
3311
|
+
api: "openai-compat",
|
|
3312
|
+
base_url: "https://api.fireworks.ai/inference/v1",
|
|
3313
|
+
key_prefixes: ["fw_"],
|
|
3314
|
+
shared_prefixes: [],
|
|
3315
|
+
key_config_name: "fireworks-api-key",
|
|
3316
|
+
requires_key: true
|
|
3317
|
+
},
|
|
3318
|
+
{
|
|
3319
|
+
id: "ollama",
|
|
3320
|
+
label: "Ollama (local)",
|
|
3321
|
+
api: "openai-compat",
|
|
3322
|
+
base_url: "http://localhost:11434/v1",
|
|
3323
|
+
key_prefixes: [],
|
|
3324
|
+
shared_prefixes: [],
|
|
3325
|
+
key_config_name: "ollama-api-key",
|
|
3326
|
+
requires_key: false
|
|
3327
|
+
}
|
|
3328
|
+
];
|
|
3329
|
+
cachedEntries = null;
|
|
3330
|
+
}
|
|
3331
|
+
});
|
|
3332
|
+
|
|
3112
3333
|
// src/config/llm-config.ts
|
|
3113
3334
|
function parseProvider(raw) {
|
|
3114
|
-
if (raw
|
|
3115
|
-
|
|
3335
|
+
if (!raw?.trim()) return void 0;
|
|
3336
|
+
const id = raw.trim();
|
|
3337
|
+
return getProviderSpec(id) ? id : void 0;
|
|
3116
3338
|
}
|
|
3117
3339
|
function parseTier(raw) {
|
|
3118
3340
|
if (raw === "high" || raw === "medium" || raw === "low") return raw;
|
|
@@ -3120,7 +3342,7 @@ function parseTier(raw) {
|
|
|
3120
3342
|
}
|
|
3121
3343
|
function parseFailoverOrder(raw) {
|
|
3122
3344
|
if (!raw?.trim()) return ["openai"];
|
|
3123
|
-
return raw.split(",").map((s) => s.trim()).filter((s) => s
|
|
3345
|
+
return raw.split(",").map((s) => s.trim()).filter((s) => !!s && !!getProviderSpec(s));
|
|
3124
3346
|
}
|
|
3125
3347
|
function parseAutoFailover(raw) {
|
|
3126
3348
|
if (!raw) return false;
|
|
@@ -3135,30 +3357,42 @@ function getOpenAiApiKey() {
|
|
|
3135
3357
|
if (fromConfig) return fromConfig;
|
|
3136
3358
|
return process.env.OPENAI_API_KEY?.trim() || void 0;
|
|
3137
3359
|
}
|
|
3360
|
+
function getProviderApiKey(provider) {
|
|
3361
|
+
const spec = getProviderSpec(provider);
|
|
3362
|
+
if (!spec) return void 0;
|
|
3363
|
+
const record = loadConfig();
|
|
3364
|
+
const fromConfig = record[spec.key_config_name]?.trim();
|
|
3365
|
+
if (fromConfig) return fromConfig;
|
|
3366
|
+
if (spec.env_var) {
|
|
3367
|
+
const fromEnv = process.env[spec.env_var]?.trim();
|
|
3368
|
+
if (fromEnv) return fromEnv;
|
|
3369
|
+
}
|
|
3370
|
+
return void 0;
|
|
3371
|
+
}
|
|
3138
3372
|
function hasProviderKey(provider) {
|
|
3139
|
-
|
|
3140
|
-
|
|
3373
|
+
const spec = getProviderSpec(provider);
|
|
3374
|
+
if (!spec) return false;
|
|
3375
|
+
if (!spec.requires_key) return isEndpointEnabled(spec.id) || !!getProviderApiKey(provider);
|
|
3376
|
+
return !!getProviderApiKey(provider);
|
|
3141
3377
|
}
|
|
3142
3378
|
function getAvailableProviders() {
|
|
3143
|
-
|
|
3144
|
-
if (hasProviderKey("anthropic")) out.push("anthropic");
|
|
3145
|
-
if (hasProviderKey("openai")) out.push("openai");
|
|
3146
|
-
return out;
|
|
3379
|
+
return listProviderSpecs().filter((s) => hasProviderKey(s.id)).map((s) => s.id);
|
|
3147
3380
|
}
|
|
3148
3381
|
function hasAnyLlmProvider() {
|
|
3149
3382
|
return getAvailableProviders().length > 0;
|
|
3150
3383
|
}
|
|
3384
|
+
function hasKeylessConfiguredProvider() {
|
|
3385
|
+
return listProviderSpecs().some((s) => !s.requires_key && hasProviderKey(s.id));
|
|
3386
|
+
}
|
|
3151
3387
|
function applyLazyMigration(config) {
|
|
3152
3388
|
if (migrated) return;
|
|
3153
3389
|
migrated = true;
|
|
3154
3390
|
let changed = false;
|
|
3155
3391
|
const record = config;
|
|
3156
3392
|
if (!record["llm-primary"]) {
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
} else if (record["openai-api-key"] || process.env.OPENAI_API_KEY) {
|
|
3161
|
-
record["llm-primary"] = "openai";
|
|
3393
|
+
const available = getAvailableProviders();
|
|
3394
|
+
if (available.length > 0) {
|
|
3395
|
+
record["llm-primary"] = available[0];
|
|
3162
3396
|
changed = true;
|
|
3163
3397
|
}
|
|
3164
3398
|
}
|
|
@@ -3201,20 +3435,20 @@ function loadLlmConfig() {
|
|
|
3201
3435
|
openaiKey: getOpenAiApiKey()
|
|
3202
3436
|
};
|
|
3203
3437
|
}
|
|
3204
|
-
function getProviderApiKey(provider) {
|
|
3205
|
-
if (provider === "anthropic") return getAnthropicApiKey();
|
|
3206
|
-
return getOpenAiApiKey();
|
|
3207
|
-
}
|
|
3208
3438
|
function getInvestigationApiKey(provider) {
|
|
3209
3439
|
if (provider === "anthropic") {
|
|
3210
3440
|
return process.env.NTRP_INVESTIGATION_API_KEY?.trim() || getAnthropicApiKey();
|
|
3211
3441
|
}
|
|
3212
|
-
|
|
3442
|
+
if (provider === "openai") {
|
|
3443
|
+
return process.env.NTRP_INVESTIGATION_OPENAI_KEY?.trim() || getOpenAiApiKey();
|
|
3444
|
+
}
|
|
3445
|
+
return getProviderApiKey(provider);
|
|
3213
3446
|
}
|
|
3214
3447
|
var migrated;
|
|
3215
3448
|
var init_llm_config = __esm({
|
|
3216
3449
|
"src/config/llm-config.ts"() {
|
|
3217
3450
|
"use strict";
|
|
3451
|
+
init_providers();
|
|
3218
3452
|
init_store();
|
|
3219
3453
|
migrated = false;
|
|
3220
3454
|
}
|
|
@@ -3644,8 +3878,8 @@ var init_admin_confirm = __esm({
|
|
|
3644
3878
|
});
|
|
3645
3879
|
|
|
3646
3880
|
// src/services/scratch-wipe.ts
|
|
3647
|
-
import { existsSync as
|
|
3648
|
-
import { join as
|
|
3881
|
+
import { existsSync as existsSync8, rmSync as rmSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
3882
|
+
import { join as join8 } from "path";
|
|
3649
3883
|
async function performScratchWipe(opts = {}) {
|
|
3650
3884
|
const home = ntrpHome();
|
|
3651
3885
|
const removed = [];
|
|
@@ -3655,25 +3889,25 @@ async function performScratchWipe(opts = {}) {
|
|
|
3655
3889
|
} catch {
|
|
3656
3890
|
}
|
|
3657
3891
|
const targets = [
|
|
3658
|
-
{ path:
|
|
3659
|
-
{ path:
|
|
3660
|
-
{ path:
|
|
3661
|
-
{ path:
|
|
3662
|
-
{ path:
|
|
3663
|
-
{ path:
|
|
3664
|
-
{ path:
|
|
3665
|
-
{ path:
|
|
3666
|
-
{ path:
|
|
3892
|
+
{ path: join8(home, "config.json"), kind: "file" },
|
|
3893
|
+
{ path: join8(home, "profile.json"), kind: "file" },
|
|
3894
|
+
{ path: join8(home, "demo-taxonomy.json"), kind: "file" },
|
|
3895
|
+
{ path: join8(home, "update-check.json"), kind: "file" },
|
|
3896
|
+
{ path: join8(home, "state.json.bak"), kind: "file" },
|
|
3897
|
+
{ path: join8(home, "ntrp.duckdb"), kind: "file" },
|
|
3898
|
+
{ path: join8(home, "ntrp.duckdb.wal"), kind: "file" },
|
|
3899
|
+
{ path: join8(home, "sessions"), kind: "dir" },
|
|
3900
|
+
{ path: join8(home, "datasets"), kind: "dir" }
|
|
3667
3901
|
];
|
|
3668
3902
|
if (opts.includeProgress) {
|
|
3669
3903
|
targets.push(
|
|
3670
|
-
{ path:
|
|
3671
|
-
{ path:
|
|
3672
|
-
{ path:
|
|
3904
|
+
{ path: join8(home, "install.json"), kind: "file" },
|
|
3905
|
+
{ path: join8(home, "progress.json"), kind: "file" },
|
|
3906
|
+
{ path: join8(home, "state.json"), kind: "file" }
|
|
3673
3907
|
);
|
|
3674
3908
|
}
|
|
3675
3909
|
for (const { path, kind } of targets) {
|
|
3676
|
-
if (!
|
|
3910
|
+
if (!existsSync8(path)) continue;
|
|
3677
3911
|
try {
|
|
3678
3912
|
if (kind === "dir") {
|
|
3679
3913
|
rmSync3(path, { recursive: true, force: true });
|
|
@@ -3701,10 +3935,10 @@ var init_scratch_wipe = __esm({
|
|
|
3701
3935
|
});
|
|
3702
3936
|
|
|
3703
3937
|
// src/config/profile.ts
|
|
3704
|
-
import { readFileSync as
|
|
3705
|
-
import { join as
|
|
3938
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
3939
|
+
import { join as join9 } from "path";
|
|
3706
3940
|
function ensureDir5() {
|
|
3707
|
-
if (!
|
|
3941
|
+
if (!existsSync9(NTRP_DIR3)) {
|
|
3708
3942
|
mkdirSync6(NTRP_DIR3, { recursive: true });
|
|
3709
3943
|
}
|
|
3710
3944
|
}
|
|
@@ -3712,16 +3946,16 @@ function profilePath() {
|
|
|
3712
3946
|
return PROFILE_PATH;
|
|
3713
3947
|
}
|
|
3714
3948
|
function profileExists() {
|
|
3715
|
-
return
|
|
3949
|
+
return existsSync9(PROFILE_PATH);
|
|
3716
3950
|
}
|
|
3717
3951
|
function isProfileConfigured(profile = loadProfile()) {
|
|
3718
3952
|
if (!profile) return false;
|
|
3719
3953
|
return profile.company_name.trim().length > 0;
|
|
3720
3954
|
}
|
|
3721
3955
|
function loadProfile() {
|
|
3722
|
-
if (!
|
|
3956
|
+
if (!existsSync9(PROFILE_PATH)) return null;
|
|
3723
3957
|
try {
|
|
3724
|
-
const parsed = JSON.parse(
|
|
3958
|
+
const parsed = JSON.parse(readFileSync7(PROFILE_PATH, "utf-8"));
|
|
3725
3959
|
if (!parsed || typeof parsed !== "object") return null;
|
|
3726
3960
|
return parsed;
|
|
3727
3961
|
} catch {
|
|
@@ -3737,7 +3971,7 @@ function saveProfile(profile) {
|
|
|
3737
3971
|
created_at: profile.created_at || now2,
|
|
3738
3972
|
updated_at: now2
|
|
3739
3973
|
};
|
|
3740
|
-
|
|
3974
|
+
writeFileSync7(PROFILE_PATH, JSON.stringify(toWrite, null, 2) + "\n");
|
|
3741
3975
|
}
|
|
3742
3976
|
function updateProfile(patch) {
|
|
3743
3977
|
const existing = loadProfile();
|
|
@@ -3763,7 +3997,7 @@ var init_profile = __esm({
|
|
|
3763
3997
|
"use strict";
|
|
3764
3998
|
init_store();
|
|
3765
3999
|
NTRP_DIR3 = ntrpHome();
|
|
3766
|
-
PROFILE_PATH =
|
|
4000
|
+
PROFILE_PATH = join9(NTRP_DIR3, "profile.json");
|
|
3767
4001
|
}
|
|
3768
4002
|
});
|
|
3769
4003
|
|
|
@@ -4115,7 +4349,13 @@ function resolvePrimaryApiKey(ctx) {
|
|
|
4115
4349
|
if (isInvestigationMode(ctx)) {
|
|
4116
4350
|
return getInvestigationApiKey(primary) ?? getInvestigationApiKey("anthropic") ?? getInvestigationApiKey("openai");
|
|
4117
4351
|
}
|
|
4118
|
-
|
|
4352
|
+
const primaryKey = getProviderApiKey(primary);
|
|
4353
|
+
if (primaryKey) return primaryKey;
|
|
4354
|
+
for (const provider of getAvailableProviders()) {
|
|
4355
|
+
const key = getProviderApiKey(provider);
|
|
4356
|
+
if (key) return key;
|
|
4357
|
+
}
|
|
4358
|
+
return void 0;
|
|
4119
4359
|
}
|
|
4120
4360
|
function canUseReplAi(ctx) {
|
|
4121
4361
|
if (!ctx) return false;
|
|
@@ -4126,25 +4366,21 @@ function canUseReplAi(ctx) {
|
|
|
4126
4366
|
}
|
|
4127
4367
|
function assertReplAi(ctx) {
|
|
4128
4368
|
if (!ctx) {
|
|
4129
|
-
throw new Error(
|
|
4130
|
-
"AI features require stored API keys. Run `ntrp`, then /config set api-key or /config set openai-api-key."
|
|
4131
|
-
);
|
|
4369
|
+
throw new Error(`AI features require stored API keys. Run \`ntrp\`, then /connect.`);
|
|
4132
4370
|
}
|
|
4133
4371
|
if (!canUseReplAi(ctx)) {
|
|
4134
4372
|
if (!hasAnyLlmProvider()) {
|
|
4135
|
-
throw new Error(
|
|
4136
|
-
"No LLM API key configured. Run: /config set api-key (Anthropic) and/or /config set openai-api-key"
|
|
4137
|
-
);
|
|
4373
|
+
throw new Error(NO_KEY_MESSAGE);
|
|
4138
4374
|
}
|
|
4139
4375
|
throw new Error(
|
|
4140
4376
|
"AI features run only in the interactive REPL or headless mode with stored keys."
|
|
4141
4377
|
);
|
|
4142
4378
|
}
|
|
4143
4379
|
const key = resolvePrimaryApiKey(ctx);
|
|
4144
|
-
if (!key) {
|
|
4145
|
-
throw new Error(
|
|
4380
|
+
if (!key && !hasKeylessConfiguredProvider()) {
|
|
4381
|
+
throw new Error(NO_KEY_MESSAGE);
|
|
4146
4382
|
}
|
|
4147
|
-
return key;
|
|
4383
|
+
return key ?? "";
|
|
4148
4384
|
}
|
|
4149
4385
|
function hasEnvApiKeyHint() {
|
|
4150
4386
|
return !!(process.env.ANTHROPIC_API_KEY ?? process.env.NTRP_API_KEY ?? process.env.OPENAI_API_KEY);
|
|
@@ -4157,10 +4393,12 @@ function describeLlmReadiness() {
|
|
|
4157
4393
|
openai: providers.includes("openai")
|
|
4158
4394
|
};
|
|
4159
4395
|
}
|
|
4396
|
+
var NO_KEY_MESSAGE;
|
|
4160
4397
|
var init_gate = __esm({
|
|
4161
4398
|
"src/ai/llm/gate.ts"() {
|
|
4162
4399
|
"use strict";
|
|
4163
4400
|
init_llm_config();
|
|
4401
|
+
NO_KEY_MESSAGE = "No LLM API key configured. Run /connect and paste any provider's key (Anthropic, OpenAI, Groq, Gemini, ...).";
|
|
4164
4402
|
}
|
|
4165
4403
|
});
|
|
4166
4404
|
|
|
@@ -4185,18 +4423,18 @@ var init_repl_api = __esm({
|
|
|
4185
4423
|
});
|
|
4186
4424
|
|
|
4187
4425
|
// src/demo/taxonomy-cache.ts
|
|
4188
|
-
import { readFileSync as
|
|
4426
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, existsSync as existsSync10, mkdirSync as mkdirSync7, unlinkSync as unlinkSync4 } from "fs";
|
|
4189
4427
|
import { homedir as homedir3 } from "os";
|
|
4190
|
-
import { join as
|
|
4428
|
+
import { join as join10 } from "path";
|
|
4191
4429
|
function ensureDir6() {
|
|
4192
|
-
if (!
|
|
4430
|
+
if (!existsSync10(NTRP_DIR4)) {
|
|
4193
4431
|
mkdirSync7(NTRP_DIR4, { recursive: true });
|
|
4194
4432
|
}
|
|
4195
4433
|
}
|
|
4196
4434
|
function loadCachedTaxonomy(profile) {
|
|
4197
|
-
if (!
|
|
4435
|
+
if (!existsSync10(TAXONOMY_PATH)) return null;
|
|
4198
4436
|
try {
|
|
4199
|
-
const parsed = JSON.parse(
|
|
4437
|
+
const parsed = JSON.parse(readFileSync8(TAXONOMY_PATH, "utf-8"));
|
|
4200
4438
|
if (!parsed || typeof parsed !== "object") return null;
|
|
4201
4439
|
if (parsed.profile_updated_at !== profile.updated_at) return null;
|
|
4202
4440
|
return parsed;
|
|
@@ -4206,10 +4444,10 @@ function loadCachedTaxonomy(profile) {
|
|
|
4206
4444
|
}
|
|
4207
4445
|
function saveCachedTaxonomy(taxonomy) {
|
|
4208
4446
|
ensureDir6();
|
|
4209
|
-
|
|
4447
|
+
writeFileSync8(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
|
|
4210
4448
|
}
|
|
4211
4449
|
function invalidateTaxonomy() {
|
|
4212
|
-
if (
|
|
4450
|
+
if (existsSync10(TAXONOMY_PATH)) {
|
|
4213
4451
|
try {
|
|
4214
4452
|
unlinkSync4(TAXONOMY_PATH);
|
|
4215
4453
|
} catch {
|
|
@@ -4220,8 +4458,8 @@ var NTRP_DIR4, TAXONOMY_PATH;
|
|
|
4220
4458
|
var init_taxonomy_cache = __esm({
|
|
4221
4459
|
"src/demo/taxonomy-cache.ts"() {
|
|
4222
4460
|
"use strict";
|
|
4223
|
-
NTRP_DIR4 =
|
|
4224
|
-
TAXONOMY_PATH =
|
|
4461
|
+
NTRP_DIR4 = join10(homedir3(), ".ntrp");
|
|
4462
|
+
TAXONOMY_PATH = join10(NTRP_DIR4, "demo-taxonomy.json");
|
|
4225
4463
|
}
|
|
4226
4464
|
});
|
|
4227
4465
|
|
|
@@ -4246,6 +4484,12 @@ var init_types = __esm({
|
|
|
4246
4484
|
});
|
|
4247
4485
|
|
|
4248
4486
|
// src/ai/llm/errors.ts
|
|
4487
|
+
function isToolsUnsupportedMessage(message) {
|
|
4488
|
+
const msg = message.toLowerCase();
|
|
4489
|
+
const mentionsTools = msg.includes("tool") || msg.includes("function");
|
|
4490
|
+
const mentionsUnsupported = msg.includes("not support") || msg.includes("unsupported") || msg.includes("no support") || msg.includes("not available") || msg.includes("not enabled");
|
|
4491
|
+
return mentionsTools && mentionsUnsupported;
|
|
4492
|
+
}
|
|
4249
4493
|
function mapAnthropicError(err, provider) {
|
|
4250
4494
|
const e = err;
|
|
4251
4495
|
const status = e.status;
|
|
@@ -4263,6 +4507,9 @@ function mapAnthropicError(err, provider) {
|
|
|
4263
4507
|
if (status === 503) {
|
|
4264
4508
|
return new LlmError("OVERLOADED", message, provider, status);
|
|
4265
4509
|
}
|
|
4510
|
+
if (isToolsUnsupportedMessage(message)) {
|
|
4511
|
+
return new LlmError("TOOLS_UNSUPPORTED", message, provider, status);
|
|
4512
|
+
}
|
|
4266
4513
|
if (status === 404 || message.toLowerCase().includes("model")) {
|
|
4267
4514
|
return new LlmError("MODEL_NOT_FOUND", message, provider, status);
|
|
4268
4515
|
}
|
|
@@ -4271,6 +4518,11 @@ function mapAnthropicError(err, provider) {
|
|
|
4271
4518
|
}
|
|
4272
4519
|
return new LlmError("UNKNOWN", message, provider, status);
|
|
4273
4520
|
}
|
|
4521
|
+
function isModelNotFoundMessage(message) {
|
|
4522
|
+
const msg = message.toLowerCase();
|
|
4523
|
+
if (!msg.includes("model")) return false;
|
|
4524
|
+
return msg.includes("not found") || msg.includes("does not exist") || msg.includes("decommissioned") || msg.includes("deprecated") || msg.includes("retired") || msg.includes("do not have access") || msg.includes("invalid model");
|
|
4525
|
+
}
|
|
4274
4526
|
function mapOpenAiError(err, provider) {
|
|
4275
4527
|
const e = err;
|
|
4276
4528
|
const status = e.status;
|
|
@@ -4285,7 +4537,10 @@ function mapOpenAiError(err, provider) {
|
|
|
4285
4537
|
if (status === 503 || code === "server_error") {
|
|
4286
4538
|
return new LlmError("OVERLOADED", message, provider, status);
|
|
4287
4539
|
}
|
|
4288
|
-
if (
|
|
4540
|
+
if (isToolsUnsupportedMessage(message)) {
|
|
4541
|
+
return new LlmError("TOOLS_UNSUPPORTED", message, provider, status);
|
|
4542
|
+
}
|
|
4543
|
+
if (status === 404 || code === "model_not_found" || code === "model_decommissioned" || isModelNotFoundMessage(message)) {
|
|
4289
4544
|
return new LlmError("MODEL_NOT_FOUND", message, provider, status);
|
|
4290
4545
|
}
|
|
4291
4546
|
if (code === "context_length_exceeded") {
|
|
@@ -4421,8 +4676,15 @@ var init_anthropic = __esm({
|
|
|
4421
4676
|
}
|
|
4422
4677
|
});
|
|
4423
4678
|
|
|
4424
|
-
// src/ai/llm/adapters/openai.ts
|
|
4679
|
+
// src/ai/llm/adapters/openai-compat.ts
|
|
4425
4680
|
import OpenAI from "openai";
|
|
4681
|
+
function makeClient(apiKey, baseUrl) {
|
|
4682
|
+
return new OpenAI({
|
|
4683
|
+
// Keyless endpoints (Ollama) still need a non-empty string for the SDK.
|
|
4684
|
+
apiKey: apiKey || "local",
|
|
4685
|
+
...baseUrl ? { baseURL: baseUrl } : {}
|
|
4686
|
+
});
|
|
4687
|
+
}
|
|
4426
4688
|
function toOpenAiTools(tools) {
|
|
4427
4689
|
return tools.map((t) => ({
|
|
4428
4690
|
type: "function",
|
|
@@ -4491,9 +4753,8 @@ function parseResponse2(message) {
|
|
|
4491
4753
|
assistant_message: { role: "assistant", content: text, tool_calls }
|
|
4492
4754
|
};
|
|
4493
4755
|
}
|
|
4494
|
-
async function
|
|
4495
|
-
const
|
|
4496
|
-
const client = new OpenAI({ apiKey });
|
|
4756
|
+
async function openaiCompatComplete(provider, baseUrl, apiKey, model, req) {
|
|
4757
|
+
const client = makeClient(apiKey, baseUrl);
|
|
4497
4758
|
try {
|
|
4498
4759
|
const response = await client.chat.completions.create({
|
|
4499
4760
|
model,
|
|
@@ -4503,7 +4764,7 @@ async function openaiComplete(apiKey, model, req) {
|
|
|
4503
4764
|
});
|
|
4504
4765
|
const choice = response.choices[0];
|
|
4505
4766
|
if (!choice?.message) {
|
|
4506
|
-
throw new Error(
|
|
4767
|
+
throw new Error(`${provider} returned no message`);
|
|
4507
4768
|
}
|
|
4508
4769
|
const parsed = parseResponse2(choice.message);
|
|
4509
4770
|
if (response.usage) {
|
|
@@ -4514,15 +4775,11 @@ async function openaiComplete(apiKey, model, req) {
|
|
|
4514
4775
|
}
|
|
4515
4776
|
return parsed;
|
|
4516
4777
|
} catch (err) {
|
|
4517
|
-
if (err instanceof OpenAI.APIError) {
|
|
4518
|
-
throw mapOpenAiError(err, provider);
|
|
4519
|
-
}
|
|
4520
4778
|
throw mapOpenAiError(err, provider);
|
|
4521
4779
|
}
|
|
4522
4780
|
}
|
|
4523
|
-
async function*
|
|
4524
|
-
const
|
|
4525
|
-
const client = new OpenAI({ apiKey });
|
|
4781
|
+
async function* openaiCompatStream(provider, baseUrl, apiKey, model, req) {
|
|
4782
|
+
const client = makeClient(apiKey, baseUrl);
|
|
4526
4783
|
try {
|
|
4527
4784
|
const stream = await client.chat.completions.create({
|
|
4528
4785
|
model,
|
|
@@ -4535,68 +4792,118 @@ async function* openaiStream(apiKey, model, req) {
|
|
|
4535
4792
|
if (delta) yield { type: "text_delta", text: delta };
|
|
4536
4793
|
}
|
|
4537
4794
|
} catch (err) {
|
|
4538
|
-
if (err instanceof OpenAI.APIError) {
|
|
4539
|
-
throw mapOpenAiError(err, provider);
|
|
4540
|
-
}
|
|
4541
4795
|
throw mapOpenAiError(err, provider);
|
|
4542
4796
|
}
|
|
4543
4797
|
}
|
|
4544
|
-
var
|
|
4545
|
-
"src/ai/llm/adapters/openai.ts"() {
|
|
4798
|
+
var init_openai_compat = __esm({
|
|
4799
|
+
"src/ai/llm/adapters/openai-compat.ts"() {
|
|
4546
4800
|
"use strict";
|
|
4547
4801
|
init_errors();
|
|
4548
4802
|
}
|
|
4549
4803
|
});
|
|
4550
4804
|
|
|
4551
|
-
// src/ai/llm/
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
if (seen.has(current)) break;
|
|
4564
|
-
seen.add(current);
|
|
4565
|
-
const entry = byId.get(current);
|
|
4566
|
-
if (!entry) return current;
|
|
4567
|
-
if (entry.status === "active") return entry.id;
|
|
4568
|
-
if (!entry.successor_id) {
|
|
4569
|
-
const fallback = cheapestActiveInTier(entry.provider, entry.tier);
|
|
4570
|
-
return fallback?.id ?? current;
|
|
4571
|
-
}
|
|
4572
|
-
current = entry.successor_id;
|
|
4805
|
+
// src/ai/llm/models-cache.ts
|
|
4806
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
4807
|
+
import { join as join11 } from "path";
|
|
4808
|
+
function cachePath() {
|
|
4809
|
+
return join11(ntrpHome(), "models.json");
|
|
4810
|
+
}
|
|
4811
|
+
function loadFile() {
|
|
4812
|
+
if (cached) return cached;
|
|
4813
|
+
const path = cachePath();
|
|
4814
|
+
if (!existsSync11(path)) {
|
|
4815
|
+
cached = { version: 1, providers: {} };
|
|
4816
|
+
return cached;
|
|
4573
4817
|
}
|
|
4574
|
-
|
|
4818
|
+
try {
|
|
4819
|
+
const parsed = JSON.parse(readFileSync9(path, "utf-8"));
|
|
4820
|
+
cached = { version: 1, providers: parsed.providers ?? {} };
|
|
4821
|
+
} catch {
|
|
4822
|
+
cached = { version: 1, providers: {} };
|
|
4823
|
+
}
|
|
4824
|
+
return cached;
|
|
4825
|
+
}
|
|
4826
|
+
function saveFile(file) {
|
|
4827
|
+
writeFileSync9(cachePath(), JSON.stringify(file, null, 2) + "\n");
|
|
4828
|
+
cached = file;
|
|
4829
|
+
}
|
|
4830
|
+
function getProviderModels(provider) {
|
|
4831
|
+
return loadFile().providers[provider];
|
|
4832
|
+
}
|
|
4833
|
+
function setProviderModels(provider, entry) {
|
|
4834
|
+
const file = loadFile();
|
|
4835
|
+
file.providers[provider] = entry;
|
|
4836
|
+
saveFile(file);
|
|
4837
|
+
}
|
|
4838
|
+
function getCachedTierModel(provider, tier) {
|
|
4839
|
+
return getProviderModels(provider)?.tier_stack?.[tier];
|
|
4840
|
+
}
|
|
4841
|
+
function findCachedModel(provider, modelId) {
|
|
4842
|
+
return getProviderModels(provider)?.models.find((m) => m.id === modelId);
|
|
4575
4843
|
}
|
|
4576
|
-
function
|
|
4844
|
+
function cachedModelProvider(modelId) {
|
|
4845
|
+
const file = loadFile();
|
|
4846
|
+
for (const [provider, entry] of Object.entries(file.providers)) {
|
|
4847
|
+
if (entry.models.some((m) => m.id === modelId)) return provider;
|
|
4848
|
+
}
|
|
4849
|
+
return void 0;
|
|
4850
|
+
}
|
|
4851
|
+
function markModelNoTools(provider, modelId) {
|
|
4852
|
+
const file = loadFile();
|
|
4853
|
+
const entry = file.providers[provider];
|
|
4854
|
+
if (!entry) return;
|
|
4855
|
+
const noTools = new Set(entry.quirks?.no_tools ?? []);
|
|
4856
|
+
if (noTools.has(modelId)) return;
|
|
4857
|
+
noTools.add(modelId);
|
|
4858
|
+
entry.quirks = { ...entry.quirks, no_tools: [...noTools] };
|
|
4859
|
+
saveFile(file);
|
|
4860
|
+
}
|
|
4861
|
+
function modelHasNoToolsQuirk(provider, modelId) {
|
|
4862
|
+
return !!getProviderModels(provider)?.quirks?.no_tools?.includes(modelId);
|
|
4863
|
+
}
|
|
4864
|
+
function isProviderCacheStale(provider, ttlMs = CACHE_TTL_MS) {
|
|
4865
|
+
const entry = getProviderModels(provider);
|
|
4866
|
+
if (!entry) return true;
|
|
4867
|
+
const fetched = Date.parse(entry.fetched_at);
|
|
4868
|
+
if (Number.isNaN(fetched)) return true;
|
|
4869
|
+
return Date.now() - fetched > ttlMs;
|
|
4870
|
+
}
|
|
4871
|
+
var CACHE_TTL_MS, cached;
|
|
4872
|
+
var init_models_cache = __esm({
|
|
4873
|
+
"src/ai/llm/models-cache.ts"() {
|
|
4874
|
+
"use strict";
|
|
4875
|
+
init_store();
|
|
4876
|
+
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4877
|
+
cached = null;
|
|
4878
|
+
}
|
|
4879
|
+
});
|
|
4880
|
+
|
|
4881
|
+
// src/ai/llm/catalog.ts
|
|
4882
|
+
function catalogTierDefault(provider, tier) {
|
|
4577
4883
|
const candidates = ENTRIES.filter(
|
|
4578
4884
|
(e) => e.provider === provider && e.tier === tier && e.status === "active"
|
|
4579
4885
|
);
|
|
4580
4886
|
if (candidates.length === 0) return void 0;
|
|
4581
4887
|
return candidates.sort((a, b) => a.relative_cost - b.relative_cost)[0];
|
|
4582
4888
|
}
|
|
4583
|
-
function
|
|
4584
|
-
|
|
4585
|
-
if (!entry) {
|
|
4586
|
-
throw new Error(`No active ${tier}-tier model for provider ${provider} in catalog`);
|
|
4587
|
-
}
|
|
4588
|
-
return entry;
|
|
4889
|
+
function modelProviderHint(modelId) {
|
|
4890
|
+
return cachedModelProvider(modelId) ?? byId.get(modelId)?.provider;
|
|
4589
4891
|
}
|
|
4590
|
-
function
|
|
4591
|
-
if (override)
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4892
|
+
function overrideForProvider(override, provider, activeProvider) {
|
|
4893
|
+
if (!override) return void 0;
|
|
4894
|
+
const hint = modelProviderHint(override);
|
|
4895
|
+
if (hint) return hint === provider ? override : void 0;
|
|
4896
|
+
return provider === activeProvider ? override : void 0;
|
|
4897
|
+
}
|
|
4898
|
+
function resolveModelSafe(provider, tier, override) {
|
|
4899
|
+
if (override) return override;
|
|
4900
|
+
const discovered = getCachedTierModel(provider, tier);
|
|
4901
|
+
if (discovered) return discovered;
|
|
4902
|
+
return catalogTierDefault(provider, tier)?.id;
|
|
4598
4903
|
}
|
|
4599
4904
|
function formatModelLabel(provider, modelId) {
|
|
4905
|
+
const cachedName = findCachedModel(provider, modelId)?.display_name;
|
|
4906
|
+
if (cachedName) return `${provider}/${cachedName}`;
|
|
4600
4907
|
const entry = byId.get(modelId);
|
|
4601
4908
|
return entry ? `${provider}/${entry.display_name}` : `${provider}/${modelId}`;
|
|
4602
4909
|
}
|
|
@@ -4604,6 +4911,7 @@ var ENTRIES, byId;
|
|
|
4604
4911
|
var init_catalog = __esm({
|
|
4605
4912
|
"src/ai/llm/catalog.ts"() {
|
|
4606
4913
|
"use strict";
|
|
4914
|
+
init_models_cache();
|
|
4607
4915
|
ENTRIES = [
|
|
4608
4916
|
{
|
|
4609
4917
|
id: "claude-opus-4-6",
|
|
@@ -4676,6 +4984,306 @@ var init_catalog = __esm({
|
|
|
4676
4984
|
}
|
|
4677
4985
|
});
|
|
4678
4986
|
|
|
4987
|
+
// src/ai/llm/http.ts
|
|
4988
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
4989
|
+
function fixtureResponse(url, headers) {
|
|
4990
|
+
try {
|
|
4991
|
+
const raw = readFileSync10(process.env.NTRP_LLM_HTTP_FIXTURE, "utf-8");
|
|
4992
|
+
const entries = JSON.parse(raw);
|
|
4993
|
+
const headerValues = Object.values(headers).join(" ");
|
|
4994
|
+
for (const entry of entries) {
|
|
4995
|
+
if (!url.includes(entry.url_includes)) continue;
|
|
4996
|
+
if (entry.auth_includes && !headerValues.includes(entry.auth_includes)) continue;
|
|
4997
|
+
return { status: entry.status, ok: entry.status >= 200 && entry.status < 300, body: entry.body };
|
|
4998
|
+
}
|
|
4999
|
+
} catch {
|
|
5000
|
+
}
|
|
5001
|
+
return { status: 0, ok: false, body: void 0 };
|
|
5002
|
+
}
|
|
5003
|
+
async function llmHttpGetJson(url, headers, timeoutMs = 6e3) {
|
|
5004
|
+
if (process.env.NTRP_LLM_HTTP_FIXTURE) {
|
|
5005
|
+
return fixtureResponse(url, headers);
|
|
5006
|
+
}
|
|
5007
|
+
const controller = new AbortController();
|
|
5008
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
5009
|
+
try {
|
|
5010
|
+
const res = await fetch(url, { method: "GET", headers, signal: controller.signal });
|
|
5011
|
+
let body;
|
|
5012
|
+
try {
|
|
5013
|
+
body = await res.json();
|
|
5014
|
+
} catch {
|
|
5015
|
+
body = void 0;
|
|
5016
|
+
}
|
|
5017
|
+
return { status: res.status, ok: res.ok, body };
|
|
5018
|
+
} catch {
|
|
5019
|
+
return { status: 0, ok: false, body: void 0 };
|
|
5020
|
+
} finally {
|
|
5021
|
+
clearTimeout(timer);
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
5024
|
+
var init_http = __esm({
|
|
5025
|
+
"src/ai/llm/http.ts"() {
|
|
5026
|
+
"use strict";
|
|
5027
|
+
}
|
|
5028
|
+
});
|
|
5029
|
+
|
|
5030
|
+
// src/ai/llm/ranking.ts
|
|
5031
|
+
function compareModels(a, b) {
|
|
5032
|
+
const createdA = a.created ?? 0;
|
|
5033
|
+
const createdB = b.created ?? 0;
|
|
5034
|
+
if (createdA !== createdB) return createdB - createdA;
|
|
5035
|
+
const versionA = extractVersion(a.id);
|
|
5036
|
+
const versionB = extractVersion(b.id);
|
|
5037
|
+
if (versionA !== versionB) return versionB - versionA;
|
|
5038
|
+
if (a.id.length !== b.id.length) return a.id.length - b.id.length;
|
|
5039
|
+
return a.id.localeCompare(b.id);
|
|
5040
|
+
}
|
|
5041
|
+
function extractVersion(id) {
|
|
5042
|
+
const match = id.match(/(\d+(?:\.\d+)?)/);
|
|
5043
|
+
return match ? Number(match[1]) : 0;
|
|
5044
|
+
}
|
|
5045
|
+
function pickByPatterns(models, patterns) {
|
|
5046
|
+
for (const pattern of patterns) {
|
|
5047
|
+
const matches = models.filter((m) => pattern.test(m.id));
|
|
5048
|
+
if (matches.length > 0) return [...matches].sort(compareModels)[0];
|
|
5049
|
+
}
|
|
5050
|
+
return void 0;
|
|
5051
|
+
}
|
|
5052
|
+
function genericBucket(model) {
|
|
5053
|
+
if (GENERIC_HIGH.test(model.id)) return "high";
|
|
5054
|
+
if (GENERIC_LOW.test(model.id)) return "low";
|
|
5055
|
+
return "medium";
|
|
5056
|
+
}
|
|
5057
|
+
function genericPick(models, tier) {
|
|
5058
|
+
const bucket = models.filter((m) => genericBucket(m) === tier);
|
|
5059
|
+
if (bucket.length > 0) return [...bucket].sort(compareModels)[0];
|
|
5060
|
+
return void 0;
|
|
5061
|
+
}
|
|
5062
|
+
function rankModels(providerId, models) {
|
|
5063
|
+
if (models.length === 0) return null;
|
|
5064
|
+
const preferences = PROVIDER_PREFERENCES[providerId];
|
|
5065
|
+
const picks = {};
|
|
5066
|
+
for (const tier of ["high", "medium", "low"]) {
|
|
5067
|
+
const preferred = preferences ? pickByPatterns(models, preferences[tier]) : void 0;
|
|
5068
|
+
const generic = preferred ?? genericPick(models, tier);
|
|
5069
|
+
if (generic) picks[tier] = generic.id;
|
|
5070
|
+
}
|
|
5071
|
+
const anyModel = [...models].sort(compareModels)[0].id;
|
|
5072
|
+
const high = picks.high ?? picks.medium ?? picks.low ?? anyModel;
|
|
5073
|
+
const medium = picks.medium ?? picks.high ?? picks.low ?? anyModel;
|
|
5074
|
+
const low = picks.low ?? picks.medium ?? picks.high ?? anyModel;
|
|
5075
|
+
return { high, medium, low };
|
|
5076
|
+
}
|
|
5077
|
+
var PROVIDER_PREFERENCES, GENERIC_LOW, GENERIC_HIGH;
|
|
5078
|
+
var init_ranking = __esm({
|
|
5079
|
+
"src/ai/llm/ranking.ts"() {
|
|
5080
|
+
"use strict";
|
|
5081
|
+
PROVIDER_PREFERENCES = {
|
|
5082
|
+
anthropic: {
|
|
5083
|
+
high: [/^claude-opus/i, /^claude-sonnet/i],
|
|
5084
|
+
medium: [/^claude-sonnet/i, /^claude-haiku/i],
|
|
5085
|
+
low: [/^claude-haiku/i, /^claude-sonnet/i]
|
|
5086
|
+
},
|
|
5087
|
+
openai: {
|
|
5088
|
+
high: [/^gpt-5(?!.*(mini|nano|chat))/i, /^gpt-4\.1(?!.*(mini|nano))/i, /^gpt-4o(?!.*mini)/i, /^o3(?!.*mini)/i],
|
|
5089
|
+
medium: [/^gpt-5.*mini/i, /^gpt-4\.1-mini/i, /^gpt-4o-mini/i, /^o4-mini/i],
|
|
5090
|
+
low: [/^gpt-5.*nano/i, /^gpt-4\.1-nano/i, /^gpt-4o-mini/i]
|
|
5091
|
+
},
|
|
5092
|
+
google: {
|
|
5093
|
+
high: [/^gemini-[\d.]+-pro/i, /^gemini-[\d.]+-flash(?!-lite)/i],
|
|
5094
|
+
medium: [/^gemini-[\d.]+-flash(?!-lite|-8b)/i, /^gemini-[\d.]+-pro/i],
|
|
5095
|
+
low: [/^gemini-[\d.]+-flash-lite/i, /flash-8b/i, /^gemini-[\d.]+-flash(?!-lite)/i]
|
|
5096
|
+
},
|
|
5097
|
+
groq: {
|
|
5098
|
+
high: [/llama-3\.3-70b/i, /gpt-oss-120b/i, /70b/i, /deepseek-r1/i],
|
|
5099
|
+
medium: [/llama-3\.1-8b-instant/i, /gpt-oss-20b/i, /llama.*8b/i],
|
|
5100
|
+
low: [/8b-instant/i, /llama.*8b/i, /gemma/i]
|
|
5101
|
+
},
|
|
5102
|
+
deepseek: {
|
|
5103
|
+
high: [/reasoner/i, /chat/i],
|
|
5104
|
+
medium: [/chat/i],
|
|
5105
|
+
low: [/chat/i]
|
|
5106
|
+
},
|
|
5107
|
+
mistral: {
|
|
5108
|
+
high: [/large/i, /medium/i],
|
|
5109
|
+
medium: [/medium/i, /^mistral-small/i],
|
|
5110
|
+
low: [/ministral/i, /small/i, /tiny/i]
|
|
5111
|
+
},
|
|
5112
|
+
xai: {
|
|
5113
|
+
high: [/^grok-\d+(?!.*(mini|fast))/i, /^grok(?!.*(mini|fast))/i],
|
|
5114
|
+
medium: [/^grok.*mini(?!.*fast)/i, /^grok.*fast/i],
|
|
5115
|
+
low: [/^grok.*mini.*fast/i, /^grok.*mini/i]
|
|
5116
|
+
},
|
|
5117
|
+
openrouter: {
|
|
5118
|
+
high: [/^openrouter\/auto$/i, /claude.*opus/i, /^openai\/gpt-5(?!.*(mini|nano))/i, /gemini.*pro/i],
|
|
5119
|
+
medium: [/claude.*sonnet/i, /gpt-5.*mini/i, /gpt-4\.1-mini/i, /gemini.*flash(?!-lite)/i],
|
|
5120
|
+
low: [/claude.*haiku/i, /nano/i, /flash-lite/i, /mini/i]
|
|
5121
|
+
}
|
|
5122
|
+
};
|
|
5123
|
+
GENERIC_LOW = /(mini|nano|lite|tiny|micro|small|haiku|instant|flash|turbo|\b0?\.?5b\b|\b[1-8]b\b)/i;
|
|
5124
|
+
GENERIC_HIGH = /(opus|ultra|large|max\b|\bpro\b|405b|253b|235b|120b|72b|70b|reason|-r1\b|think|deep)/i;
|
|
5125
|
+
}
|
|
5126
|
+
});
|
|
5127
|
+
|
|
5128
|
+
// src/ai/llm/discovery.ts
|
|
5129
|
+
var discovery_exports = {};
|
|
5130
|
+
__export(discovery_exports, {
|
|
5131
|
+
fetchProviderModels: () => fetchProviderModels,
|
|
5132
|
+
filterChatModels: () => filterChatModels,
|
|
5133
|
+
refreshProviderModels: () => refreshProviderModels,
|
|
5134
|
+
refreshStaleProviderCaches: () => refreshStaleProviderCaches,
|
|
5135
|
+
rerankExcluding: () => rerankExcluding,
|
|
5136
|
+
storeDiscoveredModels: () => storeDiscoveredModels
|
|
5137
|
+
});
|
|
5138
|
+
function authHeaders(spec, apiKey) {
|
|
5139
|
+
if (spec.api === "anthropic") {
|
|
5140
|
+
return { "x-api-key": apiKey ?? "", "anthropic-version": "2023-06-01" };
|
|
5141
|
+
}
|
|
5142
|
+
return apiKey ? { Authorization: `Bearer ${apiKey}` } : {};
|
|
5143
|
+
}
|
|
5144
|
+
function normalizeItem(spec, item) {
|
|
5145
|
+
let id = item.id ?? "";
|
|
5146
|
+
if (!id) return null;
|
|
5147
|
+
if (id.startsWith("models/")) id = id.slice("models/".length);
|
|
5148
|
+
const model = { id };
|
|
5149
|
+
const display2 = item.display_name ?? item.name;
|
|
5150
|
+
if (display2 && display2 !== id) model.display_name = display2;
|
|
5151
|
+
if (typeof item.created === "number") model.created = item.created;
|
|
5152
|
+
else if (item.created_at) {
|
|
5153
|
+
const parsed = Date.parse(item.created_at);
|
|
5154
|
+
if (!Number.isNaN(parsed)) model.created = Math.floor(parsed / 1e3);
|
|
5155
|
+
}
|
|
5156
|
+
if (typeof item.context_length === "number") model.context_length = item.context_length;
|
|
5157
|
+
if (Array.isArray(item.supported_parameters)) {
|
|
5158
|
+
model.supports_tools = item.supported_parameters.includes("tools");
|
|
5159
|
+
}
|
|
5160
|
+
return model;
|
|
5161
|
+
}
|
|
5162
|
+
async function fetchProviderModels(spec, apiKey, timeoutMs = 6e3) {
|
|
5163
|
+
const headers = authHeaders(spec, apiKey);
|
|
5164
|
+
if (spec.api === "anthropic") {
|
|
5165
|
+
const models2 = [];
|
|
5166
|
+
let url = modelsUrl(spec);
|
|
5167
|
+
for (let page = 0; page < 5 && url; page++) {
|
|
5168
|
+
const res2 = await llmHttpGetJson(url, headers, timeoutMs);
|
|
5169
|
+
if (!res2.ok) return models2.length > 0 ? { ok: true, models: models2 } : { ok: false, status: res2.status };
|
|
5170
|
+
const body2 = res2.body;
|
|
5171
|
+
for (const item of body2?.data ?? []) {
|
|
5172
|
+
const model = normalizeItem(spec, item);
|
|
5173
|
+
if (model) models2.push(model);
|
|
5174
|
+
}
|
|
5175
|
+
url = body2?.has_more && body2.last_id ? `${spec.base_url}/v1/models?limit=100&after_id=${encodeURIComponent(body2.last_id)}` : null;
|
|
5176
|
+
}
|
|
5177
|
+
return { ok: true, models: models2 };
|
|
5178
|
+
}
|
|
5179
|
+
const res = await llmHttpGetJson(modelsUrl(spec), headers, timeoutMs);
|
|
5180
|
+
if (!res.ok) return { ok: false, status: res.status };
|
|
5181
|
+
const body = res.body;
|
|
5182
|
+
const list = Array.isArray(body) ? body : body?.data ?? [];
|
|
5183
|
+
const models = [];
|
|
5184
|
+
for (const item of list) {
|
|
5185
|
+
const model = normalizeItem(spec, item);
|
|
5186
|
+
if (model) models.push(model);
|
|
5187
|
+
}
|
|
5188
|
+
return { ok: true, models };
|
|
5189
|
+
}
|
|
5190
|
+
function filterChatModels(spec, models) {
|
|
5191
|
+
const extra = PROVIDER_EXCLUDE[spec.id];
|
|
5192
|
+
return models.filter((m) => !NON_CHAT.test(m.id) && !(extra && extra.test(m.id)));
|
|
5193
|
+
}
|
|
5194
|
+
function storeDiscoveredModels(providerId, rawModels) {
|
|
5195
|
+
const spec = getProviderSpec(providerId);
|
|
5196
|
+
if (!spec || rawModels.length === 0) return null;
|
|
5197
|
+
const chat = filterChatModels(spec, rawModels);
|
|
5198
|
+
const usable = chat.length > 0 ? chat : rawModels;
|
|
5199
|
+
const stack = rankModels(providerId, usable);
|
|
5200
|
+
if (!stack) return null;
|
|
5201
|
+
const prior = getProviderModels(providerId);
|
|
5202
|
+
const entry = {
|
|
5203
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5204
|
+
models: usable,
|
|
5205
|
+
tier_stack: stack,
|
|
5206
|
+
...prior?.quirks ? { quirks: prior.quirks } : {}
|
|
5207
|
+
};
|
|
5208
|
+
setProviderModels(providerId, entry);
|
|
5209
|
+
return entry;
|
|
5210
|
+
}
|
|
5211
|
+
async function refreshProviderModels(providerId, opts = {}) {
|
|
5212
|
+
const spec = getProviderSpec(providerId);
|
|
5213
|
+
if (!spec) return null;
|
|
5214
|
+
if (!opts.force && !isProviderCacheStale(providerId)) {
|
|
5215
|
+
return getProviderModels(providerId) ?? null;
|
|
5216
|
+
}
|
|
5217
|
+
const apiKey = opts.apiKey ?? getProviderApiKey(providerId);
|
|
5218
|
+
if (spec.requires_key && !apiKey) return null;
|
|
5219
|
+
const result = await fetchProviderModels(spec, apiKey);
|
|
5220
|
+
if (!result.ok) return null;
|
|
5221
|
+
return storeDiscoveredModels(providerId, result.models);
|
|
5222
|
+
}
|
|
5223
|
+
function rerankExcluding(providerId, deadModelId) {
|
|
5224
|
+
const prior = getProviderModels(providerId);
|
|
5225
|
+
if (!prior) return null;
|
|
5226
|
+
const survivors = prior.models.filter((m) => m.id !== deadModelId);
|
|
5227
|
+
const stack = rankModels(providerId, survivors);
|
|
5228
|
+
if (!stack) return null;
|
|
5229
|
+
const entry = { ...prior, models: survivors, tier_stack: stack };
|
|
5230
|
+
setProviderModels(providerId, entry);
|
|
5231
|
+
return entry;
|
|
5232
|
+
}
|
|
5233
|
+
async function refreshStaleProviderCaches() {
|
|
5234
|
+
await Promise.allSettled(
|
|
5235
|
+
getAvailableProviders().filter((p) => isProviderCacheStale(p)).map((p) => refreshProviderModels(p))
|
|
5236
|
+
);
|
|
5237
|
+
}
|
|
5238
|
+
var NON_CHAT, PROVIDER_EXCLUDE;
|
|
5239
|
+
var init_discovery = __esm({
|
|
5240
|
+
"src/ai/llm/discovery.ts"() {
|
|
5241
|
+
"use strict";
|
|
5242
|
+
init_llm_config();
|
|
5243
|
+
init_http();
|
|
5244
|
+
init_models_cache();
|
|
5245
|
+
init_providers();
|
|
5246
|
+
init_ranking();
|
|
5247
|
+
NON_CHAT = /(embed|embedding|whisper|tts|dall-e|davinci|babbage|curie|\bada\b|moderation|-audio|realtime|transcribe|-image|rerank|guard|voice|sora|distil-whisper)/i;
|
|
5248
|
+
PROVIDER_EXCLUDE = {
|
|
5249
|
+
openai: /(chatgpt|-search|deep-research|-pro\b|computer-use|codex-mini|-instruct\b)/i
|
|
5250
|
+
};
|
|
5251
|
+
}
|
|
5252
|
+
});
|
|
5253
|
+
|
|
5254
|
+
// src/ai/llm/heal.ts
|
|
5255
|
+
async function healModelNotFound(opts) {
|
|
5256
|
+
const { provider, tier, deadModel } = opts;
|
|
5257
|
+
const refreshed = await refreshProviderModels(provider, { apiKey: opts.apiKey, force: true });
|
|
5258
|
+
let candidate = refreshed?.tier_stack?.[tier] ?? getCachedTierModel(provider, tier);
|
|
5259
|
+
if (!candidate || candidate === deadModel) {
|
|
5260
|
+
const reranked = rerankExcluding(provider, deadModel);
|
|
5261
|
+
candidate = reranked?.tier_stack?.[tier];
|
|
5262
|
+
}
|
|
5263
|
+
if (!candidate || candidate === deadModel) return null;
|
|
5264
|
+
clearDeadOverride(deadModel, opts.ctx);
|
|
5265
|
+
return {
|
|
5266
|
+
model: candidate,
|
|
5267
|
+
notice: `model ${deadModel} is no longer available \u2014 switched to ${candidate}`
|
|
5268
|
+
};
|
|
5269
|
+
}
|
|
5270
|
+
function clearDeadOverride(deadModel, ctx) {
|
|
5271
|
+
if (getConfigValue("llm-model-override")?.trim() === deadModel) {
|
|
5272
|
+
deleteConfigValue("llm-model-override");
|
|
5273
|
+
}
|
|
5274
|
+
if (ctx?.llm?.modelOverride === deadModel) {
|
|
5275
|
+
ctx.llm.modelOverride = void 0;
|
|
5276
|
+
}
|
|
5277
|
+
}
|
|
5278
|
+
var init_heal = __esm({
|
|
5279
|
+
"src/ai/llm/heal.ts"() {
|
|
5280
|
+
"use strict";
|
|
5281
|
+
init_store();
|
|
5282
|
+
init_discovery();
|
|
5283
|
+
init_models_cache();
|
|
5284
|
+
}
|
|
5285
|
+
});
|
|
5286
|
+
|
|
4679
5287
|
// src/ai/llm/surfaces.ts
|
|
4680
5288
|
function tierForSurface(surface, userTier) {
|
|
4681
5289
|
const spec = SURFACE_SPECS[surface];
|
|
@@ -4747,7 +5355,7 @@ function resolveActiveProvider(ctx) {
|
|
|
4747
5355
|
if (session && hasProviderKey(session)) return session;
|
|
4748
5356
|
const cfg = loadLlmConfig();
|
|
4749
5357
|
if (hasProviderKey(cfg.primary)) return cfg.primary;
|
|
4750
|
-
const available =
|
|
5358
|
+
const available = getAvailableProviders();
|
|
4751
5359
|
if (available.length > 0) return available[0];
|
|
4752
5360
|
return cfg.primary;
|
|
4753
5361
|
}
|
|
@@ -4769,8 +5377,8 @@ function resolveModelForActive(ctx, surface) {
|
|
|
4769
5377
|
const provider = resolveActiveProvider(ctx);
|
|
4770
5378
|
const tier = resolveEffectiveTier(ctx, surface);
|
|
4771
5379
|
const override = resolveEffectiveModelOverride(ctx);
|
|
4772
|
-
const providerOverride = override
|
|
4773
|
-
const modelId =
|
|
5380
|
+
const providerOverride = overrideForProvider(override, provider, provider);
|
|
5381
|
+
const modelId = resolveModelSafe(provider, tier, providerOverride);
|
|
4774
5382
|
return { provider, tier, modelId };
|
|
4775
5383
|
}
|
|
4776
5384
|
function resolveProviderOrder(ctx) {
|
|
@@ -4781,30 +5389,30 @@ function resolveProviderOrder(ctx) {
|
|
|
4781
5389
|
for (const p of cfg.failoverOrder) {
|
|
4782
5390
|
if (p !== active && hasProviderKey(p) && !order.includes(p)) order.push(p);
|
|
4783
5391
|
}
|
|
4784
|
-
for (const p of
|
|
4785
|
-
if (p !== active &&
|
|
5392
|
+
for (const p of getAvailableProviders()) {
|
|
5393
|
+
if (p !== active && !order.includes(p)) order.push(p);
|
|
4786
5394
|
}
|
|
4787
5395
|
return order;
|
|
4788
5396
|
}
|
|
4789
5397
|
function formatActiveStack(ctx, surface = "agentic_investigation") {
|
|
4790
5398
|
const { provider, tier, modelId } = resolveModelForActive(ctx, surface);
|
|
4791
|
-
return `${provider} \xB7 ${tier} \xB7 ${modelId}`;
|
|
5399
|
+
return `${provider} \xB7 ${tier} \xB7 ${modelId ?? "no models yet (run /connect)"}`;
|
|
4792
5400
|
}
|
|
4793
5401
|
function formatActiveStackShort(ctx, surface = "agentic_investigation") {
|
|
4794
5402
|
const { provider, tier } = resolveModelForActive(ctx, surface);
|
|
4795
5403
|
return `${provider} \xB7 ${tier}`;
|
|
4796
5404
|
}
|
|
4797
5405
|
function countAvailableEngines() {
|
|
4798
|
-
return
|
|
5406
|
+
return getAvailableProviders().length;
|
|
4799
5407
|
}
|
|
4800
5408
|
function availableEngineLabels() {
|
|
4801
|
-
return
|
|
5409
|
+
return getAvailableProviders();
|
|
4802
5410
|
}
|
|
4803
5411
|
function validateModelForProvider(modelId, provider) {
|
|
4804
|
-
const
|
|
4805
|
-
if (!
|
|
4806
|
-
if (
|
|
4807
|
-
return `Model ${modelId} belongs to ${
|
|
5412
|
+
const hint = modelProviderHint(modelId);
|
|
5413
|
+
if (!hint) return null;
|
|
5414
|
+
if (hint !== provider) {
|
|
5415
|
+
return `Model ${modelId} belongs to ${hint}. Run /provider ${hint} first.`;
|
|
4808
5416
|
}
|
|
4809
5417
|
return null;
|
|
4810
5418
|
}
|
|
@@ -4818,23 +5426,16 @@ var init_session_state = __esm({
|
|
|
4818
5426
|
});
|
|
4819
5427
|
|
|
4820
5428
|
// src/ai/llm/resolver.ts
|
|
4821
|
-
function getProviderOrder(config, ctx) {
|
|
4822
|
-
void config;
|
|
4823
|
-
return resolveProviderOrder(ctx);
|
|
4824
|
-
}
|
|
4825
5429
|
function resolveCompletionContext(surface, opts = {}) {
|
|
4826
5430
|
const activeProvider = resolveActiveProvider(opts.ctx);
|
|
4827
5431
|
const tier = opts.tier ?? resolveEffectiveTier(opts.ctx, surface);
|
|
4828
5432
|
const override = opts.modelOverride ?? resolveEffectiveModelOverride(opts.ctx);
|
|
4829
5433
|
const providerOrder = resolveProviderOrder(opts.ctx);
|
|
4830
5434
|
const modelByProvider = {};
|
|
4831
|
-
for (const provider of providerOrder) {
|
|
4832
|
-
const providerOverride =
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
if (!modelByProvider[activeProvider]) {
|
|
4836
|
-
const activeOverride = override && getCatalogEntry(override)?.provider === activeProvider ? override : void 0;
|
|
4837
|
-
modelByProvider[activeProvider] = resolveModel(activeProvider, tier, activeOverride);
|
|
5435
|
+
for (const provider of /* @__PURE__ */ new Set([...providerOrder, activeProvider])) {
|
|
5436
|
+
const providerOverride = overrideForProvider(override, provider, activeProvider);
|
|
5437
|
+
const model = resolveModelSafe(provider, tier, providerOverride);
|
|
5438
|
+
if (model) modelByProvider[provider] = model;
|
|
4838
5439
|
}
|
|
4839
5440
|
return {
|
|
4840
5441
|
providerOrder,
|
|
@@ -4860,70 +5461,142 @@ var init_resolver = __esm({
|
|
|
4860
5461
|
|
|
4861
5462
|
// src/ai/llm/failover.ts
|
|
4862
5463
|
async function completeOnProvider(provider, model, apiKey, req) {
|
|
4863
|
-
|
|
4864
|
-
|
|
5464
|
+
const spec = getProviderSpec(provider);
|
|
5465
|
+
if (!spec) {
|
|
5466
|
+
throw new LlmError("UNKNOWN", `Unknown provider "${provider}" \u2014 run /connect to register it.`, provider);
|
|
5467
|
+
}
|
|
5468
|
+
if (spec.api === "anthropic") {
|
|
5469
|
+
return anthropicComplete(apiKey ?? "", model, req);
|
|
5470
|
+
}
|
|
5471
|
+
return openaiCompatComplete(provider, spec.base_url, apiKey, model, req);
|
|
5472
|
+
}
|
|
5473
|
+
function usableKey(provider, ctx) {
|
|
5474
|
+
const spec = getProviderSpec(provider);
|
|
5475
|
+
if (!spec) return { ok: false };
|
|
5476
|
+
const apiKey = getApiKeyForProvider(provider, ctx);
|
|
5477
|
+
if (spec.requires_key && !apiKey) return { ok: false };
|
|
5478
|
+
return { ok: true, apiKey };
|
|
5479
|
+
}
|
|
5480
|
+
async function resolveModelWithDiscovery(provider, cfg, opts) {
|
|
5481
|
+
const known = cfg.modelByProvider[provider];
|
|
5482
|
+
if (known) return known;
|
|
5483
|
+
const override = overrideForProvider(opts.modelOverride, provider, cfg.activeProvider);
|
|
5484
|
+
const direct = resolveModelSafe(provider, cfg.tier, override);
|
|
5485
|
+
if (direct) return direct;
|
|
5486
|
+
await refreshProviderModels(provider, { apiKey: opts.apiKey, force: true }).catch(() => null);
|
|
5487
|
+
return resolveModelSafe(provider, cfg.tier, override);
|
|
5488
|
+
}
|
|
5489
|
+
function stripTools(req) {
|
|
5490
|
+
const { tools: _tools, ...rest } = req;
|
|
5491
|
+
return rest;
|
|
4865
5492
|
}
|
|
4866
5493
|
async function completeWithFailover(req, opts = {}) {
|
|
4867
|
-
const
|
|
5494
|
+
const cfg = resolveCompletionContext(req.surface, {
|
|
4868
5495
|
max_tokens: req.max_tokens,
|
|
4869
5496
|
tier: opts.tier,
|
|
4870
5497
|
modelOverride: opts.modelOverride,
|
|
4871
5498
|
ctx: opts.ctx
|
|
4872
5499
|
});
|
|
4873
|
-
const providers =
|
|
5500
|
+
const providers = cfg.providerOrder;
|
|
4874
5501
|
if (providers.length === 0) {
|
|
4875
|
-
throw new Error(
|
|
5502
|
+
throw new Error(NO_PROVIDER_MESSAGE);
|
|
4876
5503
|
}
|
|
5504
|
+
const notices = [];
|
|
4877
5505
|
let lastError;
|
|
4878
5506
|
let failoverFrom;
|
|
5507
|
+
const buildMeta = (provider, model, response) => ({
|
|
5508
|
+
provider_used: provider,
|
|
5509
|
+
model_used: model,
|
|
5510
|
+
...response.token_usage ?? {},
|
|
5511
|
+
...failoverFrom ? { failover: true, failover_from: failoverFrom } : {},
|
|
5512
|
+
...notices.length > 0 ? { notices: [...notices] } : {}
|
|
5513
|
+
});
|
|
4879
5514
|
for (let i = 0; i < providers.length; i++) {
|
|
4880
5515
|
const provider = providers[i];
|
|
4881
|
-
const
|
|
4882
|
-
if (!
|
|
4883
|
-
let model =
|
|
5516
|
+
const key = usableKey(provider, opts.ctx);
|
|
5517
|
+
if (!key.ok) continue;
|
|
5518
|
+
let model = await resolveModelWithDiscovery(provider, cfg, {
|
|
5519
|
+
modelOverride: opts.modelOverride,
|
|
5520
|
+
apiKey: key.apiKey
|
|
5521
|
+
});
|
|
5522
|
+
if (!model) {
|
|
5523
|
+
lastError = new LlmError(
|
|
5524
|
+
"MODEL_NOT_FOUND",
|
|
5525
|
+
`No models known for provider "${provider}". Run /connect or /model refresh.`,
|
|
5526
|
+
provider
|
|
5527
|
+
);
|
|
5528
|
+
continue;
|
|
5529
|
+
}
|
|
5530
|
+
let effectiveReq = req;
|
|
5531
|
+
if (req.tools?.length && modelHasNoToolsQuirk(provider, model)) {
|
|
5532
|
+
effectiveReq = stripTools(req);
|
|
5533
|
+
notices.push(`${model} doesn't support tool calling \u2014 answering without live data tools`);
|
|
5534
|
+
}
|
|
4884
5535
|
try {
|
|
4885
|
-
const response = await completeOnProvider(provider, model, apiKey,
|
|
4886
|
-
const meta =
|
|
4887
|
-
provider_used: provider,
|
|
4888
|
-
model_used: model,
|
|
4889
|
-
...response.token_usage ?? {},
|
|
4890
|
-
...failoverFrom ? { failover: true, failover_from: failoverFrom } : {}
|
|
4891
|
-
};
|
|
5536
|
+
const response = await completeOnProvider(provider, model, key.apiKey, effectiveReq);
|
|
5537
|
+
const meta = buildMeta(provider, model, response);
|
|
4892
5538
|
recordLlmUsage(response.token_usage);
|
|
4893
5539
|
return { response, meta };
|
|
4894
5540
|
} catch (err) {
|
|
4895
|
-
|
|
5541
|
+
let llmErr = err;
|
|
4896
5542
|
if (llmErr.name !== "LlmError") throw err;
|
|
4897
5543
|
lastError = llmErr;
|
|
4898
|
-
if (llmErr.code === "
|
|
4899
|
-
|
|
5544
|
+
if (llmErr.code === "TOOLS_UNSUPPORTED" && effectiveReq.tools?.length) {
|
|
5545
|
+
markModelNoTools(provider, model);
|
|
5546
|
+
notices.push(`${model} doesn't support tool calling \u2014 retrying without live data tools`);
|
|
4900
5547
|
try {
|
|
4901
|
-
const response = await completeOnProvider(provider, model, apiKey,
|
|
4902
|
-
const meta =
|
|
4903
|
-
provider_used: provider,
|
|
4904
|
-
model_used: model,
|
|
4905
|
-
...response.token_usage ?? {},
|
|
4906
|
-
...failoverFrom ? { failover: true, failover_from: failoverFrom } : {}
|
|
4907
|
-
};
|
|
5548
|
+
const response = await completeOnProvider(provider, model, key.apiKey, stripTools(effectiveReq));
|
|
5549
|
+
const meta = buildMeta(provider, model, response);
|
|
4908
5550
|
recordLlmUsage(response.token_usage);
|
|
4909
5551
|
return { response, meta };
|
|
4910
5552
|
} catch (retryErr) {
|
|
4911
5553
|
const retryLlm = retryErr;
|
|
4912
|
-
if (retryLlm.name
|
|
4913
|
-
|
|
5554
|
+
if (retryLlm.name !== "LlmError") throw retryErr;
|
|
5555
|
+
lastError = retryLlm;
|
|
5556
|
+
llmErr = retryLlm;
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
if (llmErr.code === "MODEL_NOT_FOUND") {
|
|
5560
|
+
const healed = await healModelNotFound({
|
|
5561
|
+
provider,
|
|
5562
|
+
tier: cfg.tier,
|
|
5563
|
+
deadModel: model,
|
|
5564
|
+
apiKey: key.apiKey,
|
|
5565
|
+
ctx: opts.ctx
|
|
5566
|
+
}).catch(() => null);
|
|
5567
|
+
if (healed) {
|
|
5568
|
+
notices.push(healed.notice);
|
|
5569
|
+
model = healed.model;
|
|
5570
|
+
let retryReq = req;
|
|
5571
|
+
if (req.tools?.length && modelHasNoToolsQuirk(provider, model)) {
|
|
5572
|
+
retryReq = stripTools(req);
|
|
5573
|
+
notices.push(`${model} doesn't support tool calling \u2014 answering without live data tools`);
|
|
5574
|
+
}
|
|
5575
|
+
try {
|
|
5576
|
+
const response = await completeOnProvider(provider, model, key.apiKey, retryReq);
|
|
5577
|
+
const meta = buildMeta(provider, model, response);
|
|
5578
|
+
recordLlmUsage(response.token_usage);
|
|
5579
|
+
return { response, meta };
|
|
5580
|
+
} catch (retryErr) {
|
|
5581
|
+
const retryLlm = retryErr;
|
|
5582
|
+
if (retryLlm.name !== "LlmError") throw retryErr;
|
|
5583
|
+
lastError = retryLlm;
|
|
5584
|
+
llmErr = retryLlm;
|
|
5585
|
+
}
|
|
4914
5586
|
}
|
|
4915
5587
|
}
|
|
4916
5588
|
if (!isFailoverEligible(llmErr.code)) throw llmErr;
|
|
4917
5589
|
const next = providers[i + 1];
|
|
4918
5590
|
if (next) {
|
|
4919
5591
|
failoverFrom = failoverFrom ?? provider;
|
|
5592
|
+
notices.push(`${provider} unavailable (${llmErr.code.toLowerCase()}) \u2014 trying ${next}`);
|
|
4920
5593
|
opts.onFailover?.(provider, next, llmErr.code);
|
|
4921
5594
|
continue;
|
|
4922
5595
|
}
|
|
4923
5596
|
throw llmErr;
|
|
4924
5597
|
}
|
|
4925
5598
|
}
|
|
4926
|
-
throw lastError ?? new Error(
|
|
5599
|
+
throw lastError ?? new Error(NO_PROVIDER_MESSAGE);
|
|
4927
5600
|
}
|
|
4928
5601
|
async function* streamWithFailover(req, opts = {}) {
|
|
4929
5602
|
const cfg = resolveCompletionContext(req.surface, {
|
|
@@ -4932,23 +5605,47 @@ async function* streamWithFailover(req, opts = {}) {
|
|
|
4932
5605
|
modelOverride: opts.modelOverride,
|
|
4933
5606
|
ctx: opts.ctx
|
|
4934
5607
|
});
|
|
4935
|
-
const providers =
|
|
5608
|
+
const providers = cfg.providerOrder;
|
|
4936
5609
|
if (providers.length === 0) {
|
|
4937
|
-
throw new Error(
|
|
5610
|
+
throw new Error(NO_PROVIDER_MESSAGE);
|
|
4938
5611
|
}
|
|
5612
|
+
const notices = [];
|
|
4939
5613
|
let lastError;
|
|
4940
5614
|
let failoverFrom;
|
|
5615
|
+
async function* streamOnProvider(provider, model, apiKey) {
|
|
5616
|
+
const spec = getProviderSpec(provider);
|
|
5617
|
+
if (!spec) {
|
|
5618
|
+
throw new LlmError("UNKNOWN", `Unknown provider "${provider}" \u2014 run /connect to register it.`, provider);
|
|
5619
|
+
}
|
|
5620
|
+
if (spec.api === "anthropic") {
|
|
5621
|
+
yield* anthropicStream(apiKey ?? "", model, req);
|
|
5622
|
+
return;
|
|
5623
|
+
}
|
|
5624
|
+
yield* openaiCompatStream(provider, spec.base_url, apiKey, model, req);
|
|
5625
|
+
}
|
|
4941
5626
|
for (let i = 0; i < providers.length; i++) {
|
|
4942
5627
|
const provider = providers[i];
|
|
4943
|
-
const
|
|
4944
|
-
if (!
|
|
4945
|
-
|
|
4946
|
-
|
|
5628
|
+
const key = usableKey(provider, opts.ctx);
|
|
5629
|
+
if (!key.ok) continue;
|
|
5630
|
+
let model = await resolveModelWithDiscovery(provider, cfg, {
|
|
5631
|
+
modelOverride: opts.modelOverride,
|
|
5632
|
+
apiKey: key.apiKey
|
|
5633
|
+
});
|
|
5634
|
+
if (!model) {
|
|
5635
|
+
lastError = new LlmError(
|
|
5636
|
+
"MODEL_NOT_FOUND",
|
|
5637
|
+
`No models known for provider "${provider}". Run /connect or /model refresh.`,
|
|
5638
|
+
provider
|
|
5639
|
+
);
|
|
5640
|
+
continue;
|
|
5641
|
+
}
|
|
5642
|
+
let yieldedAny = false;
|
|
5643
|
+
const attempt = async function* (attemptModel) {
|
|
4947
5644
|
let fullText = "";
|
|
4948
|
-
const
|
|
4949
|
-
for await (const event of streamFn(apiKey, model, req)) {
|
|
5645
|
+
for await (const event of streamOnProvider(provider, attemptModel, key.apiKey)) {
|
|
4950
5646
|
if (event.type === "text_delta") {
|
|
4951
5647
|
fullText += event.text;
|
|
5648
|
+
yieldedAny = true;
|
|
4952
5649
|
yield event;
|
|
4953
5650
|
}
|
|
4954
5651
|
}
|
|
@@ -4956,10 +5653,11 @@ async function* streamWithFailover(req, opts = {}) {
|
|
|
4956
5653
|
recordLlmUsage({ input_tokens: 0, output_tokens: estimatedOut });
|
|
4957
5654
|
const meta = {
|
|
4958
5655
|
provider_used: provider,
|
|
4959
|
-
model_used:
|
|
5656
|
+
model_used: attemptModel,
|
|
4960
5657
|
input_tokens: 0,
|
|
4961
5658
|
output_tokens: estimatedOut,
|
|
4962
|
-
...failoverFrom ? { failover: true, failover_from: failoverFrom } : {}
|
|
5659
|
+
...failoverFrom ? { failover: true, failover_from: failoverFrom } : {},
|
|
5660
|
+
...notices.length > 0 ? { notices: [...notices] } : {}
|
|
4963
5661
|
};
|
|
4964
5662
|
yield {
|
|
4965
5663
|
type: "done",
|
|
@@ -4971,32 +5669,66 @@ async function* streamWithFailover(req, opts = {}) {
|
|
|
4971
5669
|
},
|
|
4972
5670
|
meta
|
|
4973
5671
|
};
|
|
5672
|
+
};
|
|
5673
|
+
try {
|
|
5674
|
+
yield* attempt(model);
|
|
4974
5675
|
return;
|
|
4975
5676
|
} catch (err) {
|
|
4976
5677
|
const llmErr = err;
|
|
4977
5678
|
if (llmErr.name !== "LlmError") throw err;
|
|
4978
5679
|
lastError = llmErr;
|
|
4979
|
-
if (
|
|
5680
|
+
if (yieldedAny) throw llmErr;
|
|
5681
|
+
if (llmErr.code === "MODEL_NOT_FOUND") {
|
|
5682
|
+
const healed = await healModelNotFound({
|
|
5683
|
+
provider,
|
|
5684
|
+
tier: cfg.tier,
|
|
5685
|
+
deadModel: model,
|
|
5686
|
+
apiKey: key.apiKey,
|
|
5687
|
+
ctx: opts.ctx
|
|
5688
|
+
}).catch(() => null);
|
|
5689
|
+
if (healed) {
|
|
5690
|
+
notices.push(healed.notice);
|
|
5691
|
+
model = healed.model;
|
|
5692
|
+
try {
|
|
5693
|
+
yield* attempt(model);
|
|
5694
|
+
return;
|
|
5695
|
+
} catch (retryErr) {
|
|
5696
|
+
const retryLlm = retryErr;
|
|
5697
|
+
if (retryLlm.name !== "LlmError") throw retryErr;
|
|
5698
|
+
lastError = retryLlm;
|
|
5699
|
+
if (yieldedAny) throw retryLlm;
|
|
5700
|
+
}
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5703
|
+
if (!isFailoverEligible(lastError.code)) throw lastError;
|
|
4980
5704
|
const next = providers[i + 1];
|
|
4981
5705
|
if (next) {
|
|
4982
5706
|
failoverFrom = failoverFrom ?? provider;
|
|
4983
|
-
|
|
5707
|
+
notices.push(`${provider} unavailable (${lastError.code.toLowerCase()}) \u2014 trying ${next}`);
|
|
5708
|
+
opts.onFailover?.(provider, next, lastError.code);
|
|
4984
5709
|
continue;
|
|
4985
5710
|
}
|
|
4986
|
-
throw
|
|
5711
|
+
throw lastError;
|
|
4987
5712
|
}
|
|
4988
5713
|
}
|
|
4989
|
-
throw lastError ?? new Error(
|
|
5714
|
+
throw lastError ?? new Error(NO_PROVIDER_MESSAGE);
|
|
4990
5715
|
}
|
|
5716
|
+
var NO_PROVIDER_MESSAGE;
|
|
4991
5717
|
var init_failover = __esm({
|
|
4992
5718
|
"src/ai/llm/failover.ts"() {
|
|
4993
5719
|
"use strict";
|
|
4994
5720
|
init_usage_stats();
|
|
4995
5721
|
init_anthropic();
|
|
4996
|
-
|
|
5722
|
+
init_openai_compat();
|
|
4997
5723
|
init_catalog();
|
|
5724
|
+
init_discovery();
|
|
4998
5725
|
init_errors();
|
|
5726
|
+
init_heal();
|
|
5727
|
+
init_models_cache();
|
|
5728
|
+
init_providers();
|
|
5729
|
+
init_types();
|
|
4999
5730
|
init_resolver();
|
|
5731
|
+
NO_PROVIDER_MESSAGE = "No LLM provider configured. Run /connect and paste any API key (Anthropic, OpenAI, Groq, Gemini, ...).";
|
|
5000
5732
|
}
|
|
5001
5733
|
});
|
|
5002
5734
|
|
|
@@ -5630,29 +6362,258 @@ function showProfile() {
|
|
|
5630
6362
|
console.log(chalk7.dim(` Multi-thread min: ${resolved.thread_depth.multi_thread_threshold} contacts`));
|
|
5631
6363
|
console.log();
|
|
5632
6364
|
}
|
|
5633
|
-
var PRESET_DESCRIPTIONS, PRESET_LABELS;
|
|
5634
|
-
var init_profile2 = __esm({
|
|
5635
|
-
"src/commands/profile.ts"() {
|
|
6365
|
+
var PRESET_DESCRIPTIONS, PRESET_LABELS;
|
|
6366
|
+
var init_profile2 = __esm({
|
|
6367
|
+
"src/commands/profile.ts"() {
|
|
6368
|
+
"use strict";
|
|
6369
|
+
init_store();
|
|
6370
|
+
init_profile();
|
|
6371
|
+
init_profile_presets();
|
|
6372
|
+
init_defaults();
|
|
6373
|
+
init_resolve();
|
|
6374
|
+
init_argparse();
|
|
6375
|
+
init_markdown();
|
|
6376
|
+
init_theme();
|
|
6377
|
+
PRESET_DESCRIPTIONS = {
|
|
6378
|
+
plg: "Product-Led Growth \u2014 shorter cycles, high volume, self-serve focus",
|
|
6379
|
+
smb_velocity: "SMB Velocity \u2014 fast sales cycles, quick close, volume-oriented",
|
|
6380
|
+
mid_market: "Mid-Market \u2014 moderate deal sizes, structured sales process",
|
|
6381
|
+
enterprise: "Enterprise \u2014 long cycles, large deals, multi-threaded engagement"
|
|
6382
|
+
};
|
|
6383
|
+
PRESET_LABELS = {
|
|
6384
|
+
plg: "PLG",
|
|
6385
|
+
smb_velocity: "SMB Velocity",
|
|
6386
|
+
mid_market: "Mid-Market",
|
|
6387
|
+
enterprise: "Enterprise"
|
|
6388
|
+
};
|
|
6389
|
+
}
|
|
6390
|
+
});
|
|
6391
|
+
|
|
6392
|
+
// src/ai/llm/detect.ts
|
|
6393
|
+
function detectProviderByKey(key) {
|
|
6394
|
+
const k = key.trim();
|
|
6395
|
+
const specs = listProviderSpecs();
|
|
6396
|
+
let best;
|
|
6397
|
+
for (const spec of specs) {
|
|
6398
|
+
for (const prefix of spec.key_prefixes) {
|
|
6399
|
+
if (k.startsWith(prefix) && (!best || prefix.length > best.length)) {
|
|
6400
|
+
best = { id: spec.id, length: prefix.length };
|
|
6401
|
+
}
|
|
6402
|
+
}
|
|
6403
|
+
}
|
|
6404
|
+
if (best) return { certain: best.id, candidates: [best.id] };
|
|
6405
|
+
const shared = specs.filter((s) => s.shared_prefixes.some((p) => k.startsWith(p)));
|
|
6406
|
+
if (shared.length > 0) return { candidates: shared.map((s) => s.id) };
|
|
6407
|
+
const noPrefix = specs.filter(
|
|
6408
|
+
(s) => s.requires_key && !s.custom && s.key_prefixes.length === 0 && s.shared_prefixes.length === 0
|
|
6409
|
+
);
|
|
6410
|
+
return { candidates: noPrefix.map((s) => s.id) };
|
|
6411
|
+
}
|
|
6412
|
+
async function probeProviders(key, candidateIds, timeoutMs = 5e3) {
|
|
6413
|
+
const results = await Promise.all(
|
|
6414
|
+
candidateIds.map(async (id) => {
|
|
6415
|
+
const spec = getProviderSpec(id);
|
|
6416
|
+
if (!spec) return { id, ok: false, status: 0 };
|
|
6417
|
+
const result = await fetchProviderModels(spec, key, timeoutMs);
|
|
6418
|
+
if (result.ok) return { id, ok: true, models: result.models };
|
|
6419
|
+
return { id, ok: false, status: result.status };
|
|
6420
|
+
})
|
|
6421
|
+
);
|
|
6422
|
+
const accepted = [];
|
|
6423
|
+
let sawNetworkFailure = false;
|
|
6424
|
+
for (const r of results) {
|
|
6425
|
+
if (r.ok && r.models.length > 0) accepted.push({ provider: r.id, models: r.models });
|
|
6426
|
+
else if (!r.ok && r.status === 0) sawNetworkFailure = true;
|
|
6427
|
+
}
|
|
6428
|
+
return { accepted, sawNetworkFailure };
|
|
6429
|
+
}
|
|
6430
|
+
var init_detect = __esm({
|
|
6431
|
+
"src/ai/llm/detect.ts"() {
|
|
6432
|
+
"use strict";
|
|
6433
|
+
init_discovery();
|
|
6434
|
+
init_providers();
|
|
6435
|
+
}
|
|
6436
|
+
});
|
|
6437
|
+
|
|
6438
|
+
// src/services/connect.ts
|
|
6439
|
+
var connect_exports = {};
|
|
6440
|
+
__export(connect_exports, {
|
|
6441
|
+
ConnectCancelled: () => ConnectCancelled,
|
|
6442
|
+
ConnectError: () => ConnectError,
|
|
6443
|
+
connectCustomEndpoint: () => connectCustomEndpoint,
|
|
6444
|
+
connectKeyless: () => connectKeyless,
|
|
6445
|
+
connectWithKey: () => connectWithKey,
|
|
6446
|
+
describeConnectOutcome: () => describeConnectOutcome
|
|
6447
|
+
});
|
|
6448
|
+
function finishConnect(spec, opts) {
|
|
6449
|
+
const before = getAvailableProviders();
|
|
6450
|
+
if (opts.key) {
|
|
6451
|
+
setConfigValue(spec.key_config_name, opts.key);
|
|
6452
|
+
}
|
|
6453
|
+
const entry = opts.models && opts.models.length > 0 ? storeDiscoveredModels(spec.id, opts.models) : null;
|
|
6454
|
+
const cfg = loadLlmConfig();
|
|
6455
|
+
let becamePrimary = false;
|
|
6456
|
+
if (cfg.primary !== spec.id && (before.length === 0 || !hasProviderKey(cfg.primary))) {
|
|
6457
|
+
setConfigValue("llm-primary", spec.id);
|
|
6458
|
+
becamePrimary = true;
|
|
6459
|
+
}
|
|
6460
|
+
return {
|
|
6461
|
+
provider: spec.id,
|
|
6462
|
+
label: spec.label,
|
|
6463
|
+
modelCount: entry?.models.length ?? 0,
|
|
6464
|
+
...entry ? { stack: entry.tier_stack } : {},
|
|
6465
|
+
becamePrimary,
|
|
6466
|
+
offline: !!opts.offline
|
|
6467
|
+
};
|
|
6468
|
+
}
|
|
6469
|
+
async function connectWithKey(rawKey, opts = {}) {
|
|
6470
|
+
const key = rawKey.trim();
|
|
6471
|
+
if (!key) throw new ConnectError("Empty key.");
|
|
6472
|
+
if (opts.providerId) {
|
|
6473
|
+
const spec = getProviderSpec(opts.providerId);
|
|
6474
|
+
if (!spec) {
|
|
6475
|
+
throw new ConnectError(
|
|
6476
|
+
`Unknown provider "${opts.providerId}". Use a built-in id or /connect --base-url <url> --id ${opts.providerId} for a custom endpoint.`
|
|
6477
|
+
);
|
|
6478
|
+
}
|
|
6479
|
+
const result = await fetchProviderModels(spec, key);
|
|
6480
|
+
if (result.ok) return finishConnect(spec, { key, models: result.models });
|
|
6481
|
+
if (result.status === 0) {
|
|
6482
|
+
return finishConnect(spec, { key, offline: true });
|
|
6483
|
+
}
|
|
6484
|
+
throw new ConnectError(`${spec.label} rejected this key (HTTP ${result.status}) \u2014 double-check it and try again.`);
|
|
6485
|
+
}
|
|
6486
|
+
const detection = detectProviderByKey(key);
|
|
6487
|
+
if (detection.certain) {
|
|
6488
|
+
const spec = getProviderSpec(detection.certain);
|
|
6489
|
+
const result = await fetchProviderModels(spec, key);
|
|
6490
|
+
if (result.ok) return finishConnect(spec, { key, models: result.models });
|
|
6491
|
+
if (result.status === 0) return finishConnect(spec, { key, offline: true });
|
|
6492
|
+
throw new ConnectError(`${spec.label} rejected this key (HTTP ${result.status}) \u2014 double-check it and try again.`);
|
|
6493
|
+
}
|
|
6494
|
+
const report = await probeProviders(key, detection.candidates);
|
|
6495
|
+
if (report.accepted.length === 1) {
|
|
6496
|
+
const match = report.accepted[0];
|
|
6497
|
+
const spec = getProviderSpec(match.provider);
|
|
6498
|
+
if (opts.callbacks?.confirmDetection) {
|
|
6499
|
+
const ok = await opts.callbacks.confirmDetection(match.provider);
|
|
6500
|
+
if (!ok) throw new ConnectCancelled();
|
|
6501
|
+
}
|
|
6502
|
+
return finishConnect(spec, { key, models: match.models });
|
|
6503
|
+
}
|
|
6504
|
+
if (report.accepted.length > 1) {
|
|
6505
|
+
if (opts.callbacks?.chooseProvider) {
|
|
6506
|
+
const chosen = await opts.callbacks.chooseProvider(report.accepted);
|
|
6507
|
+
if (!chosen) throw new ConnectCancelled();
|
|
6508
|
+
const match = report.accepted.find((a) => a.provider === chosen);
|
|
6509
|
+
return finishConnect(getProviderSpec(chosen), { key, models: match.models });
|
|
6510
|
+
}
|
|
6511
|
+
throw new ConnectError(
|
|
6512
|
+
`Multiple providers accepted this key (${report.accepted.map((a) => a.provider).join(", ")}). Re-run with --provider <id>.`
|
|
6513
|
+
);
|
|
6514
|
+
}
|
|
6515
|
+
if (report.sawNetworkFailure) {
|
|
6516
|
+
throw new ConnectError(
|
|
6517
|
+
`Couldn't reach ${detection.candidates.map(providerLabel).join(" / ")} to identify this key. Check your connection, or force a provider with --provider <id>.`
|
|
6518
|
+
);
|
|
6519
|
+
}
|
|
6520
|
+
throw new ConnectError(
|
|
6521
|
+
`No provider accepted this key (tried ${detection.candidates.map(providerLabel).join(", ")}). If it belongs to an OpenAI-compatible endpoint, run /connect --base-url <url>.`
|
|
6522
|
+
);
|
|
6523
|
+
}
|
|
6524
|
+
async function connectCustomEndpoint(opts) {
|
|
6525
|
+
const id = opts.id.trim().toLowerCase();
|
|
6526
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(id)) {
|
|
6527
|
+
throw new ConnectError(`Invalid provider id "${opts.id}" \u2014 use letters, digits, dashes.`);
|
|
6528
|
+
}
|
|
6529
|
+
const baseUrl = opts.baseUrl.trim().replace(/\/+$/, "");
|
|
6530
|
+
if (!/^https?:\/\//.test(baseUrl)) {
|
|
6531
|
+
throw new ConnectError(`Base URL must start with http:// or https:// (got "${opts.baseUrl}").`);
|
|
6532
|
+
}
|
|
6533
|
+
const builtin = getProviderSpec(id);
|
|
6534
|
+
const spec = builtin ? { ...builtin, base_url: baseUrl } : {
|
|
6535
|
+
id,
|
|
6536
|
+
label: opts.label ?? id,
|
|
6537
|
+
api: "openai-compat",
|
|
6538
|
+
base_url: baseUrl,
|
|
6539
|
+
key_prefixes: [],
|
|
6540
|
+
shared_prefixes: [],
|
|
6541
|
+
key_config_name: `${id}-api-key`,
|
|
6542
|
+
requires_key: !!opts.key,
|
|
6543
|
+
custom: true
|
|
6544
|
+
};
|
|
6545
|
+
const result = await fetchProviderModels(spec, opts.key);
|
|
6546
|
+
if (!result.ok) {
|
|
6547
|
+
if (result.status === 0) {
|
|
6548
|
+
throw new ConnectError(`Couldn't reach ${baseUrl} \u2014 check the URL (expects an OpenAI-compatible /models endpoint).`);
|
|
6549
|
+
}
|
|
6550
|
+
if (result.status === 401 || result.status === 403) {
|
|
6551
|
+
throw new ConnectError(
|
|
6552
|
+
opts.key ? `${spec.label} rejected the key (HTTP ${result.status}).` : `${spec.label} requires an API key (HTTP ${result.status}) \u2014 re-run with a key.`
|
|
6553
|
+
);
|
|
6554
|
+
}
|
|
6555
|
+
throw new ConnectError(`${baseUrl} answered HTTP ${result.status} \u2014 is this an OpenAI-compatible endpoint?`);
|
|
6556
|
+
}
|
|
6557
|
+
if (result.models.length === 0) {
|
|
6558
|
+
throw new ConnectError(`${baseUrl} lists no models \u2014 nothing to connect.`);
|
|
6559
|
+
}
|
|
6560
|
+
saveCustomProvider({
|
|
6561
|
+
id,
|
|
6562
|
+
...opts.label ? { label: opts.label } : {},
|
|
6563
|
+
base_url: baseUrl,
|
|
6564
|
+
requires_key: !!opts.key,
|
|
6565
|
+
enabled: true
|
|
6566
|
+
});
|
|
6567
|
+
return finishConnect(getProviderSpec(id), { key: opts.key, models: result.models });
|
|
6568
|
+
}
|
|
6569
|
+
async function connectKeyless(providerId, baseUrl) {
|
|
6570
|
+
const builtin = getProviderSpec(providerId);
|
|
6571
|
+
if (!builtin) throw new ConnectError(`Unknown provider "${providerId}".`);
|
|
6572
|
+
const spec = baseUrl ? { ...builtin, base_url: baseUrl.replace(/\/+$/, "") } : builtin;
|
|
6573
|
+
const result = await fetchProviderModels(spec, void 0);
|
|
6574
|
+
if (!result.ok) {
|
|
6575
|
+
throw new ConnectError(
|
|
6576
|
+
`${spec.label} not reachable at ${spec.base_url}. Is it running? (ollama serve, then retry)`
|
|
6577
|
+
);
|
|
6578
|
+
}
|
|
6579
|
+
if (result.models.length === 0) {
|
|
6580
|
+
throw new ConnectError(`${spec.label} is running but has no models \u2014 pull one first (e.g. \`ollama pull llama3.2\`).`);
|
|
6581
|
+
}
|
|
6582
|
+
saveCustomProvider({ id: spec.id, base_url: spec.base_url, enabled: true });
|
|
6583
|
+
return finishConnect(getProviderSpec(spec.id), { models: result.models });
|
|
6584
|
+
}
|
|
6585
|
+
function describeConnectOutcome(outcome) {
|
|
6586
|
+
const lines = [];
|
|
6587
|
+
if (outcome.offline) {
|
|
6588
|
+
lines.push(`${outcome.label} key saved \u2014 provider unreachable right now, models will be discovered on first use.`);
|
|
6589
|
+
} else {
|
|
6590
|
+
lines.push(`Connected ${outcome.label} \u2014 ${outcome.modelCount} chat model${outcome.modelCount === 1 ? "" : "s"} available.`);
|
|
6591
|
+
}
|
|
6592
|
+
if (outcome.stack) {
|
|
6593
|
+
lines.push(`high ${outcome.stack.high}`);
|
|
6594
|
+
lines.push(`medium ${outcome.stack.medium}`);
|
|
6595
|
+
lines.push(`low ${outcome.stack.low}`);
|
|
6596
|
+
}
|
|
6597
|
+
if (outcome.becamePrimary) {
|
|
6598
|
+
lines.push(`Primary engine: ${outcome.provider}`);
|
|
6599
|
+
}
|
|
6600
|
+
return lines;
|
|
6601
|
+
}
|
|
6602
|
+
var ConnectError, ConnectCancelled;
|
|
6603
|
+
var init_connect = __esm({
|
|
6604
|
+
"src/services/connect.ts"() {
|
|
5636
6605
|
"use strict";
|
|
6606
|
+
init_detect();
|
|
6607
|
+
init_discovery();
|
|
6608
|
+
init_providers();
|
|
6609
|
+
init_llm_config();
|
|
5637
6610
|
init_store();
|
|
5638
|
-
|
|
5639
|
-
init_profile_presets();
|
|
5640
|
-
init_defaults();
|
|
5641
|
-
init_resolve();
|
|
5642
|
-
init_argparse();
|
|
5643
|
-
init_markdown();
|
|
5644
|
-
init_theme();
|
|
5645
|
-
PRESET_DESCRIPTIONS = {
|
|
5646
|
-
plg: "Product-Led Growth \u2014 shorter cycles, high volume, self-serve focus",
|
|
5647
|
-
smb_velocity: "SMB Velocity \u2014 fast sales cycles, quick close, volume-oriented",
|
|
5648
|
-
mid_market: "Mid-Market \u2014 moderate deal sizes, structured sales process",
|
|
5649
|
-
enterprise: "Enterprise \u2014 long cycles, large deals, multi-threaded engagement"
|
|
6611
|
+
ConnectError = class extends Error {
|
|
5650
6612
|
};
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
enterprise: "Enterprise"
|
|
6613
|
+
ConnectCancelled = class extends ConnectError {
|
|
6614
|
+
constructor() {
|
|
6615
|
+
super("Connect cancelled.");
|
|
6616
|
+
}
|
|
5656
6617
|
};
|
|
5657
6618
|
}
|
|
5658
6619
|
});
|
|
@@ -5984,42 +6945,58 @@ function printIntro() {
|
|
|
5984
6945
|
}
|
|
5985
6946
|
async function ensureLlmKeys(session) {
|
|
5986
6947
|
if (hasAnyLlmProvider()) return;
|
|
6948
|
+
const { connectWithKey: connectWithKey2, describeConnectOutcome: describeConnectOutcome2, ConnectCancelled: ConnectCancelled2 } = await Promise.resolve().then(() => (init_connect(), connect_exports));
|
|
6949
|
+
const { providerLabel: providerLabel2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
|
|
6950
|
+
const { countAvailableEngines: countAvailableEngines2 } = await Promise.resolve().then(() => (init_session_state(), session_state_exports));
|
|
5987
6951
|
console.log();
|
|
5988
6952
|
console.log(" " + chalk8.dim("Onboarding uses AI to draft your profile."));
|
|
5989
6953
|
console.log(
|
|
5990
|
-
" " + chalk8.dim("
|
|
6954
|
+
" " + chalk8.dim("Paste any provider's API key \u2014 Anthropic, OpenAI, Groq, Gemini, Mistral, ...")
|
|
5991
6955
|
);
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
{ value: "openai", label: "OpenAI (GPT)", description: "Full parity on all surfaces" }
|
|
5995
|
-
]);
|
|
5996
|
-
const firstLabel = first === "anthropic" ? "Anthropic API key" : "OpenAI API key";
|
|
5997
|
-
const firstKey = await session.askSecret(firstLabel, { confirm: true });
|
|
5998
|
-
if (first === "anthropic") setConfigValue("api-key", firstKey);
|
|
5999
|
-
else setConfigValue("openai-api-key", firstKey);
|
|
6000
|
-
setConfigValue("llm-primary", first);
|
|
6001
|
-
setConfigValue("llm-tier", "high");
|
|
6002
|
-
const second = first === "anthropic" ? "openai" : "anthropic";
|
|
6003
|
-
setConfigValue("llm-failover-order", second);
|
|
6004
|
-
setConfigValue("llm-auto-failover", "off");
|
|
6005
|
-
const addSecond = await session.confirm(
|
|
6006
|
-
`Add a second engine (${second}) for switching in the REPL?`,
|
|
6007
|
-
false
|
|
6956
|
+
console.log(
|
|
6957
|
+
" " + chalk8.dim("NTRP detects the provider and discovers its models. Or run ") + paint("accent", "/connect") + chalk8.dim(" anytime.")
|
|
6008
6958
|
);
|
|
6009
|
-
|
|
6010
|
-
const
|
|
6011
|
-
const
|
|
6012
|
-
|
|
6013
|
-
|
|
6959
|
+
for (; ; ) {
|
|
6960
|
+
const key = await session.askSecret("LLM API key (any provider)", { confirm: false });
|
|
6961
|
+
const spinner = ora({ text: "Identifying provider\u2026", discardStdin: false }).start();
|
|
6962
|
+
try {
|
|
6963
|
+
const outcome = await connectWithKey2(key, {
|
|
6964
|
+
callbacks: {
|
|
6965
|
+
confirmDetection: async (providerId) => {
|
|
6966
|
+
spinner.stop();
|
|
6967
|
+
return session.confirm(`Detected ${providerLabel2(providerId)} \u2014 connect it?`, true);
|
|
6968
|
+
},
|
|
6969
|
+
chooseProvider: async (accepted) => {
|
|
6970
|
+
spinner.stop();
|
|
6971
|
+
return session.choose(
|
|
6972
|
+
"Multiple providers accepted this key \u2014 which is it?",
|
|
6973
|
+
accepted.map((a) => ({ value: a.provider, label: providerLabel2(a.provider) }))
|
|
6974
|
+
);
|
|
6975
|
+
}
|
|
6976
|
+
}
|
|
6977
|
+
});
|
|
6978
|
+
spinner.stop();
|
|
6979
|
+
const [headline, ...rest] = describeConnectOutcome2(outcome);
|
|
6980
|
+
console.log(" " + paint("success", "\u2713") + " " + (headline ?? ""));
|
|
6981
|
+
for (const line of rest) console.log(" " + chalk8.dim(line));
|
|
6982
|
+
} catch (err) {
|
|
6983
|
+
spinner.stop();
|
|
6984
|
+
if (!(err instanceof ConnectCancelled2)) {
|
|
6985
|
+
console.log(" " + chalk8.red(String(err.message ?? err)));
|
|
6986
|
+
}
|
|
6987
|
+
const retry = await session.confirm("Try another key?", true);
|
|
6988
|
+
if (retry) continue;
|
|
6989
|
+
if (!hasAnyLlmProvider()) return;
|
|
6990
|
+
}
|
|
6991
|
+
const addAnother = await session.confirm("Add another engine? (switch anytime with /provider)", false);
|
|
6992
|
+
if (!addAnother) break;
|
|
6993
|
+
}
|
|
6994
|
+
if (countAvailableEngines2() >= 2) {
|
|
6014
6995
|
const enableFailover = await session.confirm(
|
|
6015
6996
|
"Enable auto-failover on rate limits? (off = you choose engine with /provider)",
|
|
6016
6997
|
false
|
|
6017
6998
|
);
|
|
6018
|
-
|
|
6019
|
-
} else {
|
|
6020
|
-
console.log(
|
|
6021
|
-
" " + chalk8.dim(`Single engine \u2014 add ${second} later via /config set ${second === "anthropic" ? "api-key" : "openai-api-key"}.`)
|
|
6022
|
-
);
|
|
6999
|
+
setConfigValue("llm-auto-failover", enableFailover ? "on" : "off");
|
|
6023
7000
|
}
|
|
6024
7001
|
console.log(" " + paint("success", "\u2713") + " " + chalk8.dim("LLM engines configured. Use /provider to switch."));
|
|
6025
7002
|
}
|
|
@@ -6318,16 +7295,16 @@ async function runGlobalAdminCommand(command, line, ctx) {
|
|
|
6318
7295
|
const args = tokens.slice(1);
|
|
6319
7296
|
switch (command) {
|
|
6320
7297
|
case "scratch": {
|
|
6321
|
-
const { handler:
|
|
6322
|
-
return
|
|
7298
|
+
const { handler: handler46 } = await Promise.resolve().then(() => (init_scratch(), scratch_exports));
|
|
7299
|
+
return handler46(args, ctx);
|
|
6323
7300
|
}
|
|
6324
7301
|
case "cleanup": {
|
|
6325
|
-
const { handler:
|
|
6326
|
-
return
|
|
7302
|
+
const { handler: handler46 } = await Promise.resolve().then(() => (init_cleanup(), cleanup_exports));
|
|
7303
|
+
return handler46(args, ctx);
|
|
6327
7304
|
}
|
|
6328
7305
|
case "deactivate-demo": {
|
|
6329
|
-
const { handler:
|
|
6330
|
-
return
|
|
7306
|
+
const { handler: handler46 } = await Promise.resolve().then(() => (init_deactivate_demo(), deactivate_demo_exports));
|
|
7307
|
+
return handler46(args, ctx);
|
|
6331
7308
|
}
|
|
6332
7309
|
default:
|
|
6333
7310
|
return void 0;
|
|
@@ -9218,6 +10195,9 @@ function formatLlmAttribution(meta) {
|
|
|
9218
10195
|
return line;
|
|
9219
10196
|
}
|
|
9220
10197
|
function printLlmAttribution(meta) {
|
|
10198
|
+
for (const notice of meta.notices ?? []) {
|
|
10199
|
+
console.log(chalk13.dim(` ${notice}`));
|
|
10200
|
+
}
|
|
9221
10201
|
const line = formatLlmAttribution(meta);
|
|
9222
10202
|
if (line) console.log(chalk13.dim(` ${line}`));
|
|
9223
10203
|
}
|
|
@@ -9535,6 +10515,7 @@ async function renderDiagnoseStream(options) {
|
|
|
9535
10515
|
let modelUsed = "";
|
|
9536
10516
|
let providerUsed;
|
|
9537
10517
|
let failover;
|
|
10518
|
+
let notices;
|
|
9538
10519
|
let rawPrompt = "";
|
|
9539
10520
|
try {
|
|
9540
10521
|
for await (const event of runFindings(fullResult)) {
|
|
@@ -9550,6 +10531,7 @@ async function renderDiagnoseStream(options) {
|
|
|
9550
10531
|
modelUsed = event.model_used;
|
|
9551
10532
|
providerUsed = event.provider_used;
|
|
9552
10533
|
failover = event.failover;
|
|
10534
|
+
notices = event.usage?.notices;
|
|
9553
10535
|
rawPrompt = event.raw_prompt;
|
|
9554
10536
|
}
|
|
9555
10537
|
}
|
|
@@ -9573,7 +10555,8 @@ async function renderDiagnoseStream(options) {
|
|
|
9573
10555
|
printLlmAttribution({
|
|
9574
10556
|
model_used: modelUsed,
|
|
9575
10557
|
provider_used: providerUsed,
|
|
9576
|
-
failover
|
|
10558
|
+
failover,
|
|
10559
|
+
notices
|
|
9577
10560
|
});
|
|
9578
10561
|
} catch (err) {
|
|
9579
10562
|
findingsSpinner.fail(deep ? "Agentic investigation failed" : "AI findings failed");
|
|
@@ -9953,8 +10936,8 @@ function markFailure(ctx) {
|
|
|
9953
10936
|
}
|
|
9954
10937
|
async function loadOrBuildTaxonomy(profile, forceRegen, ctx) {
|
|
9955
10938
|
if (!forceRegen) {
|
|
9956
|
-
const
|
|
9957
|
-
if (
|
|
10939
|
+
const cached2 = loadCachedTaxonomy(profile);
|
|
10940
|
+
if (cached2) return cached2;
|
|
9958
10941
|
}
|
|
9959
10942
|
const spinnerText = forceRegen ? "Rebuilding market taxonomy\u2026" : "Researching your market taxonomy\u2026";
|
|
9960
10943
|
const spinner = ora3({ text: spinnerText, discardStdin: false }).start();
|
|
@@ -10112,7 +11095,7 @@ __export(ingest_exports, {
|
|
|
10112
11095
|
});
|
|
10113
11096
|
import chalk16 from "chalk";
|
|
10114
11097
|
import ora4 from "ora";
|
|
10115
|
-
import { readFileSync as
|
|
11098
|
+
import { readFileSync as readFileSync11, existsSync as existsSync12 } from "fs";
|
|
10116
11099
|
import { basename as basename3 } from "path";
|
|
10117
11100
|
async function handler7(args, ctx) {
|
|
10118
11101
|
const { positional, flags } = parseArgs2(args, [
|
|
@@ -10136,7 +11119,7 @@ async function handler7(args, ctx) {
|
|
|
10136
11119
|
console.error(chalk16.dim(" /ingest --demo [--scenario <name>]"));
|
|
10137
11120
|
process.exit(1);
|
|
10138
11121
|
}
|
|
10139
|
-
if (!
|
|
11122
|
+
if (!existsSync12(file)) {
|
|
10140
11123
|
console.error(chalk16.red(` File not found: ${file}`));
|
|
10141
11124
|
process.exit(1);
|
|
10142
11125
|
}
|
|
@@ -10154,7 +11137,7 @@ async function handler7(args, ctx) {
|
|
|
10154
11137
|
try {
|
|
10155
11138
|
await initSchema();
|
|
10156
11139
|
spinner.text = "Parsing CSV...";
|
|
10157
|
-
const content =
|
|
11140
|
+
const content = readFileSync11(file, "utf-8");
|
|
10158
11141
|
const { rows, headers } = parseCSV(content);
|
|
10159
11142
|
if (rows.length === 0) {
|
|
10160
11143
|
spinner.fail("CSV is empty");
|
|
@@ -12317,16 +13300,16 @@ var init_compute = __esm({
|
|
|
12317
13300
|
});
|
|
12318
13301
|
|
|
12319
13302
|
// src/data/playbook.ts
|
|
12320
|
-
import { existsSync as
|
|
12321
|
-
import { join as
|
|
13303
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12, appendFileSync } from "fs";
|
|
13304
|
+
import { join as join12 } from "path";
|
|
12322
13305
|
function playsPath() {
|
|
12323
|
-
return
|
|
13306
|
+
return join12(getMemoryDir(), PLAYS_FILE);
|
|
12324
13307
|
}
|
|
12325
13308
|
function getCustomPlays() {
|
|
12326
13309
|
const path = playsPath();
|
|
12327
|
-
if (!
|
|
13310
|
+
if (!existsSync13(path)) return [];
|
|
12328
13311
|
const out = [];
|
|
12329
|
-
for (const line of
|
|
13312
|
+
for (const line of readFileSync12(path, "utf-8").split("\n")) {
|
|
12330
13313
|
const trimmed = line.trim();
|
|
12331
13314
|
if (!trimmed) continue;
|
|
12332
13315
|
try {
|
|
@@ -12918,7 +13901,7 @@ async function runMetricsAnalysis(options = {}) {
|
|
|
12918
13901
|
if (options.findings) {
|
|
12919
13902
|
if (!canUseReplAi(options.ctx)) {
|
|
12920
13903
|
throw new Error(
|
|
12921
|
-
"AI metrics findings require stored API keys. Run `ntrp`,
|
|
13904
|
+
"AI metrics findings require stored API keys. Run `ntrp`, then /connect (any provider key), then /metrics --findings."
|
|
12922
13905
|
);
|
|
12923
13906
|
}
|
|
12924
13907
|
options.onProgress?.("findings");
|
|
@@ -13327,7 +14310,8 @@ async function* streamFindings(input, ctx) {
|
|
|
13327
14310
|
findings,
|
|
13328
14311
|
model_used: meta.model_used,
|
|
13329
14312
|
provider_used: meta.provider_used,
|
|
13330
|
-
raw_prompt: userMessage
|
|
14313
|
+
raw_prompt: userMessage,
|
|
14314
|
+
usage: meta
|
|
13331
14315
|
};
|
|
13332
14316
|
}
|
|
13333
14317
|
}
|
|
@@ -13628,9 +14612,9 @@ var init_tool_schemas = __esm({
|
|
|
13628
14612
|
});
|
|
13629
14613
|
|
|
13630
14614
|
// src/ai/privacy.ts
|
|
13631
|
-
import { existsSync as
|
|
14615
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2 } from "fs";
|
|
13632
14616
|
import { homedir as homedir4 } from "os";
|
|
13633
|
-
import { join as
|
|
14617
|
+
import { join as join13 } from "path";
|
|
13634
14618
|
function stripPII(obj) {
|
|
13635
14619
|
if (obj === null || obj === void 0) return obj;
|
|
13636
14620
|
if (typeof obj !== "object") return obj;
|
|
@@ -13645,14 +14629,14 @@ function stripPII(obj) {
|
|
|
13645
14629
|
return out;
|
|
13646
14630
|
}
|
|
13647
14631
|
function ensureAuditDir() {
|
|
13648
|
-
if (!
|
|
14632
|
+
if (!existsSync14(AUDIT_DIR)) {
|
|
13649
14633
|
mkdirSync8(AUDIT_DIR, { recursive: true });
|
|
13650
14634
|
}
|
|
13651
14635
|
}
|
|
13652
14636
|
function logToolCall(entry) {
|
|
13653
14637
|
ensureAuditDir();
|
|
13654
14638
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
13655
|
-
const path =
|
|
14639
|
+
const path = join13(AUDIT_DIR, `agentic-${date}.jsonl`);
|
|
13656
14640
|
appendFileSync2(path, JSON.stringify(entry) + "\n");
|
|
13657
14641
|
}
|
|
13658
14642
|
var PII_FIELDS, AUDIT_DIR;
|
|
@@ -13676,7 +14660,7 @@ var init_privacy = __esm({
|
|
|
13676
14660
|
"raw_data",
|
|
13677
14661
|
"metadata"
|
|
13678
14662
|
]);
|
|
13679
|
-
AUDIT_DIR =
|
|
14663
|
+
AUDIT_DIR = join13(homedir4(), ".ntrp", "audit");
|
|
13680
14664
|
}
|
|
13681
14665
|
});
|
|
13682
14666
|
|
|
@@ -14182,7 +15166,7 @@ __export(ingest_chat_exports, {
|
|
|
14182
15166
|
loadDemoFromChat: () => loadDemoFromChat,
|
|
14183
15167
|
looksLikeFilePath: () => looksLikeFilePath
|
|
14184
15168
|
});
|
|
14185
|
-
import { existsSync as
|
|
15169
|
+
import { existsSync as existsSync15 } from "fs";
|
|
14186
15170
|
import { basename as basename4, resolve as resolve4 } from "path";
|
|
14187
15171
|
import { homedir as homedir5 } from "os";
|
|
14188
15172
|
import chalk22 from "chalk";
|
|
@@ -14202,11 +15186,11 @@ function extractFilePath(input) {
|
|
|
14202
15186
|
const m = trimmed.match(re);
|
|
14203
15187
|
if (m?.[1]) {
|
|
14204
15188
|
const p = expandPath(m[1]);
|
|
14205
|
-
if (
|
|
15189
|
+
if (existsSync15(p)) return p;
|
|
14206
15190
|
}
|
|
14207
15191
|
if (!m?.[1] && re.test(trimmed) && trimmed.toLowerCase().endsWith(".csv")) {
|
|
14208
15192
|
const p = expandPath(trimmed.replace(/^["']|["']$/g, ""));
|
|
14209
|
-
if (
|
|
15193
|
+
if (existsSync15(p)) return p;
|
|
14210
15194
|
}
|
|
14211
15195
|
}
|
|
14212
15196
|
return null;
|
|
@@ -14236,12 +15220,12 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
14236
15220
|
}
|
|
14237
15221
|
const { handler: ingest } = await Promise.resolve().then(() => (init_ingest(), ingest_exports));
|
|
14238
15222
|
const { detectEntityType: detectEntityType2 } = await Promise.resolve().then(() => (init_csv_detect(), csv_detect_exports));
|
|
14239
|
-
const { readFileSync:
|
|
15223
|
+
const { readFileSync: readFileSync19 } = await import("fs");
|
|
14240
15224
|
const { parseCSV: parseCSV2 } = await Promise.resolve().then(() => (init_csv_parse(), csv_parse_exports));
|
|
14241
15225
|
const { getStoredApiKey } = await Promise.resolve().then(() => (init_repl_api(), repl_api_exports));
|
|
14242
15226
|
let headerCheckFailed = false;
|
|
14243
15227
|
try {
|
|
14244
|
-
const raw =
|
|
15228
|
+
const raw = readFileSync19(filePath, "utf-8");
|
|
14245
15229
|
const { headers } = parseCSV2(raw);
|
|
14246
15230
|
const detected = detectEntityType2(headers, "unknown");
|
|
14247
15231
|
if (!detected) headerCheckFailed = true;
|
|
@@ -14600,15 +15584,18 @@ function inferHandoffTarget(input) {
|
|
|
14600
15584
|
return "plan";
|
|
14601
15585
|
}
|
|
14602
15586
|
function isShipIntent(input) {
|
|
14603
|
-
|
|
14604
|
-
|
|
14605
|
-
);
|
|
15587
|
+
const line = input.trim();
|
|
15588
|
+
if (/\?\s*$/.test(line) || QUESTION_LEAD_RE.test(line)) return false;
|
|
15589
|
+
return SHIP_INTENT_RE.test(line);
|
|
14606
15590
|
}
|
|
15591
|
+
var QUESTION_LEAD_RE, SHIP_INTENT_RE;
|
|
14607
15592
|
var init_handoff_draft = __esm({
|
|
14608
15593
|
"src/conversation/handoff-draft.ts"() {
|
|
14609
15594
|
"use strict";
|
|
14610
15595
|
init_profile();
|
|
14611
15596
|
init_session_analysis();
|
|
15597
|
+
QUESTION_LEAD_RE = /^\s*(what|why|how|when|where|who|which|is|are|was|were|do|does|did|explain|tell me|help me understand)\b/i;
|
|
15598
|
+
SHIP_INTENT_RE = /\b(ship|export|deliver|write[- ]?up|board memo|action plan|turn (this|it|that) into|(draft|create|make|build|prepare|generate|send)\s+(me\s+)?(a\s+|the\s+)?hand[- ]?off|hand[- ]?off\s+(prompt|doc|document|plan))\b/i;
|
|
14612
15599
|
}
|
|
14613
15600
|
});
|
|
14614
15601
|
|
|
@@ -14989,12 +15976,12 @@ async function handleDraftHandoff(input) {
|
|
|
14989
15976
|
};
|
|
14990
15977
|
}
|
|
14991
15978
|
async function executeToolCall(name, input, ctx) {
|
|
14992
|
-
const
|
|
14993
|
-
if (!
|
|
15979
|
+
const handler46 = HANDLERS[name];
|
|
15980
|
+
if (!handler46) {
|
|
14994
15981
|
return JSON.stringify({ error: `Unknown tool '${name}'` });
|
|
14995
15982
|
}
|
|
14996
15983
|
const start = Date.now();
|
|
14997
|
-
const rawResult = await
|
|
15984
|
+
const rawResult = await handler46(input, ctx);
|
|
14998
15985
|
const safeResult = stripPII(rawResult);
|
|
14999
15986
|
const resultJson = JSON.stringify(safeResult);
|
|
15000
15987
|
const duration = Date.now() - start;
|
|
@@ -15387,7 +16374,7 @@ async function runDiagnosis(options = {}) {
|
|
|
15387
16374
|
if (options.findings) {
|
|
15388
16375
|
if (!canUseReplAi(options.ctx)) {
|
|
15389
16376
|
throw new Error(
|
|
15390
|
-
"AI findings require stored API keys. Run `ntrp`, then /
|
|
16377
|
+
"AI findings require stored API keys. Run `ntrp`, then /connect (any provider key), and use /diagnose --findings."
|
|
15391
16378
|
);
|
|
15392
16379
|
}
|
|
15393
16380
|
if (options.deep) {
|
|
@@ -15530,7 +16517,7 @@ async function handler8(args, ctx) {
|
|
|
15530
16517
|
console.log();
|
|
15531
16518
|
console.log(" " + chalk23.red("AI findings run only in the interactive REPL."));
|
|
15532
16519
|
console.log(" " + chalk23.dim("Vital signs compute without a key \u2014 omit --findings for numbers only."));
|
|
15533
|
-
console.log(" " + chalk23.dim("Start with ") + paint("accent", "ntrp") + chalk23.dim(",
|
|
16520
|
+
console.log(" " + chalk23.dim("Start with ") + paint("accent", "ntrp") + chalk23.dim(", run ") + paint("accent", "/connect") + chalk23.dim(" (any provider key), then /diagnose --findings."));
|
|
15534
16521
|
console.log();
|
|
15535
16522
|
return;
|
|
15536
16523
|
}
|
|
@@ -15710,7 +16697,7 @@ __export(new_exports, {
|
|
|
15710
16697
|
handler: () => handler9
|
|
15711
16698
|
});
|
|
15712
16699
|
import chalk24 from "chalk";
|
|
15713
|
-
import { existsSync as
|
|
16700
|
+
import { existsSync as existsSync16 } from "fs";
|
|
15714
16701
|
import { basename as basename5 } from "path";
|
|
15715
16702
|
async function handler9(args, ctx) {
|
|
15716
16703
|
const { positional, flags } = parseArgs2(args, ["demo", "empty", "list-scenarios", "regen-taxonomy"]);
|
|
@@ -15732,7 +16719,7 @@ async function handler9(args, ctx) {
|
|
|
15732
16719
|
console.error(chalk24.red(" Usage: /new <file.csv> | --demo [--scenario <name>] | --empty [--lens health|metrics]"));
|
|
15733
16720
|
return;
|
|
15734
16721
|
}
|
|
15735
|
-
if (source.kind === "file" && !
|
|
16722
|
+
if (source.kind === "file" && !existsSync16(source.path)) {
|
|
15736
16723
|
console.error(chalk24.red(` File not found: ${source.path}`));
|
|
15737
16724
|
return;
|
|
15738
16725
|
}
|
|
@@ -15799,11 +16786,11 @@ async function handler9(args, ctx) {
|
|
|
15799
16786
|
return "New empty session";
|
|
15800
16787
|
}
|
|
15801
16788
|
if (lens === "revenue_metrics") {
|
|
15802
|
-
const
|
|
16789
|
+
const ora20 = (await import("ora")).default;
|
|
15803
16790
|
const { runMetricsAnalysis: runMetricsAnalysis2 } = await Promise.resolve().then(() => (init_metrics_analysis(), metrics_analysis_exports));
|
|
15804
16791
|
const { renderMetricsReport: renderMetricsReport2 } = await Promise.resolve().then(() => (init_metrics_report(), metrics_report_exports));
|
|
15805
16792
|
const structured = isStructuredOutput(ctx.execution);
|
|
15806
|
-
const spinner = structured ? null :
|
|
16793
|
+
const spinner = structured ? null : ora20({ text: "Computing SaaS metrics\u2026", indent: 2, discardStdin: false }).start();
|
|
15807
16794
|
let result;
|
|
15808
16795
|
try {
|
|
15809
16796
|
result = await runMetricsAnalysis2({
|
|
@@ -16009,7 +16996,7 @@ __export(session_exports, {
|
|
|
16009
16996
|
handler: () => handler11
|
|
16010
16997
|
});
|
|
16011
16998
|
import chalk26 from "chalk";
|
|
16012
|
-
import { join as
|
|
16999
|
+
import { join as join14 } from "path";
|
|
16013
17000
|
import ora7 from "ora";
|
|
16014
17001
|
async function handler11(args, ctx) {
|
|
16015
17002
|
const sub = args[0];
|
|
@@ -16098,7 +17085,7 @@ async function pickUp(idArg, ctx) {
|
|
|
16098
17085
|
}
|
|
16099
17086
|
resetContextForSwitch(ctx, {
|
|
16100
17087
|
sessionId: target.id,
|
|
16101
|
-
sessionFile:
|
|
17088
|
+
sessionFile: join14(getSessionsDir(), `${target.id}.json`),
|
|
16102
17089
|
sessionName: session.name,
|
|
16103
17090
|
messages: [...session.messages],
|
|
16104
17091
|
conversation: session.thread ? [...session.thread] : [],
|
|
@@ -16410,7 +17397,7 @@ __export(report_exports, {
|
|
|
16410
17397
|
handler: () => handler12
|
|
16411
17398
|
});
|
|
16412
17399
|
import chalk27 from "chalk";
|
|
16413
|
-
import { writeFileSync as
|
|
17400
|
+
import { writeFileSync as writeFileSync10 } from "fs";
|
|
16414
17401
|
import { dirname as dirname2 } from "path";
|
|
16415
17402
|
async function handler12(args, ctx) {
|
|
16416
17403
|
const { flags } = parseArgs2(args);
|
|
@@ -16506,7 +17493,7 @@ async function handler12(args, ctx) {
|
|
|
16506
17493
|
if (!isInsideNtrp(resolvedOutput)) {
|
|
16507
17494
|
console.warn(chalk27.yellow(` Warning: writing report outside ~/.ntrp (${dirname2(resolvedOutput)})`));
|
|
16508
17495
|
}
|
|
16509
|
-
|
|
17496
|
+
writeFileSync10(resolvedOutput, rendered);
|
|
16510
17497
|
console.log(chalk27.green(` Report written to ${resolvedOutput}`));
|
|
16511
17498
|
} else if (rendered) {
|
|
16512
17499
|
console.log(rendered);
|
|
@@ -16537,8 +17524,8 @@ var init_report2 = __esm({
|
|
|
16537
17524
|
});
|
|
16538
17525
|
|
|
16539
17526
|
// src/output/notes-export.ts
|
|
16540
|
-
import { writeFileSync as
|
|
16541
|
-
import { join as
|
|
17527
|
+
import { writeFileSync as writeFileSync11 } from "fs";
|
|
17528
|
+
import { join as join15 } from "path";
|
|
16542
17529
|
function exportToNotes(data) {
|
|
16543
17530
|
const { computeResult, divergences, findings, exchanges } = data;
|
|
16544
17531
|
const { aggregate, segments } = computeResult;
|
|
@@ -16547,7 +17534,7 @@ function exportToNotes(data) {
|
|
|
16547
17534
|
const timeStr = formatTime(now2);
|
|
16548
17535
|
const filename = `${dateStr}-${timeStr}-gtm-health.md`;
|
|
16549
17536
|
const dir = getExportsDir();
|
|
16550
|
-
const filepath =
|
|
17537
|
+
const filepath = join15(dir, filename);
|
|
16551
17538
|
const severityTags = /* @__PURE__ */ new Set();
|
|
16552
17539
|
for (const f of findings) severityTags.add(f.severity);
|
|
16553
17540
|
const tags = ["ntrp", "gtm-health", ...severityTags];
|
|
@@ -16636,7 +17623,7 @@ function exportToNotes(data) {
|
|
|
16636
17623
|
}
|
|
16637
17624
|
}
|
|
16638
17625
|
const content = frontmatter.join("\n") + "\n\n" + body.join("\n") + "\n";
|
|
16639
|
-
|
|
17626
|
+
writeFileSync11(filepath, content);
|
|
16640
17627
|
return filepath;
|
|
16641
17628
|
}
|
|
16642
17629
|
function formatDate(d) {
|
|
@@ -16776,8 +17763,8 @@ __export(backmeup_exports, {
|
|
|
16776
17763
|
});
|
|
16777
17764
|
import chalk29 from "chalk";
|
|
16778
17765
|
import Papa5 from "papaparse";
|
|
16779
|
-
import { mkdirSync as mkdirSync9, writeFileSync as
|
|
16780
|
-
import { join as
|
|
17766
|
+
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync12 } from "fs";
|
|
17767
|
+
import { join as join16 } from "path";
|
|
16781
17768
|
function sanitizeCsvValue(value) {
|
|
16782
17769
|
if (typeof value !== "string") return value;
|
|
16783
17770
|
return CSV_FORMULA_RE.test(value) ? `'${value}` : value;
|
|
@@ -16813,7 +17800,7 @@ async function handler14(args, _ctx) {
|
|
|
16813
17800
|
if (!isInsideNtrp(baseDir)) {
|
|
16814
17801
|
console.warn(chalk29.yellow(` Warning: writing backup outside ~/.ntrp (${baseDir})`));
|
|
16815
17802
|
}
|
|
16816
|
-
const folder =
|
|
17803
|
+
const folder = join16(baseDir, folderName);
|
|
16817
17804
|
mkdirSync9(folder, { recursive: true });
|
|
16818
17805
|
const generatedAt = now2.toISOString();
|
|
16819
17806
|
let fileCount = 0;
|
|
@@ -16828,7 +17815,7 @@ async function handler14(args, _ctx) {
|
|
|
16828
17815
|
"Total At Risk": health.total_value_at_risk != null ? formatCurrency(health.total_value_at_risk) : "N/A",
|
|
16829
17816
|
"Generated At": generatedAt
|
|
16830
17817
|
}));
|
|
16831
|
-
|
|
17818
|
+
writeFileSync12(join16(folder, "cover-sheet.csv"), Papa5.unparse(sanitizeCsvRows(coverRows)), "utf-8");
|
|
16832
17819
|
fileCount++;
|
|
16833
17820
|
if (findings.length > 0) {
|
|
16834
17821
|
const findingsRows = findings.map((f) => ({
|
|
@@ -16838,7 +17825,7 @@ async function handler14(args, _ctx) {
|
|
|
16838
17825
|
Finding: f.finding,
|
|
16839
17826
|
"Recommended Plays": f.recommended_plays ? f.recommended_plays.map((p) => p.play_name).join("; ") : ""
|
|
16840
17827
|
}));
|
|
16841
|
-
|
|
17828
|
+
writeFileSync12(join16(folder, "findings.csv"), Papa5.unparse(sanitizeCsvRows(findingsRows)), "utf-8");
|
|
16842
17829
|
fileCount++;
|
|
16843
17830
|
}
|
|
16844
17831
|
for (const vs of health.vital_signs) {
|
|
@@ -16848,7 +17835,7 @@ async function handler14(args, _ctx) {
|
|
|
16848
17835
|
...detail
|
|
16849
17836
|
}));
|
|
16850
17837
|
const filename = EVIDENCE_FILENAMES[vs.vital_sign] ?? `${vs.vital_sign}.csv`;
|
|
16851
|
-
|
|
17838
|
+
writeFileSync12(join16(folder, filename), Papa5.unparse(sanitizeCsvRows(rows)), "utf-8");
|
|
16852
17839
|
fileCount++;
|
|
16853
17840
|
}
|
|
16854
17841
|
console.log(chalk29.green(`
|
|
@@ -17042,8 +18029,8 @@ var init_bundle = __esm({
|
|
|
17042
18029
|
});
|
|
17043
18030
|
|
|
17044
18031
|
// src/repositories/markdown.ts
|
|
17045
|
-
import { mkdirSync as mkdirSync10, writeFileSync as
|
|
17046
|
-
import { basename as basename6, dirname as dirname3, join as
|
|
18032
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync13 } from "fs";
|
|
18033
|
+
import { basename as basename6, dirname as dirname3, join as join17, resolve as resolve6 } from "path";
|
|
17047
18034
|
import { stringify as stringifyYaml } from "yaml";
|
|
17048
18035
|
function renderMarkdownFiles(pkg) {
|
|
17049
18036
|
const bundleJson = JSON.stringify(pkg, null, 2) + "\n";
|
|
@@ -17266,9 +18253,9 @@ var init_markdown3 = __esm({
|
|
|
17266
18253
|
mkdirSync10(root, { recursive: true });
|
|
17267
18254
|
const written = [];
|
|
17268
18255
|
for (const file of files) {
|
|
17269
|
-
const absolutePath =
|
|
18256
|
+
const absolutePath = join17(root, file.relativePath);
|
|
17270
18257
|
mkdirSync10(dirname3(absolutePath), { recursive: true });
|
|
17271
|
-
|
|
18258
|
+
writeFileSync13(absolutePath, file.contents, "utf-8");
|
|
17272
18259
|
written.push(absolutePath);
|
|
17273
18260
|
}
|
|
17274
18261
|
return {
|
|
@@ -17572,8 +18559,8 @@ __export(handoff_exports, {
|
|
|
17572
18559
|
handler: () => handler16
|
|
17573
18560
|
});
|
|
17574
18561
|
import chalk31 from "chalk";
|
|
17575
|
-
import { writeFileSync as
|
|
17576
|
-
import { join as
|
|
18562
|
+
import { writeFileSync as writeFileSync14 } from "fs";
|
|
18563
|
+
import { join as join18 } from "path";
|
|
17577
18564
|
async function handler16(args, ctx) {
|
|
17578
18565
|
const sub = args[0];
|
|
17579
18566
|
if (!sub) {
|
|
@@ -17631,7 +18618,7 @@ async function interactiveMenu(ctx) {
|
|
|
17631
18618
|
}
|
|
17632
18619
|
}
|
|
17633
18620
|
async function runReport(args, ctx) {
|
|
17634
|
-
const out =
|
|
18621
|
+
const out = join18(getExportsDir(), `report-${stamp()}.md`);
|
|
17635
18622
|
const { handler: report } = await Promise.resolve().then(() => (init_report2(), report_exports));
|
|
17636
18623
|
await report(["--format", "md", "--output", out, ...args], ctx);
|
|
17637
18624
|
recordDeliverable(ctx, { kind: "report", at: (/* @__PURE__ */ new Date()).toISOString(), path: out });
|
|
@@ -17679,8 +18666,8 @@ async function runPrompt(target, ctx) {
|
|
|
17679
18666
|
return;
|
|
17680
18667
|
}
|
|
17681
18668
|
const prompt = draft.markdown;
|
|
17682
|
-
const out =
|
|
17683
|
-
|
|
18669
|
+
const out = join18(getExportsDir(), `handoff-${target}-${stamp()}.md`);
|
|
18670
|
+
writeFileSync14(out, prompt, "utf-8");
|
|
17684
18671
|
recordDeliverable(ctx, { kind: `prompt:${target}`, at: (/* @__PURE__ */ new Date()).toISOString(), path: out });
|
|
17685
18672
|
console.log();
|
|
17686
18673
|
console.log(" " + paint("accent", `Agent prompt ready (${target})`));
|
|
@@ -18552,24 +19539,24 @@ JSON SHAPE:
|
|
|
18552
19539
|
|
|
18553
19540
|
// src/strategies/readers.ts
|
|
18554
19541
|
import { createHash } from "crypto";
|
|
18555
|
-
import { existsSync as
|
|
19542
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
18556
19543
|
import { extname, resolve as resolve7 } from "path";
|
|
18557
19544
|
import { parse as parseYaml } from "yaml";
|
|
18558
19545
|
import { PDFParse } from "pdf-parse";
|
|
18559
19546
|
async function readStrategyFile(pathOrDash) {
|
|
18560
19547
|
if (pathOrDash === "-") {
|
|
18561
|
-
const text2 =
|
|
19548
|
+
const text2 = readFileSync13(0, "utf-8");
|
|
18562
19549
|
return createDocument("stdin", null, text2, {});
|
|
18563
19550
|
}
|
|
18564
19551
|
const sourcePath = resolve7(pathOrDash);
|
|
18565
|
-
if (!
|
|
19552
|
+
if (!existsSync17(sourcePath)) {
|
|
18566
19553
|
throw new NtrpError("strategy_file_not_found", `Strategy file not found: ${pathOrDash}`, 2 /* Usage */);
|
|
18567
19554
|
}
|
|
18568
19555
|
const ext = extname(sourcePath).toLowerCase();
|
|
18569
19556
|
if (ext === ".pdf") {
|
|
18570
19557
|
return readPdf(sourcePath);
|
|
18571
19558
|
}
|
|
18572
|
-
const text =
|
|
19559
|
+
const text = readFileSync13(sourcePath, "utf-8");
|
|
18573
19560
|
if (ext === ".yaml" || ext === ".yml") {
|
|
18574
19561
|
const structured = parseStructuredYaml(text);
|
|
18575
19562
|
return createDocument("yaml", sourcePath, text, structured);
|
|
@@ -18584,7 +19571,7 @@ function readStrategyText(text) {
|
|
|
18584
19571
|
return createDocument("text", null, text, {});
|
|
18585
19572
|
}
|
|
18586
19573
|
async function readPdf(sourcePath) {
|
|
18587
|
-
const data =
|
|
19574
|
+
const data = readFileSync13(sourcePath);
|
|
18588
19575
|
const parser = new PDFParse({ data });
|
|
18589
19576
|
try {
|
|
18590
19577
|
const result = await parser.getText();
|
|
@@ -18631,15 +19618,15 @@ var init_readers = __esm({
|
|
|
18631
19618
|
});
|
|
18632
19619
|
|
|
18633
19620
|
// src/strategies/library.ts
|
|
18634
|
-
import { writeFileSync as
|
|
18635
|
-
import { join as
|
|
19621
|
+
import { writeFileSync as writeFileSync15 } from "fs";
|
|
19622
|
+
import { join as join19 } from "path";
|
|
18636
19623
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
18637
19624
|
function strategyLibraryPath(slug) {
|
|
18638
|
-
return
|
|
19625
|
+
return join19(getStrategiesDir(), `${slug}.md`);
|
|
18639
19626
|
}
|
|
18640
19627
|
function writeStrategyMarkdown(strategy) {
|
|
18641
19628
|
const path = strategyLibraryPath(strategy.slug);
|
|
18642
|
-
|
|
19629
|
+
writeFileSync15(path, renderStrategyMarkdown(strategy), "utf-8");
|
|
18643
19630
|
return path;
|
|
18644
19631
|
}
|
|
18645
19632
|
function renderStrategyMarkdown(strategy) {
|
|
@@ -18713,7 +19700,7 @@ var init_library = __esm({
|
|
|
18713
19700
|
// src/strategies/connectors.ts
|
|
18714
19701
|
import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
18715
19702
|
import { homedir as homedir7 } from "os";
|
|
18716
|
-
import { basename as basename7, extname as extname2, join as
|
|
19703
|
+
import { basename as basename7, extname as extname2, join as join20, relative, resolve as resolve8, sep as sep3 } from "path";
|
|
18717
19704
|
function createLocalFolderConnector(options) {
|
|
18718
19705
|
const rootPath = resolveUserPath2(options.rootPath);
|
|
18719
19706
|
const name = options.name ?? (basename7(rootPath) || "local");
|
|
@@ -18756,7 +19743,7 @@ function createLocalFolderConnector(options) {
|
|
|
18756
19743
|
}
|
|
18757
19744
|
function walkLocalFolder(rootPath, currentPath, refs, opts) {
|
|
18758
19745
|
for (const entry of readdirSync2(currentPath, { withFileTypes: true })) {
|
|
18759
|
-
const absolutePath =
|
|
19746
|
+
const absolutePath = join20(currentPath, entry.name);
|
|
18760
19747
|
const relativePath = normalizePath(relative(rootPath, absolutePath));
|
|
18761
19748
|
if (entry.isDirectory()) {
|
|
18762
19749
|
if (shouldSkipDirectory(entry.name) || matchesAny(relativePath, opts.excludePatterns)) continue;
|
|
@@ -18820,7 +19807,7 @@ function normalizePath(path) {
|
|
|
18820
19807
|
}
|
|
18821
19808
|
function resolveUserPath2(path) {
|
|
18822
19809
|
if (path === "~") return homedir7();
|
|
18823
|
-
if (path.startsWith("~/")) return
|
|
19810
|
+
if (path.startsWith("~/")) return join20(homedir7(), path.slice(2));
|
|
18824
19811
|
return resolve8(path);
|
|
18825
19812
|
}
|
|
18826
19813
|
var DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, SUPPORTED_EXTENSIONS, DEFAULT_EXCLUDED_DIRS;
|
|
@@ -19674,18 +20661,25 @@ __export(config_exports, {
|
|
|
19674
20661
|
handler: () => handler23
|
|
19675
20662
|
});
|
|
19676
20663
|
import chalk38 from "chalk";
|
|
20664
|
+
import ora10 from "ora";
|
|
20665
|
+
function secretKeys() {
|
|
20666
|
+
const keys = /* @__PURE__ */ new Set(["license-key", "license-instance-id", "voyage-api-key", "tavily-api-key", "brave-api-key"]);
|
|
20667
|
+
for (const spec of listProviderSpecs()) keys.add(spec.key_config_name);
|
|
20668
|
+
return keys;
|
|
20669
|
+
}
|
|
19677
20670
|
function display(key, value) {
|
|
19678
|
-
return
|
|
20671
|
+
return secretKeys().has(key) ? String(value).slice(0, 10) + "..." : String(value);
|
|
19679
20672
|
}
|
|
19680
20673
|
function secretPromptLabel(key) {
|
|
19681
|
-
|
|
19682
|
-
if (
|
|
20674
|
+
const spec = findSpecByConfigKey(key);
|
|
20675
|
+
if (spec) return `${spec.label} API key`;
|
|
19683
20676
|
if (key === "license-key") return "License key";
|
|
19684
20677
|
return key;
|
|
19685
20678
|
}
|
|
19686
20679
|
function usage() {
|
|
19687
20680
|
console.log(chalk38.dim(" Usage: /config <get|set|list|delete> [key] [value]"));
|
|
19688
20681
|
console.log(chalk38.dim(" Tip: ") + paint("accent", "/config set api-key") + chalk38.dim(" opens a hidden prompt (no inline paste)."));
|
|
20682
|
+
console.log(chalk38.dim(" Tip: ") + paint("accent", "/connect") + chalk38.dim(" auto-detects the provider from any pasted key."));
|
|
19689
20683
|
}
|
|
19690
20684
|
function fail(message, ctx) {
|
|
19691
20685
|
console.error(chalk38.red(` ${message}`));
|
|
@@ -19717,7 +20711,7 @@ async function handler23(args, ctx) {
|
|
|
19717
20711
|
return;
|
|
19718
20712
|
}
|
|
19719
20713
|
let value = inlineValue;
|
|
19720
|
-
if (!value &&
|
|
20714
|
+
if (!value && secretKeys().has(key)) {
|
|
19721
20715
|
try {
|
|
19722
20716
|
value = await promptSecretValue(key, ctx);
|
|
19723
20717
|
} catch (err) {
|
|
@@ -19737,8 +20731,24 @@ async function handler23(args, ctx) {
|
|
|
19737
20731
|
}
|
|
19738
20732
|
console.log();
|
|
19739
20733
|
console.log(chalk38.green(` \u2713 ${key} saved`) + chalk38.dim(` (${display(key, value)})`));
|
|
19740
|
-
|
|
19741
|
-
|
|
20734
|
+
const spec = findSpecByConfigKey(key);
|
|
20735
|
+
if (spec) {
|
|
20736
|
+
const spinner = ora10({ text: `Discovering ${spec.label} models\u2026`, discardStdin: false }).start();
|
|
20737
|
+
try {
|
|
20738
|
+
const { refreshProviderModels: refreshProviderModels2 } = await Promise.resolve().then(() => (init_discovery(), discovery_exports));
|
|
20739
|
+
const entry = await refreshProviderModels2(spec.id, { apiKey: value, force: true });
|
|
20740
|
+
if (entry) {
|
|
20741
|
+
spinner.succeed(`${spec.label}: ${entry.models.length} chat models available.`);
|
|
20742
|
+
console.log(
|
|
20743
|
+
" " + chalk38.dim(`high ${entry.tier_stack.high} \xB7 medium ${entry.tier_stack.medium} \xB7 low ${entry.tier_stack.low}`)
|
|
20744
|
+
);
|
|
20745
|
+
} else {
|
|
20746
|
+
spinner.warn(`${spec.label} unreachable \u2014 models will be discovered on first use.`);
|
|
20747
|
+
}
|
|
20748
|
+
} catch {
|
|
20749
|
+
spinner.warn(`${spec.label} unreachable \u2014 models will be discovered on first use.`);
|
|
20750
|
+
}
|
|
20751
|
+
console.log(" " + chalk38.dim("Switch engines with /provider \xB7 browse models with /model list."));
|
|
19742
20752
|
}
|
|
19743
20753
|
console.log();
|
|
19744
20754
|
return;
|
|
@@ -19781,15 +20791,14 @@ async function handler23(args, ctx) {
|
|
|
19781
20791
|
}
|
|
19782
20792
|
}
|
|
19783
20793
|
}
|
|
19784
|
-
var SECRET_KEYS;
|
|
19785
20794
|
var init_config = __esm({
|
|
19786
20795
|
"src/commands/config.ts"() {
|
|
19787
20796
|
"use strict";
|
|
19788
20797
|
init_store();
|
|
20798
|
+
init_providers();
|
|
19789
20799
|
init_argparse();
|
|
19790
20800
|
init_prompts();
|
|
19791
20801
|
init_theme();
|
|
19792
|
-
SECRET_KEYS = /* @__PURE__ */ new Set(["api-key", "openai-api-key", "license-key", "license-instance-id"]);
|
|
19793
20802
|
}
|
|
19794
20803
|
});
|
|
19795
20804
|
|
|
@@ -20571,15 +21580,15 @@ var init_checkout = __esm({
|
|
|
20571
21580
|
});
|
|
20572
21581
|
|
|
20573
21582
|
// src/services/setup.ts
|
|
20574
|
-
import { existsSync as
|
|
20575
|
-
import { join as
|
|
21583
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync16 } from "fs";
|
|
21584
|
+
import { join as join21 } from "path";
|
|
20576
21585
|
function setupCheck() {
|
|
20577
21586
|
const home = ntrpHome();
|
|
20578
21587
|
let writable = false;
|
|
20579
21588
|
try {
|
|
20580
21589
|
mkdirSync11(home, { recursive: true });
|
|
20581
|
-
const probe =
|
|
20582
|
-
|
|
21590
|
+
const probe = join21(home, ".write-check");
|
|
21591
|
+
writeFileSync16(probe, "ok\n");
|
|
20583
21592
|
writable = true;
|
|
20584
21593
|
} catch {
|
|
20585
21594
|
writable = false;
|
|
@@ -20604,7 +21613,8 @@ function setupCheck() {
|
|
|
20604
21613
|
tier: llmCfg.tier,
|
|
20605
21614
|
auto_failover: llmCfg.autoFailover,
|
|
20606
21615
|
anthropic: llmReady.anthropic,
|
|
20607
|
-
openai: llmReady.openai
|
|
21616
|
+
openai: llmReady.openai,
|
|
21617
|
+
providers: llmReady.providers
|
|
20608
21618
|
}
|
|
20609
21619
|
},
|
|
20610
21620
|
license: {
|
|
@@ -20616,7 +21626,7 @@ function setupCheck() {
|
|
|
20616
21626
|
};
|
|
20617
21627
|
}
|
|
20618
21628
|
function readProfileInput(pathOrDash) {
|
|
20619
|
-
const raw = pathOrDash === "-" ?
|
|
21629
|
+
const raw = pathOrDash === "-" ? readFileSync14(0, "utf-8") : readFileSync14(pathOrDash, "utf-8");
|
|
20620
21630
|
return JSON.parse(raw);
|
|
20621
21631
|
}
|
|
20622
21632
|
function writeAgentProfile(input) {
|
|
@@ -20641,11 +21651,15 @@ function writeAgentProfile(input) {
|
|
|
20641
21651
|
saveProfile(profile);
|
|
20642
21652
|
return profile;
|
|
20643
21653
|
}
|
|
20644
|
-
function applyAgentConfig(opts) {
|
|
21654
|
+
async function applyAgentConfig(opts) {
|
|
20645
21655
|
if (opts.defaultFormat) setConfigValue("default-format", opts.defaultFormat);
|
|
20646
21656
|
if (opts.apiKey) setConfigValue("api-key", opts.apiKey);
|
|
20647
21657
|
if (opts.openaiApiKey) setConfigValue("openai-api-key", opts.openaiApiKey);
|
|
20648
|
-
if (opts.
|
|
21658
|
+
if (opts.llmKey) {
|
|
21659
|
+
const { connectWithKey: connectWithKey2 } = await Promise.resolve().then(() => (init_connect(), connect_exports));
|
|
21660
|
+
await connectWithKey2(opts.llmKey, { providerId: opts.llmProvider });
|
|
21661
|
+
}
|
|
21662
|
+
if (opts.llmPrimary && getProviderSpec(opts.llmPrimary)) {
|
|
20649
21663
|
setConfigValue("llm-primary", opts.llmPrimary);
|
|
20650
21664
|
}
|
|
20651
21665
|
if (opts.licenseKey) setConfigValue("license-key", opts.licenseKey);
|
|
@@ -20655,6 +21669,7 @@ var init_setup = __esm({
|
|
|
20655
21669
|
"src/services/setup.ts"() {
|
|
20656
21670
|
"use strict";
|
|
20657
21671
|
init_repl_api();
|
|
21672
|
+
init_providers();
|
|
20658
21673
|
init_llm_config();
|
|
20659
21674
|
init_store();
|
|
20660
21675
|
init_profile();
|
|
@@ -20697,13 +21712,12 @@ async function handler27(args, ctx) {
|
|
|
20697
21712
|
console.log(` Writable: ${result.writable ? "yes" : "no"}`);
|
|
20698
21713
|
console.log(` Profile: ${result.profile.exists ? "ready" : "missing"} (${result.profile.path})`);
|
|
20699
21714
|
const llm = result.config.llm;
|
|
20700
|
-
if (llm) {
|
|
20701
|
-
|
|
20702
|
-
console.log(`
|
|
20703
|
-
console.log(` Anthropic: ${llm.anthropic ? "set" : "missing"} \xB7 OpenAI: ${llm.openai ? "set" : "missing"}`);
|
|
21715
|
+
if (llm && llm.providers.length > 0) {
|
|
21716
|
+
console.log(` Engines: ${llm.providers.length} \xB7 default ${llm.primary} \xB7 tier ${llm.tier}`);
|
|
21717
|
+
console.log(` Connected: ${llm.providers.join(", ")}`);
|
|
20704
21718
|
console.log(` Auto-failover: ${llm.auto_failover ? "on" : "off"}`);
|
|
20705
21719
|
} else {
|
|
20706
|
-
console.log(" Engines: missing");
|
|
21720
|
+
console.log(" Engines: missing \u2014 run /connect with any provider key");
|
|
20707
21721
|
}
|
|
20708
21722
|
console.log(` License: ${formatLicenseSetupLine(result.license)}`);
|
|
20709
21723
|
console.log();
|
|
@@ -20724,10 +21738,12 @@ async function handler27(args, ctx) {
|
|
|
20724
21738
|
sales_motion: getString(flags, "sales-motion")
|
|
20725
21739
|
};
|
|
20726
21740
|
}
|
|
20727
|
-
applyAgentConfig({
|
|
21741
|
+
await applyAgentConfig({
|
|
20728
21742
|
defaultFormat: getString(flags, "default-format"),
|
|
20729
21743
|
apiKey: getString(flags, "api-key"),
|
|
20730
21744
|
openaiApiKey: getString(flags, "openai-api-key"),
|
|
21745
|
+
llmKey: getString(flags, "llm-key"),
|
|
21746
|
+
llmProvider: getString(flags, "llm-provider"),
|
|
20731
21747
|
llmPrimary: getString(flags, "llm-primary"),
|
|
20732
21748
|
licenseKey: getString(flags, "license-key"),
|
|
20733
21749
|
exportDir: getString(flags, "export-dir")
|
|
@@ -20767,8 +21783,8 @@ var init_setup2 = __esm({
|
|
|
20767
21783
|
|
|
20768
21784
|
// src/conversation/orchestrator.ts
|
|
20769
21785
|
import chalk43 from "chalk";
|
|
20770
|
-
import { writeFileSync as
|
|
20771
|
-
import { join as
|
|
21786
|
+
import { writeFileSync as writeFileSync17 } from "fs";
|
|
21787
|
+
import { join as join22 } from "path";
|
|
20772
21788
|
function printScopeProposal(ctx) {
|
|
20773
21789
|
if (!ctx.scope) return;
|
|
20774
21790
|
const lens = ctx.scope.primary_lens === "revenue_metrics" ? "SaaS metrics" : "pipeline health";
|
|
@@ -20926,8 +21942,8 @@ async function handleDeliverFlow(input, ctx) {
|
|
|
20926
21942
|
prompts.close();
|
|
20927
21943
|
}
|
|
20928
21944
|
const stamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/T/, "-").replace(/:/g, "").slice(0, 15);
|
|
20929
|
-
const out =
|
|
20930
|
-
|
|
21945
|
+
const out = join22(getExportsDir(), `handoff-${target}-${stamp2}.md`);
|
|
21946
|
+
writeFileSync17(out, draft.markdown, "utf-8");
|
|
20931
21947
|
const d = {
|
|
20932
21948
|
kind: `prompt:${target}`,
|
|
20933
21949
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -20951,7 +21967,7 @@ async function handleExploreWithoutKey(ctx) {
|
|
|
20951
21967
|
console.log();
|
|
20952
21968
|
console.log(" " + chalk43.red("AI interpretation needs an LLM API key saved in config."));
|
|
20953
21969
|
console.log(
|
|
20954
|
-
" " + chalk43.dim("
|
|
21970
|
+
" " + chalk43.dim("Run ") + paint("accent", "/connect") + chalk43.dim(" and paste any provider's key (Anthropic, OpenAI, Groq, Gemini, ...).")
|
|
20955
21971
|
);
|
|
20956
21972
|
console.log(" " + chalk43.dim("Number crunching works without a key \u2014 only Q&A in the REPL needs one."));
|
|
20957
21973
|
if (ctx.gapAudit) {
|
|
@@ -21126,8 +22142,8 @@ async function callProvider(texts) {
|
|
|
21126
22142
|
async function embedText(text) {
|
|
21127
22143
|
const key = text.trim();
|
|
21128
22144
|
if (!key) return null;
|
|
21129
|
-
const
|
|
21130
|
-
if (
|
|
22145
|
+
const cached2 = cache.get(key);
|
|
22146
|
+
if (cached2) return cached2;
|
|
21131
22147
|
const result = await callProvider([key]);
|
|
21132
22148
|
const vec = result?.[0] ?? null;
|
|
21133
22149
|
if (vec) cache.set(key, vec);
|
|
@@ -21137,8 +22153,8 @@ async function embedItems(items) {
|
|
|
21137
22153
|
const needing = [];
|
|
21138
22154
|
const out = items.map((it, index) => {
|
|
21139
22155
|
if (it.embedding && it.embedding.length > 0) return { ...it };
|
|
21140
|
-
const
|
|
21141
|
-
if (
|
|
22156
|
+
const cached2 = cache.get(it.text.trim());
|
|
22157
|
+
if (cached2) return { ...it, embedding: cached2 };
|
|
21142
22158
|
needing.push({ index, text: it.text });
|
|
21143
22159
|
return { ...it };
|
|
21144
22160
|
});
|
|
@@ -21297,17 +22313,17 @@ var init_retrieval = __esm({
|
|
|
21297
22313
|
});
|
|
21298
22314
|
|
|
21299
22315
|
// src/memory/knowledge.ts
|
|
21300
|
-
import { existsSync as
|
|
21301
|
-
import { join as
|
|
22316
|
+
import { existsSync as existsSync19, readFileSync as readFileSync15, appendFileSync as appendFileSync3, readdirSync as readdirSync3 } from "fs";
|
|
22317
|
+
import { join as join23 } from "path";
|
|
21302
22318
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
21303
22319
|
function knowledgePath() {
|
|
21304
|
-
return
|
|
22320
|
+
return join23(getMemoryDir(), KNOWLEDGE_FILE);
|
|
21305
22321
|
}
|
|
21306
22322
|
function loadKnowledgeChunks() {
|
|
21307
22323
|
const path = knowledgePath();
|
|
21308
|
-
if (!
|
|
22324
|
+
if (!existsSync19(path)) return [];
|
|
21309
22325
|
const out = [];
|
|
21310
|
-
for (const line of
|
|
22326
|
+
for (const line of readFileSync15(path, "utf-8").split("\n")) {
|
|
21311
22327
|
const trimmed = line.trim();
|
|
21312
22328
|
if (!trimmed) continue;
|
|
21313
22329
|
try {
|
|
@@ -21408,17 +22424,17 @@ __export(store_exports2, {
|
|
|
21408
22424
|
rewriteJsonl: () => rewriteJsonl,
|
|
21409
22425
|
scrubText: () => scrubText
|
|
21410
22426
|
});
|
|
21411
|
-
import { existsSync as
|
|
21412
|
-
import { join as
|
|
22427
|
+
import { existsSync as existsSync20, readFileSync as readFileSync16, appendFileSync as appendFileSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync18 } from "fs";
|
|
22428
|
+
import { join as join24 } from "path";
|
|
21413
22429
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
21414
22430
|
function memPath(file) {
|
|
21415
|
-
return
|
|
22431
|
+
return join24(getMemoryDir(), file);
|
|
21416
22432
|
}
|
|
21417
22433
|
function readJsonl(file) {
|
|
21418
22434
|
const path = memPath(file);
|
|
21419
|
-
if (!
|
|
22435
|
+
if (!existsSync20(path)) return [];
|
|
21420
22436
|
const out = [];
|
|
21421
|
-
for (const line of
|
|
22437
|
+
for (const line of readFileSync16(path, "utf-8").split("\n")) {
|
|
21422
22438
|
const trimmed = line.trim();
|
|
21423
22439
|
if (!trimmed) continue;
|
|
21424
22440
|
try {
|
|
@@ -21436,7 +22452,7 @@ function appendJsonl(file, obj) {
|
|
|
21436
22452
|
}
|
|
21437
22453
|
function rewriteJsonl(file, rows) {
|
|
21438
22454
|
try {
|
|
21439
|
-
|
|
22455
|
+
writeFileSync18(memPath(file), rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length ? "\n" : ""));
|
|
21440
22456
|
} catch {
|
|
21441
22457
|
}
|
|
21442
22458
|
}
|
|
@@ -21498,7 +22514,7 @@ function loadWinSnippets() {
|
|
|
21498
22514
|
const out = [];
|
|
21499
22515
|
for (const name of readdirSync4(dir)) {
|
|
21500
22516
|
if (!name.endsWith(".md") || name.toLowerCase() === "readme.md") continue;
|
|
21501
|
-
const raw =
|
|
22517
|
+
const raw = readFileSync16(join24(dir, name), "utf-8");
|
|
21502
22518
|
const title = raw.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? name.replace(/\.md$/, "");
|
|
21503
22519
|
const body = raw.replace(/^#.*$/m, "").replace(/\s+/g, " ").trim().slice(0, 300);
|
|
21504
22520
|
out.push({ id: `win:${name}`, title, text: `${title}. ${body}` });
|
|
@@ -21587,7 +22603,7 @@ var init_store2 = __esm({
|
|
|
21587
22603
|
});
|
|
21588
22604
|
|
|
21589
22605
|
// src/services/smoke-protocol.ts
|
|
21590
|
-
import { join as
|
|
22606
|
+
import { join as join25 } from "path";
|
|
21591
22607
|
function isSmokeProtocolTrigger(input) {
|
|
21592
22608
|
return normalize(input).includes(SMOKE_TRIGGER_PHRASE);
|
|
21593
22609
|
}
|
|
@@ -21623,7 +22639,7 @@ async function runSmokeProtocol(_input, ctx) {
|
|
|
21623
22639
|
});
|
|
21624
22640
|
const proposalResult = await proposeRepositoryExport({
|
|
21625
22641
|
target: "markdown",
|
|
21626
|
-
directory:
|
|
22642
|
+
directory: join25(getExportsDir(), "repository-smoke"),
|
|
21627
22643
|
source: "smoke_protocol",
|
|
21628
22644
|
modelOrFixture: "smoke-protocol-v1"
|
|
21629
22645
|
});
|
|
@@ -21720,13 +22736,13 @@ var nl_exports = {};
|
|
|
21720
22736
|
__export(nl_exports, {
|
|
21721
22737
|
runNaturalLanguage: () => runNaturalLanguage
|
|
21722
22738
|
});
|
|
21723
|
-
import
|
|
22739
|
+
import ora11 from "ora";
|
|
21724
22740
|
import chalk44 from "chalk";
|
|
21725
22741
|
async function runNaturalLanguage(input, ctx) {
|
|
21726
22742
|
if (isSmokeProtocolTrigger(input)) {
|
|
21727
22743
|
recordMessage(ctx, "user", input);
|
|
21728
22744
|
console.log();
|
|
21729
|
-
const spinner2 =
|
|
22745
|
+
const spinner2 = ora11({ text: "Running smoke protocol\u2026", color: "cyan", discardStdin: false }).start();
|
|
21730
22746
|
try {
|
|
21731
22747
|
const result = await runSmokeProtocol(input, ctx);
|
|
21732
22748
|
spinner2.succeed("Smoke protocol complete");
|
|
@@ -21753,7 +22769,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
21753
22769
|
let snapshot = ctx.snapshot.computeResult;
|
|
21754
22770
|
if (!snapshot) {
|
|
21755
22771
|
const metricsFirst = prefersMetricsFirstContext(ctx);
|
|
21756
|
-
const spinner2 =
|
|
22772
|
+
const spinner2 = ora11({
|
|
21757
22773
|
text: metricsFirst ? "Loading session context\u2026" : "Computing vital signs\u2026",
|
|
21758
22774
|
color: "cyan",
|
|
21759
22775
|
discardStdin: false
|
|
@@ -21778,7 +22794,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
21778
22794
|
}
|
|
21779
22795
|
console.log();
|
|
21780
22796
|
const memoryBlock = await buildMemoryBlock(input).catch(() => "");
|
|
21781
|
-
const spinner =
|
|
22797
|
+
const spinner = ora11({ text: "Thinking\u2026", color: "cyan", discardStdin: false }).start();
|
|
21782
22798
|
let lastAnswer = "";
|
|
21783
22799
|
let rawHistory = [];
|
|
21784
22800
|
const toolsUsed = [];
|
|
@@ -22069,7 +23085,7 @@ __export(metrics_exports, {
|
|
|
22069
23085
|
handler: () => handler29
|
|
22070
23086
|
});
|
|
22071
23087
|
import chalk46 from "chalk";
|
|
22072
|
-
import
|
|
23088
|
+
import ora12 from "ora";
|
|
22073
23089
|
async function handler29(args, ctx) {
|
|
22074
23090
|
await hydrateAnalysisFromPersistedState(ctx);
|
|
22075
23091
|
const { flags } = parseArgs2(args, ["findings"]);
|
|
@@ -22111,7 +23127,7 @@ async function handler29(args, ctx) {
|
|
|
22111
23127
|
await initSchema();
|
|
22112
23128
|
await autoGenerateSegments();
|
|
22113
23129
|
printCompanionBanner("metrics", ctx.analysis.primary);
|
|
22114
|
-
const spinner =
|
|
23130
|
+
const spinner = ora12({
|
|
22115
23131
|
text: "Computing SaaS metrics\u2026",
|
|
22116
23132
|
indent: 2,
|
|
22117
23133
|
discardStdin: false
|
|
@@ -22310,7 +23326,7 @@ __export(feedback_exports, {
|
|
|
22310
23326
|
handler: () => handler30
|
|
22311
23327
|
});
|
|
22312
23328
|
import chalk47 from "chalk";
|
|
22313
|
-
import
|
|
23329
|
+
import ora13 from "ora";
|
|
22314
23330
|
async function handler30(args, ctx) {
|
|
22315
23331
|
const feedbackText = args.join(" ").trim();
|
|
22316
23332
|
if (!feedbackText) {
|
|
@@ -22338,7 +23354,7 @@ async function handler30(args, ctx) {
|
|
|
22338
23354
|
console.log();
|
|
22339
23355
|
return;
|
|
22340
23356
|
}
|
|
22341
|
-
const spinner =
|
|
23357
|
+
const spinner = ora13({ text: "Applying feedback\u2026", discardStdin: false }).start();
|
|
22342
23358
|
try {
|
|
22343
23359
|
const result = await applyFeedback(profile, feedbackText, ctx);
|
|
22344
23360
|
spinner.succeed("Feedback applied");
|
|
@@ -22370,7 +23386,7 @@ var recap_exports = {};
|
|
|
22370
23386
|
__export(recap_exports, {
|
|
22371
23387
|
handler: () => handler31
|
|
22372
23388
|
});
|
|
22373
|
-
import
|
|
23389
|
+
import ora14 from "ora";
|
|
22374
23390
|
import chalk48 from "chalk";
|
|
22375
23391
|
async function handler31(_args, ctx) {
|
|
22376
23392
|
if (ctx.messages.length === 0) {
|
|
@@ -22407,7 +23423,7 @@ ${companyBlock}` : "",
|
|
|
22407
23423
|
const prefix = msg.role === "user" ? "USER" : "ASSISTANT";
|
|
22408
23424
|
conversationLines.push(`[${prefix}]: ${msg.content}`);
|
|
22409
23425
|
}
|
|
22410
|
-
const spinner =
|
|
23426
|
+
const spinner = ora14({ text: "Summarizing session\u2026", color: "cyan", discardStdin: false }).start();
|
|
22411
23427
|
try {
|
|
22412
23428
|
const { text: fullText } = await llmStreamText(
|
|
22413
23429
|
"recap",
|
|
@@ -22535,7 +23551,7 @@ var init_recall = __esm({
|
|
|
22535
23551
|
|
|
22536
23552
|
// src/memory/feedback.ts
|
|
22537
23553
|
import { appendFileSync as appendFileSync5 } from "fs";
|
|
22538
|
-
import { join as
|
|
23554
|
+
import { join as join26 } from "path";
|
|
22539
23555
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
22540
23556
|
function summarize(text) {
|
|
22541
23557
|
return text.replace(/[#*`>_]/g, "").replace(/\s+/g, " ").trim().slice(0, 200);
|
|
@@ -22551,7 +23567,7 @@ function recordFeedback(input) {
|
|
|
22551
23567
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
22552
23568
|
};
|
|
22553
23569
|
try {
|
|
22554
|
-
appendFileSync5(
|
|
23570
|
+
appendFileSync5(join26(getMemoryDir(), "feedback.jsonl"), JSON.stringify(entry) + "\n");
|
|
22555
23571
|
} catch {
|
|
22556
23572
|
}
|
|
22557
23573
|
if (input.rating === "positive") {
|
|
@@ -22642,7 +23658,7 @@ __export(knowledge_exports, {
|
|
|
22642
23658
|
handler: () => handler35
|
|
22643
23659
|
});
|
|
22644
23660
|
import chalk52 from "chalk";
|
|
22645
|
-
import
|
|
23661
|
+
import ora15 from "ora";
|
|
22646
23662
|
async function handler35(args, ctx) {
|
|
22647
23663
|
const sub = (args[0] ?? "list").toLowerCase();
|
|
22648
23664
|
if (sub === "add") {
|
|
@@ -22654,7 +23670,7 @@ async function handler35(args, ctx) {
|
|
|
22654
23670
|
console.log();
|
|
22655
23671
|
return;
|
|
22656
23672
|
}
|
|
22657
|
-
const spin = ctx.execution.progress ?
|
|
23673
|
+
const spin = ctx.execution.progress ? ora15({ text: "Ingesting knowledge\u2026", color: "cyan", discardStdin: false }).start() : null;
|
|
22658
23674
|
try {
|
|
22659
23675
|
const result = await addKnowledgeFile(path);
|
|
22660
23676
|
spin?.succeed(`Indexed "${result.title}"`);
|
|
@@ -22964,8 +23980,8 @@ var switch_exports = {};
|
|
|
22964
23980
|
__export(switch_exports, {
|
|
22965
23981
|
handler: () => handler39
|
|
22966
23982
|
});
|
|
22967
|
-
import { join as
|
|
22968
|
-
import
|
|
23983
|
+
import { join as join27 } from "path";
|
|
23984
|
+
import ora16 from "ora";
|
|
22969
23985
|
import chalk56 from "chalk";
|
|
22970
23986
|
async function handler39(args, ctx) {
|
|
22971
23987
|
if (args.length === 0) {
|
|
@@ -22980,7 +23996,7 @@ async function handler39(args, ctx) {
|
|
|
22980
23996
|
}
|
|
22981
23997
|
const exchangeCount = Math.floor(ctx.messages.length / 2);
|
|
22982
23998
|
if (exchangeCount > 0) {
|
|
22983
|
-
const spinner =
|
|
23999
|
+
const spinner = ora16({ text: "Saving current session\u2026", color: "cyan", discardStdin: false }).start();
|
|
22984
24000
|
await closeSession(ctx);
|
|
22985
24001
|
const fromLabel = ctx.sessionName ? `"${ctx.sessionName}"` : ctx.sessionId.slice(-4);
|
|
22986
24002
|
spinner.succeed(`Saved ${fromLabel}`);
|
|
@@ -22995,7 +24011,7 @@ async function handler39(args, ctx) {
|
|
|
22995
24011
|
}
|
|
22996
24012
|
const context = buildSwitchContext(session);
|
|
22997
24013
|
const newId = makeSessionId();
|
|
22998
|
-
const newFile =
|
|
24014
|
+
const newFile = join27(getSessionsDir(), `${newId}.json`);
|
|
22999
24015
|
resetContextForSwitch(ctx, {
|
|
23000
24016
|
sessionId: newId,
|
|
23001
24017
|
sessionFile: newFile,
|
|
@@ -23021,7 +24037,7 @@ async function handler39(args, ctx) {
|
|
|
23021
24037
|
return `Switched to "${targetName}"`;
|
|
23022
24038
|
} else {
|
|
23023
24039
|
const newId = makeSessionId();
|
|
23024
|
-
const newFile =
|
|
24040
|
+
const newFile = join27(getSessionsDir(), `${newId}.json`);
|
|
23025
24041
|
resetContextForSwitch(ctx, {
|
|
23026
24042
|
sessionId: newId,
|
|
23027
24043
|
sessionFile: newFile,
|
|
@@ -23071,13 +24087,177 @@ var init_switch = __esm({
|
|
|
23071
24087
|
}
|
|
23072
24088
|
});
|
|
23073
24089
|
|
|
23074
|
-
// src/commands/
|
|
23075
|
-
var
|
|
23076
|
-
__export(
|
|
24090
|
+
// src/commands/connect.ts
|
|
24091
|
+
var connect_exports2 = {};
|
|
24092
|
+
__export(connect_exports2, {
|
|
23077
24093
|
handler: () => handler40
|
|
23078
24094
|
});
|
|
23079
24095
|
import chalk57 from "chalk";
|
|
24096
|
+
import ora17 from "ora";
|
|
24097
|
+
function usage2() {
|
|
24098
|
+
console.log(chalk57.dim(" Usage: /connect paste any provider key"));
|
|
24099
|
+
console.log(chalk57.dim(" /connect <provider> key for a specific provider (or: ollama)"));
|
|
24100
|
+
console.log(chalk57.dim(" /connect --key <key> non-interactive (auto-detects provider)"));
|
|
24101
|
+
console.log(chalk57.dim(" /connect --base-url <url> [--id <name>] [--key <key>] custom endpoint"));
|
|
24102
|
+
}
|
|
24103
|
+
function printOutcome(outcome, ctx) {
|
|
24104
|
+
console.log();
|
|
24105
|
+
const [headline, ...rest] = describeConnectOutcome(outcome);
|
|
24106
|
+
console.log(" " + paint("success", "\u2713") + " " + chalk57.bold(headline ?? ""));
|
|
24107
|
+
for (const line of rest) {
|
|
24108
|
+
console.log(" " + chalk57.dim(line));
|
|
24109
|
+
}
|
|
24110
|
+
console.log();
|
|
24111
|
+
console.log(" " + chalk57.dim(`Active stack: ${formatActiveStack(ctx)}`));
|
|
24112
|
+
console.log(" " + chalk57.dim("/provider to switch engines \xB7 /model list to browse models"));
|
|
24113
|
+
console.log();
|
|
24114
|
+
}
|
|
24115
|
+
function printError(err, ctx) {
|
|
24116
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
24117
|
+
console.log();
|
|
24118
|
+
console.log(" " + chalk57.red(message));
|
|
24119
|
+
console.log();
|
|
24120
|
+
if (ctx.oneShot) process.exit(1);
|
|
24121
|
+
}
|
|
24122
|
+
async function promptKey(session, label) {
|
|
24123
|
+
console.log();
|
|
24124
|
+
console.log(
|
|
24125
|
+
" " + chalk57.dim("Paste once, press Enter. Stored in ") + paint("accent", "~/.ntrp/config.json") + chalk57.dim(" only.")
|
|
24126
|
+
);
|
|
24127
|
+
return session.askSecret(label, { confirm: false });
|
|
24128
|
+
}
|
|
23080
24129
|
async function handler40(args, ctx) {
|
|
24130
|
+
const { positional, flags } = parseArgs2(args);
|
|
24131
|
+
const sub = positional[0]?.toLowerCase();
|
|
24132
|
+
if (sub === "help") {
|
|
24133
|
+
usage2();
|
|
24134
|
+
return;
|
|
24135
|
+
}
|
|
24136
|
+
const inlineKey = getString(flags, "key");
|
|
24137
|
+
const baseUrl = getString(flags, "base-url", "url");
|
|
24138
|
+
const forcedProvider = getString(flags, "provider") ?? (sub && sub !== "help" ? sub : void 0);
|
|
24139
|
+
const customId = getString(flags, "id");
|
|
24140
|
+
const label = getString(flags, "label");
|
|
24141
|
+
const forcedSpec = forcedProvider ? getProviderSpec(forcedProvider) : void 0;
|
|
24142
|
+
if (forcedSpec && !forcedSpec.requires_key && !inlineKey) {
|
|
24143
|
+
const spinner2 = ora17({ text: `Looking for ${forcedSpec.label}\u2026`, discardStdin: false }).start();
|
|
24144
|
+
try {
|
|
24145
|
+
const outcome = await connectKeyless(forcedSpec.id, baseUrl);
|
|
24146
|
+
spinner2.stop();
|
|
24147
|
+
printOutcome(outcome, ctx);
|
|
24148
|
+
} catch (err) {
|
|
24149
|
+
spinner2.stop();
|
|
24150
|
+
printError(err, ctx);
|
|
24151
|
+
}
|
|
24152
|
+
return;
|
|
24153
|
+
}
|
|
24154
|
+
if (baseUrl && !forcedSpec) {
|
|
24155
|
+
const id = customId ?? forcedProvider ?? hostToId(baseUrl);
|
|
24156
|
+
let key2 = inlineKey;
|
|
24157
|
+
if (!key2 && !ctx.oneShot && process.stdin.isTTY) {
|
|
24158
|
+
const session2 = createPromptSession(ctx.rl, ctx);
|
|
24159
|
+
try {
|
|
24160
|
+
const needsKey = await session2.confirm("Does this endpoint need an API key?", false);
|
|
24161
|
+
if (needsKey) key2 = await promptKey(session2, `API key for ${id}`);
|
|
24162
|
+
} finally {
|
|
24163
|
+
session2.close();
|
|
24164
|
+
}
|
|
24165
|
+
}
|
|
24166
|
+
const spinner2 = ora17({ text: `Checking ${baseUrl}\u2026`, discardStdin: false }).start();
|
|
24167
|
+
try {
|
|
24168
|
+
const outcome = await connectCustomEndpoint({ id, baseUrl, key: key2, label });
|
|
24169
|
+
spinner2.stop();
|
|
24170
|
+
printOutcome(outcome, ctx);
|
|
24171
|
+
} catch (err) {
|
|
24172
|
+
spinner2.stop();
|
|
24173
|
+
printError(err, ctx);
|
|
24174
|
+
}
|
|
24175
|
+
return;
|
|
24176
|
+
}
|
|
24177
|
+
if (forcedProvider && !forcedSpec) {
|
|
24178
|
+
console.log();
|
|
24179
|
+
console.log(" " + chalk57.red(`Unknown provider: ${forcedProvider}`));
|
|
24180
|
+
console.log(
|
|
24181
|
+
" " + chalk57.dim("Built-ins: anthropic, openai, google, groq, mistral, deepseek, xai, openrouter, together, fireworks, ollama")
|
|
24182
|
+
);
|
|
24183
|
+
console.log(" " + chalk57.dim(`Custom endpoint: /connect --base-url <url> --id ${forcedProvider}`));
|
|
24184
|
+
console.log();
|
|
24185
|
+
if (ctx.oneShot) process.exit(1);
|
|
24186
|
+
return;
|
|
24187
|
+
}
|
|
24188
|
+
let key = inlineKey;
|
|
24189
|
+
let session;
|
|
24190
|
+
if (!key) {
|
|
24191
|
+
if (ctx.oneShot || !process.stdin.isTTY) {
|
|
24192
|
+
printError(new ConnectError("Non-interactive mode needs --key <key>."), ctx);
|
|
24193
|
+
usage2();
|
|
24194
|
+
return;
|
|
24195
|
+
}
|
|
24196
|
+
session = createPromptSession(ctx.rl, ctx);
|
|
24197
|
+
key = await promptKey(
|
|
24198
|
+
session,
|
|
24199
|
+
forcedSpec ? `${forcedSpec.label} API key` : "LLM API key (any provider)"
|
|
24200
|
+
);
|
|
24201
|
+
}
|
|
24202
|
+
const spinner = ora17({ text: "Identifying provider\u2026", discardStdin: false }).start();
|
|
24203
|
+
try {
|
|
24204
|
+
const outcome = await connectWithKey(key, {
|
|
24205
|
+
providerId: forcedSpec?.id,
|
|
24206
|
+
callbacks: session ? {
|
|
24207
|
+
confirmDetection: async (providerId) => {
|
|
24208
|
+
spinner.stop();
|
|
24209
|
+
return session.confirm(`Detected ${providerLabel(providerId)} \u2014 connect it?`, true);
|
|
24210
|
+
},
|
|
24211
|
+
chooseProvider: async (accepted) => {
|
|
24212
|
+
spinner.stop();
|
|
24213
|
+
return session.choose(
|
|
24214
|
+
"Multiple providers accepted this key \u2014 which is it?",
|
|
24215
|
+
accepted.map((a) => ({ value: a.provider, label: providerLabel(a.provider) }))
|
|
24216
|
+
);
|
|
24217
|
+
}
|
|
24218
|
+
} : void 0
|
|
24219
|
+
});
|
|
24220
|
+
spinner.stop();
|
|
24221
|
+
printOutcome(outcome, ctx);
|
|
24222
|
+
} catch (err) {
|
|
24223
|
+
spinner.stop();
|
|
24224
|
+
if (err instanceof ConnectCancelled) {
|
|
24225
|
+
console.log(" " + chalk57.dim("Cancelled."));
|
|
24226
|
+
console.log();
|
|
24227
|
+
} else {
|
|
24228
|
+
printError(err, ctx);
|
|
24229
|
+
}
|
|
24230
|
+
} finally {
|
|
24231
|
+
session?.close();
|
|
24232
|
+
}
|
|
24233
|
+
}
|
|
24234
|
+
function hostToId(url) {
|
|
24235
|
+
try {
|
|
24236
|
+
const host = new URL(url).hostname;
|
|
24237
|
+
return host.replace(/^www\./, "").split(".")[0] ?? "custom";
|
|
24238
|
+
} catch {
|
|
24239
|
+
return "custom";
|
|
24240
|
+
}
|
|
24241
|
+
}
|
|
24242
|
+
var init_connect2 = __esm({
|
|
24243
|
+
"src/commands/connect.ts"() {
|
|
24244
|
+
"use strict";
|
|
24245
|
+
init_argparse();
|
|
24246
|
+
init_prompts();
|
|
24247
|
+
init_providers();
|
|
24248
|
+
init_session_state();
|
|
24249
|
+
init_connect();
|
|
24250
|
+
init_theme();
|
|
24251
|
+
}
|
|
24252
|
+
});
|
|
24253
|
+
|
|
24254
|
+
// src/commands/provider.ts
|
|
24255
|
+
var provider_exports = {};
|
|
24256
|
+
__export(provider_exports, {
|
|
24257
|
+
handler: () => handler41
|
|
24258
|
+
});
|
|
24259
|
+
import chalk58 from "chalk";
|
|
24260
|
+
async function handler41(args, ctx) {
|
|
23081
24261
|
const { positional, flags } = parseArgs2(args, ["default"]);
|
|
23082
24262
|
const sub = positional[0]?.toLowerCase();
|
|
23083
24263
|
if (!sub || sub === "list") {
|
|
@@ -23089,7 +24269,7 @@ async function handler40(args, ctx) {
|
|
|
23089
24269
|
if (!ctx.oneShot) saveSessionState(ctx);
|
|
23090
24270
|
console.log();
|
|
23091
24271
|
console.log(" " + paint("success", "\u2713") + " Session engine reset \u2014 using config defaults.");
|
|
23092
|
-
console.log(" " +
|
|
24272
|
+
console.log(" " + chalk58.dim(`Default: ${loadLlmConfig().primary}`));
|
|
23093
24273
|
console.log();
|
|
23094
24274
|
return;
|
|
23095
24275
|
}
|
|
@@ -23102,7 +24282,7 @@ async function handler40(args, ctx) {
|
|
|
23102
24282
|
setConfigValue("llm-auto-failover", session.autoFailover ? "on" : "off");
|
|
23103
24283
|
}
|
|
23104
24284
|
console.log();
|
|
23105
|
-
console.log(" " + paint("success", "\u2713") + ` Saved ${
|
|
24285
|
+
console.log(" " + paint("success", "\u2713") + ` Saved ${chalk58.bold(active)} as default engine.`);
|
|
23106
24286
|
console.log();
|
|
23107
24287
|
return;
|
|
23108
24288
|
}
|
|
@@ -23121,36 +24301,40 @@ async function handler40(args, ctx) {
|
|
|
23121
24301
|
}
|
|
23122
24302
|
console.log();
|
|
23123
24303
|
console.log(
|
|
23124
|
-
" " + paint("success", "\u2713") + ` Auto-failover ${session.autoFailover ?
|
|
24304
|
+
" " + paint("success", "\u2713") + ` Auto-failover ${session.autoFailover ? chalk58.bold("on") : chalk58.bold("off")} for this session.`
|
|
23125
24305
|
);
|
|
23126
|
-
if (persist) console.log(" " +
|
|
24306
|
+
if (persist) console.log(" " + chalk58.dim("Also saved as config default."));
|
|
23127
24307
|
console.log();
|
|
23128
24308
|
return;
|
|
23129
24309
|
}
|
|
23130
|
-
|
|
24310
|
+
const spec = getProviderSpec(sub);
|
|
24311
|
+
if (!spec || RESERVED.has(sub)) {
|
|
23131
24312
|
console.log();
|
|
23132
|
-
console.log(" " +
|
|
23133
|
-
console.log(" " +
|
|
24313
|
+
console.log(" " + chalk58.red(`Unknown engine: ${sub}`));
|
|
24314
|
+
console.log(" " + chalk58.dim("Usage: /provider [<id>|list|reset|save|failover on|off]"));
|
|
24315
|
+
console.log(" " + chalk58.dim("Connected: ") + (availableEngineLabels().join(", ") || chalk58.dim("none")));
|
|
24316
|
+
console.log(" " + chalk58.dim("Add one with ") + paint("accent", "/connect"));
|
|
23134
24317
|
console.log();
|
|
23135
24318
|
return;
|
|
23136
24319
|
}
|
|
23137
|
-
const provider =
|
|
24320
|
+
const provider = spec.id;
|
|
23138
24321
|
if (!hasProviderKey(provider)) {
|
|
23139
|
-
const keyHint = provider === "anthropic" ? "api-key" : "openai-api-key";
|
|
23140
24322
|
console.log();
|
|
23141
|
-
console.log(" " +
|
|
23142
|
-
console.log(
|
|
24323
|
+
console.log(" " + chalk58.red(`${spec.label} isn't connected.`));
|
|
24324
|
+
console.log(
|
|
24325
|
+
" " + chalk58.dim("Run ") + paint("accent", `/connect ${provider}`) + chalk58.dim(" (or ") + paint("accent", `/config set ${spec.key_config_name}`) + chalk58.dim(").")
|
|
24326
|
+
);
|
|
23143
24327
|
console.log();
|
|
23144
24328
|
return;
|
|
23145
24329
|
}
|
|
23146
24330
|
ensureLlmSession(ctx).provider = provider;
|
|
23147
24331
|
if (!ctx.oneShot) saveSessionState(ctx);
|
|
23148
24332
|
console.log();
|
|
23149
|
-
console.log(" " + paint("success", "\u2713") + ` Active engine: ${
|
|
23150
|
-
console.log(" " +
|
|
24333
|
+
console.log(" " + paint("success", "\u2713") + ` Active engine: ${chalk58.bold(provider)}`);
|
|
24334
|
+
console.log(" " + chalk58.dim(`Stack: ${formatActiveStack(ctx)}`));
|
|
23151
24335
|
const others = availableEngineLabels().filter((p) => p !== provider);
|
|
23152
24336
|
if (others.length > 0) {
|
|
23153
|
-
console.log(" " +
|
|
24337
|
+
console.log(" " + chalk58.dim(`Also available: ${others.join(", ")}`));
|
|
23154
24338
|
}
|
|
23155
24339
|
console.log();
|
|
23156
24340
|
}
|
|
@@ -23161,49 +24345,54 @@ function printStatus(ctx) {
|
|
|
23161
24345
|
const autoFailover = resolveAutoFailoverEnabled(ctx);
|
|
23162
24346
|
const engines = countAvailableEngines();
|
|
23163
24347
|
console.log();
|
|
23164
|
-
console.log(
|
|
23165
|
-
console.log(`
|
|
23166
|
-
|
|
23167
|
-
|
|
23168
|
-
const marker2 =
|
|
23169
|
-
console.log(` ${
|
|
24348
|
+
console.log(chalk58.bold(" LLM engines"));
|
|
24349
|
+
console.log(` Connected: ${engines} engine${engines === 1 ? "" : "s"}`);
|
|
24350
|
+
const configured = listProviderSpecs().filter((s) => hasProviderKey(s.id));
|
|
24351
|
+
for (const s of configured) {
|
|
24352
|
+
const marker2 = s.id === active ? paint("accent", " \u25BA active") : "";
|
|
24353
|
+
console.log(` ${paint("success", "\u2713")} ${s.id}${s.custom ? chalk58.dim(" (custom)") : ""}${marker2}`);
|
|
24354
|
+
}
|
|
24355
|
+
if (configured.length === 0) {
|
|
24356
|
+
console.log(" " + chalk58.dim("none \u2014 run /connect and paste any provider key"));
|
|
23170
24357
|
}
|
|
23171
24358
|
console.log();
|
|
23172
|
-
console.log(
|
|
24359
|
+
console.log(chalk58.bold(" Active stack"));
|
|
23173
24360
|
console.log(` ${formatActiveStack(ctx)}`);
|
|
23174
24361
|
if (sessionOverride) {
|
|
23175
|
-
console.log(
|
|
24362
|
+
console.log(chalk58.dim(" (session override \u2014 /provider reset to use default)"));
|
|
23176
24363
|
} else {
|
|
23177
|
-
console.log(
|
|
24364
|
+
console.log(chalk58.dim(` (config default: ${cfg.primary})`));
|
|
23178
24365
|
}
|
|
23179
24366
|
console.log();
|
|
23180
|
-
console.log(` Auto-failover: ${autoFailover ? paint("success", "on") :
|
|
23181
|
-
console.log(
|
|
23182
|
-
console.log(
|
|
23183
|
-
console.log(
|
|
24367
|
+
console.log(` Auto-failover: ${autoFailover ? paint("success", "on") : chalk58.dim("off")}`);
|
|
24368
|
+
console.log(chalk58.dim(` /provider <id> \u2014 switch engine (${configured.map((s) => s.id).join(", ") || "none connected"})`));
|
|
24369
|
+
console.log(chalk58.dim(" /provider failover on|off \u2014 rate-limit safety net"));
|
|
24370
|
+
console.log(chalk58.dim(" /provider save \u2014 persist active engine to config"));
|
|
24371
|
+
console.log(chalk58.dim(" /connect \u2014 add another provider (any API key)"));
|
|
23184
24372
|
console.log();
|
|
23185
24373
|
}
|
|
23186
|
-
var
|
|
24374
|
+
var RESERVED;
|
|
23187
24375
|
var init_provider = __esm({
|
|
23188
24376
|
"src/commands/provider.ts"() {
|
|
23189
24377
|
"use strict";
|
|
23190
24378
|
init_argparse();
|
|
23191
24379
|
init_session_state();
|
|
24380
|
+
init_providers();
|
|
23192
24381
|
init_llm_config();
|
|
23193
24382
|
init_store();
|
|
23194
24383
|
init_context2();
|
|
23195
24384
|
init_theme();
|
|
23196
|
-
|
|
24385
|
+
RESERVED = /* @__PURE__ */ new Set(["list", "reset", "save", "failover"]);
|
|
23197
24386
|
}
|
|
23198
24387
|
});
|
|
23199
24388
|
|
|
23200
24389
|
// src/commands/tier.ts
|
|
23201
24390
|
var tier_exports = {};
|
|
23202
24391
|
__export(tier_exports, {
|
|
23203
|
-
handler: () =>
|
|
24392
|
+
handler: () => handler42
|
|
23204
24393
|
});
|
|
23205
|
-
import
|
|
23206
|
-
async function
|
|
24394
|
+
import chalk59 from "chalk";
|
|
24395
|
+
async function handler42(args, ctx) {
|
|
23207
24396
|
const { positional, flags } = parseArgs2(args, ["default"]);
|
|
23208
24397
|
const sub = positional[0]?.toLowerCase();
|
|
23209
24398
|
if (!sub || sub === "list") {
|
|
@@ -23212,8 +24401,8 @@ async function handler41(args, ctx) {
|
|
|
23212
24401
|
}
|
|
23213
24402
|
if (!TIERS.includes(sub)) {
|
|
23214
24403
|
console.log();
|
|
23215
|
-
console.log(" " +
|
|
23216
|
-
console.log(" " +
|
|
24404
|
+
console.log(" " + chalk59.red(`Unknown tier: ${sub}`));
|
|
24405
|
+
console.log(" " + chalk59.dim("Usage: /tier [high|medium|low|list] [--default]"));
|
|
23217
24406
|
console.log();
|
|
23218
24407
|
return;
|
|
23219
24408
|
}
|
|
@@ -23227,40 +24416,48 @@ async function handler41(args, ctx) {
|
|
|
23227
24416
|
}
|
|
23228
24417
|
console.log();
|
|
23229
24418
|
console.log(
|
|
23230
|
-
" " + paint("success", "\u2713") + ` Inference tier set to ${
|
|
24419
|
+
" " + paint("success", "\u2713") + ` Inference tier set to ${chalk59.bold(tier.toUpperCase())}` + (persist ? chalk59.dim(" (saved as default)") : chalk59.dim(" (this session)"))
|
|
23231
24420
|
);
|
|
23232
|
-
console.log(" " +
|
|
24421
|
+
console.log(" " + chalk59.dim(`Stack: ${formatActiveStack(ctx)}`));
|
|
23233
24422
|
console.log();
|
|
23234
24423
|
}
|
|
23235
24424
|
function printCatalog(ctx) {
|
|
23236
24425
|
const cfg = loadLlmConfig();
|
|
23237
24426
|
const active = resolveModelForActive(ctx, "agentic_investigation");
|
|
23238
24427
|
const sessionTier = ctx.llm?.tier;
|
|
24428
|
+
const providers = getAvailableProviders();
|
|
23239
24429
|
console.log();
|
|
23240
|
-
console.log(
|
|
24430
|
+
console.log(chalk59.bold(" Inference settings"));
|
|
23241
24431
|
console.log(` Active: ${paint("accent", formatActiveStack(ctx))}`);
|
|
23242
24432
|
if (sessionTier) {
|
|
23243
|
-
console.log(
|
|
24433
|
+
console.log(chalk59.dim(" (session tier override)"));
|
|
23244
24434
|
} else {
|
|
23245
|
-
console.log(
|
|
24435
|
+
console.log(chalk59.dim(` Config default tier: ${cfg.tier.toUpperCase()}`));
|
|
23246
24436
|
}
|
|
23247
24437
|
console.log();
|
|
24438
|
+
if (providers.length === 0) {
|
|
24439
|
+
console.log(" " + chalk59.dim("No engines connected \u2014 run /connect and paste any provider key."));
|
|
24440
|
+
console.log();
|
|
24441
|
+
}
|
|
23248
24442
|
for (const tier of TIERS) {
|
|
23249
|
-
console.log(
|
|
23250
|
-
for (const provider of
|
|
23251
|
-
const
|
|
23252
|
-
|
|
23253
|
-
|
|
23254
|
-
|
|
23255
|
-
const status = m.status === "active" ? "" : chalk58.yellow(` [${m.status}]`);
|
|
23256
|
-
console.log(`${marker2}${provider}/${m.id}${status} \u2014 ${m.display_name}`);
|
|
24443
|
+
console.log(chalk59.bold(` ${tier.toUpperCase()}`));
|
|
24444
|
+
for (const provider of providers) {
|
|
24445
|
+
const modelId = resolveModelSafe(provider, tier);
|
|
24446
|
+
if (!modelId) {
|
|
24447
|
+
console.log(` ${provider}/${chalk59.dim("no models \u2014 /model refresh")}`);
|
|
24448
|
+
continue;
|
|
23257
24449
|
}
|
|
24450
|
+
const isActive = provider === active.provider && tier === active.tier && modelId === active.modelId;
|
|
24451
|
+
const marker2 = isActive ? paint("accent", "\u25BA ") : " ";
|
|
24452
|
+
const discovered = !!getProviderModels(provider);
|
|
24453
|
+
const source = discovered ? "" : chalk59.dim(" [bundled fallback]");
|
|
24454
|
+
console.log(`${marker2}${provider}/${modelId}${source}`);
|
|
23258
24455
|
}
|
|
23259
24456
|
console.log();
|
|
23260
24457
|
}
|
|
23261
|
-
console.log(
|
|
23262
|
-
console.log(
|
|
23263
|
-
console.log(
|
|
24458
|
+
console.log(chalk59.dim(" /tier high|medium|low \u2014 set tier for this session"));
|
|
24459
|
+
console.log(chalk59.dim(" /tier high --default \u2014 also save as config default"));
|
|
24460
|
+
console.log(chalk59.dim(" /provider <id> \u2014 switch engine \xB7 /model list \u2014 browse models"));
|
|
23264
24461
|
console.log();
|
|
23265
24462
|
}
|
|
23266
24463
|
var TIERS;
|
|
@@ -23269,6 +24466,7 @@ var init_tier = __esm({
|
|
|
23269
24466
|
"use strict";
|
|
23270
24467
|
init_argparse();
|
|
23271
24468
|
init_catalog();
|
|
24469
|
+
init_models_cache();
|
|
23272
24470
|
init_session_state();
|
|
23273
24471
|
init_llm_config();
|
|
23274
24472
|
init_store();
|
|
@@ -23281,12 +24479,21 @@ var init_tier = __esm({
|
|
|
23281
24479
|
// src/commands/model.ts
|
|
23282
24480
|
var model_exports = {};
|
|
23283
24481
|
__export(model_exports, {
|
|
23284
|
-
handler: () =>
|
|
24482
|
+
handler: () => handler43
|
|
23285
24483
|
});
|
|
23286
|
-
import
|
|
23287
|
-
|
|
23288
|
-
|
|
24484
|
+
import chalk60 from "chalk";
|
|
24485
|
+
import ora18 from "ora";
|
|
24486
|
+
async function handler43(args, ctx) {
|
|
24487
|
+
const { positional, flags } = parseArgs2(args, ["default", "all"]);
|
|
23289
24488
|
const sub = positional[0]?.toLowerCase();
|
|
24489
|
+
if (sub === "list") {
|
|
24490
|
+
printModelList(ctx, getBool(flags, "all"));
|
|
24491
|
+
return;
|
|
24492
|
+
}
|
|
24493
|
+
if (sub === "refresh") {
|
|
24494
|
+
await refreshModels(ctx);
|
|
24495
|
+
return;
|
|
24496
|
+
}
|
|
23290
24497
|
if (sub === "clear") {
|
|
23291
24498
|
const persist = getBool(flags, "default");
|
|
23292
24499
|
if (ctx.llm) ctx.llm.modelOverride = void 0;
|
|
@@ -23294,7 +24501,7 @@ async function handler42(args, ctx) {
|
|
|
23294
24501
|
if (!ctx.oneShot) saveSessionState(ctx);
|
|
23295
24502
|
console.log();
|
|
23296
24503
|
console.log(" " + paint("success", "\u2713") + " Model override cleared \u2014 using tier defaults.");
|
|
23297
|
-
console.log(" " +
|
|
24504
|
+
console.log(" " + chalk60.dim(`Stack: ${formatActiveStack(ctx)}`));
|
|
23298
24505
|
console.log();
|
|
23299
24506
|
return;
|
|
23300
24507
|
}
|
|
@@ -23302,22 +24509,28 @@ async function handler42(args, ctx) {
|
|
|
23302
24509
|
const modelId = positional[1];
|
|
23303
24510
|
if (!modelId) {
|
|
23304
24511
|
console.log();
|
|
23305
|
-
console.log(" " +
|
|
24512
|
+
console.log(" " + chalk60.red("Usage: /model set <model-id> [--default]"));
|
|
23306
24513
|
console.log();
|
|
23307
24514
|
return;
|
|
23308
24515
|
}
|
|
23309
24516
|
const active = resolveActiveProvider(ctx);
|
|
23310
24517
|
const providerErr = validateModelForProvider(modelId, active);
|
|
23311
|
-
const entry = getCatalogEntry(modelId);
|
|
23312
24518
|
if (providerErr) {
|
|
23313
24519
|
console.log();
|
|
23314
|
-
console.log(" " +
|
|
24520
|
+
console.log(" " + chalk60.red(providerErr));
|
|
23315
24521
|
console.log();
|
|
23316
24522
|
return;
|
|
23317
24523
|
}
|
|
23318
|
-
|
|
24524
|
+
const cache2 = getProviderModels(active);
|
|
24525
|
+
const known = cache2?.models.some((m) => m.id === modelId);
|
|
24526
|
+
if (cache2 && !known) {
|
|
24527
|
+
console.log();
|
|
24528
|
+
console.log(
|
|
24529
|
+
" " + chalk60.yellow("\u26A0") + ` ${modelId} isn't in ${active}'s discovered list (` + paint("accent", "/model list") + `) \u2014 saving anyway.`
|
|
24530
|
+
);
|
|
24531
|
+
} else if (!cache2) {
|
|
23319
24532
|
console.log();
|
|
23320
|
-
console.log(" " +
|
|
24533
|
+
console.log(" " + chalk60.yellow("\u26A0") + ` No discovered models for ${active} yet (` + paint("accent", "/model refresh") + `) \u2014 saving anyway.`);
|
|
23321
24534
|
}
|
|
23322
24535
|
const persist = getBool(flags, "default");
|
|
23323
24536
|
if (persist) {
|
|
@@ -23328,36 +24541,86 @@ async function handler42(args, ctx) {
|
|
|
23328
24541
|
}
|
|
23329
24542
|
console.log();
|
|
23330
24543
|
console.log(
|
|
23331
|
-
" " + paint("success", "\u2713") + ` Model: ${
|
|
24544
|
+
" " + paint("success", "\u2713") + ` Model: ${chalk60.bold(modelId)}` + (persist ? chalk60.dim(" (saved as default)") : chalk60.dim(" (this session)"))
|
|
23332
24545
|
);
|
|
23333
|
-
console.log(" " +
|
|
24546
|
+
console.log(" " + chalk60.dim(`Stack: ${formatActiveStack(ctx)}`));
|
|
23334
24547
|
console.log();
|
|
23335
24548
|
return;
|
|
23336
24549
|
}
|
|
23337
24550
|
const sessionOverride = ctx.llm?.modelOverride;
|
|
23338
24551
|
const globalOverride = getConfigValue("llm-model-override");
|
|
23339
24552
|
console.log();
|
|
23340
|
-
console.log(
|
|
24553
|
+
console.log(chalk60.bold(" Model"));
|
|
23341
24554
|
if (sessionOverride) {
|
|
23342
24555
|
console.log(` Session override: ${paint("accent", sessionOverride)}`);
|
|
23343
24556
|
} else if (globalOverride) {
|
|
23344
24557
|
console.log(` Config default: ${paint("accent", globalOverride)}`);
|
|
23345
24558
|
} else {
|
|
23346
|
-
console.log(" " +
|
|
24559
|
+
console.log(" " + chalk60.dim("No override \u2014 tier defaults apply."));
|
|
23347
24560
|
}
|
|
23348
24561
|
console.log(` Active stack: ${formatActiveStack(ctx)}`);
|
|
23349
|
-
console.log(
|
|
24562
|
+
console.log(chalk60.dim(" /model list \xB7 /model set <id> \xB7 /model refresh \xB7 /model clear"));
|
|
24563
|
+
console.log();
|
|
24564
|
+
}
|
|
24565
|
+
function tierMarkers(cache2, modelId) {
|
|
24566
|
+
const tiers = Object.entries(cache2.tier_stack).filter(([, id]) => id === modelId).map(([tier]) => tier.toUpperCase());
|
|
24567
|
+
return tiers.length > 0 ? paint("accent", ` \u25C2 ${tiers.join("/")}`) : "";
|
|
24568
|
+
}
|
|
24569
|
+
function printModelList(ctx, showAll) {
|
|
24570
|
+
const active = resolveActiveProvider(ctx);
|
|
24571
|
+
const cache2 = getProviderModels(active);
|
|
24572
|
+
console.log();
|
|
24573
|
+
console.log(chalk60.bold(` Models \u2014 ${active}`));
|
|
24574
|
+
if (!cache2) {
|
|
24575
|
+
console.log(" " + chalk60.dim("Nothing discovered yet."));
|
|
24576
|
+
console.log(" " + chalk60.dim("Run ") + paint("accent", "/model refresh") + chalk60.dim(" (or ") + paint("accent", "/connect") + chalk60.dim(" to add the provider)."));
|
|
24577
|
+
console.log();
|
|
24578
|
+
return;
|
|
24579
|
+
}
|
|
24580
|
+
const fetchedAt = cache2.fetched_at.slice(0, 10);
|
|
24581
|
+
console.log(" " + chalk60.dim(`${cache2.models.length} chat models \xB7 discovered ${fetchedAt} \xB7 /model refresh to update`));
|
|
24582
|
+
console.log();
|
|
24583
|
+
const models = showAll ? cache2.models : cache2.models.slice(0, LIST_LIMIT);
|
|
24584
|
+
const noTools = new Set(cache2.quirks?.no_tools ?? []);
|
|
24585
|
+
for (const m of models) {
|
|
24586
|
+
const name = m.display_name && m.display_name !== m.id ? chalk60.dim(` \u2014 ${m.display_name}`) : "";
|
|
24587
|
+
const quirk = noTools.has(m.id) ? chalk60.yellow(" [no tools]") : "";
|
|
24588
|
+
console.log(` ${m.id}${name}${tierMarkers(cache2, m.id)}${quirk}`);
|
|
24589
|
+
}
|
|
24590
|
+
if (!showAll && cache2.models.length > models.length) {
|
|
24591
|
+
console.log(" " + chalk60.dim(`\u2026 and ${cache2.models.length - models.length} more (/model list --all)`));
|
|
24592
|
+
}
|
|
24593
|
+
console.log();
|
|
24594
|
+
console.log(" " + chalk60.dim("/model set <id> \u2014 pin one for this session (--default to persist)"));
|
|
24595
|
+
console.log();
|
|
24596
|
+
}
|
|
24597
|
+
async function refreshModels(ctx) {
|
|
24598
|
+
const active = resolveActiveProvider(ctx);
|
|
24599
|
+
const spinner = ora18({ text: `Discovering ${active} models\u2026`, discardStdin: false }).start();
|
|
24600
|
+
const entry = await refreshProviderModels(active, { force: true });
|
|
24601
|
+
if (!entry) {
|
|
24602
|
+
spinner.fail(`Couldn't reach ${active} to refresh models.`);
|
|
24603
|
+
console.log(" " + chalk60.dim("Check your connection and key, then retry. Cached models remain in use."));
|
|
24604
|
+
console.log();
|
|
24605
|
+
return;
|
|
24606
|
+
}
|
|
24607
|
+
spinner.succeed(`${active}: ${entry.models.length} chat models discovered.`);
|
|
24608
|
+
console.log(" " + chalk60.dim(`high ${entry.tier_stack.high} \xB7 medium ${entry.tier_stack.medium} \xB7 low ${entry.tier_stack.low}`));
|
|
24609
|
+
console.log(" " + chalk60.dim(`Stack: ${formatActiveStack(ctx)}`));
|
|
23350
24610
|
console.log();
|
|
23351
24611
|
}
|
|
24612
|
+
var LIST_LIMIT;
|
|
23352
24613
|
var init_model = __esm({
|
|
23353
24614
|
"src/commands/model.ts"() {
|
|
23354
24615
|
"use strict";
|
|
23355
24616
|
init_argparse();
|
|
23356
|
-
|
|
24617
|
+
init_discovery();
|
|
24618
|
+
init_models_cache();
|
|
23357
24619
|
init_session_state();
|
|
23358
24620
|
init_store();
|
|
23359
24621
|
init_context2();
|
|
23360
24622
|
init_theme();
|
|
24623
|
+
LIST_LIMIT = 40;
|
|
23361
24624
|
}
|
|
23362
24625
|
});
|
|
23363
24626
|
|
|
@@ -23369,22 +24632,22 @@ __export(update_check_exports, {
|
|
|
23369
24632
|
loadUpdateCheckCache: () => loadUpdateCheckCache,
|
|
23370
24633
|
saveUpdateCheckCache: () => saveUpdateCheckCache
|
|
23371
24634
|
});
|
|
23372
|
-
import { existsSync as
|
|
23373
|
-
import { join as
|
|
23374
|
-
function
|
|
23375
|
-
return
|
|
24635
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync17, unlinkSync as unlinkSync5, writeFileSync as writeFileSync19 } from "fs";
|
|
24636
|
+
import { join as join28 } from "path";
|
|
24637
|
+
function cachePath2() {
|
|
24638
|
+
return join28(ntrpHome(), "update-check.json");
|
|
23376
24639
|
}
|
|
23377
24640
|
function ensureDir7() {
|
|
23378
24641
|
const dir = ntrpHome();
|
|
23379
|
-
if (!
|
|
24642
|
+
if (!existsSync21(dir)) {
|
|
23380
24643
|
mkdirSync12(dir, { recursive: true });
|
|
23381
24644
|
}
|
|
23382
24645
|
}
|
|
23383
24646
|
function loadUpdateCheckCache() {
|
|
23384
|
-
const path =
|
|
23385
|
-
if (!
|
|
24647
|
+
const path = cachePath2();
|
|
24648
|
+
if (!existsSync21(path)) return null;
|
|
23386
24649
|
try {
|
|
23387
|
-
const parsed = JSON.parse(
|
|
24650
|
+
const parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
23388
24651
|
if (!parsed || typeof parsed !== "object" || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string") {
|
|
23389
24652
|
return null;
|
|
23390
24653
|
}
|
|
@@ -23395,39 +24658,39 @@ function loadUpdateCheckCache() {
|
|
|
23395
24658
|
}
|
|
23396
24659
|
function saveUpdateCheckCache(cache2) {
|
|
23397
24660
|
ensureDir7();
|
|
23398
|
-
|
|
24661
|
+
writeFileSync19(cachePath2(), JSON.stringify(cache2, null, 2) + "\n");
|
|
23399
24662
|
}
|
|
23400
|
-
function isCacheFresh(cache2, ttlMs =
|
|
24663
|
+
function isCacheFresh(cache2, ttlMs = CACHE_TTL_MS2) {
|
|
23401
24664
|
if (!cache2) return false;
|
|
23402
24665
|
return Date.now() - cache2.lastCheck < ttlMs;
|
|
23403
24666
|
}
|
|
23404
24667
|
function invalidateUpdateCheckCache() {
|
|
23405
|
-
const path =
|
|
23406
|
-
if (
|
|
24668
|
+
const path = cachePath2();
|
|
24669
|
+
if (existsSync21(path)) {
|
|
23407
24670
|
unlinkSync5(path);
|
|
23408
24671
|
}
|
|
23409
24672
|
}
|
|
23410
|
-
var
|
|
24673
|
+
var CACHE_TTL_MS2;
|
|
23411
24674
|
var init_update_check = __esm({
|
|
23412
24675
|
"src/config/update-check.ts"() {
|
|
23413
24676
|
"use strict";
|
|
23414
24677
|
init_store();
|
|
23415
|
-
|
|
24678
|
+
CACHE_TTL_MS2 = 864e5;
|
|
23416
24679
|
}
|
|
23417
24680
|
});
|
|
23418
24681
|
|
|
23419
24682
|
// src/version.ts
|
|
23420
|
-
import { existsSync as
|
|
23421
|
-
import { dirname as dirname4, join as
|
|
24683
|
+
import { existsSync as existsSync22, readFileSync as readFileSync18 } from "fs";
|
|
24684
|
+
import { dirname as dirname4, join as join29 } from "path";
|
|
23422
24685
|
import { fileURLToPath } from "url";
|
|
23423
24686
|
function getInstalledVersion() {
|
|
23424
24687
|
if (cachedVersion) return cachedVersion;
|
|
23425
24688
|
const start = dirname4(fileURLToPath(import.meta.url));
|
|
23426
24689
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
23427
|
-
const path =
|
|
23428
|
-
if (!
|
|
24690
|
+
const path = join29(start, rel);
|
|
24691
|
+
if (!existsSync22(path)) continue;
|
|
23429
24692
|
try {
|
|
23430
|
-
const pkg = JSON.parse(
|
|
24693
|
+
const pkg = JSON.parse(readFileSync18(path, "utf-8"));
|
|
23431
24694
|
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
23432
24695
|
cachedVersion = pkg.version;
|
|
23433
24696
|
return cachedVersion;
|
|
@@ -23493,14 +24756,14 @@ function buildResult(current, latest) {
|
|
|
23493
24756
|
async function checkForUpdate(options) {
|
|
23494
24757
|
const current = getInstalledVersion();
|
|
23495
24758
|
const timeoutMs = options?.timeoutMs ?? 5e3;
|
|
23496
|
-
const
|
|
23497
|
-
if (!options?.force && isCacheFresh(
|
|
23498
|
-
return buildResult(current,
|
|
24759
|
+
const cached2 = loadUpdateCheckCache();
|
|
24760
|
+
if (!options?.force && isCacheFresh(cached2)) {
|
|
24761
|
+
return buildResult(current, cached2.latestVersion);
|
|
23499
24762
|
}
|
|
23500
24763
|
const latest = await fetchLatestVersion(timeoutMs);
|
|
23501
24764
|
if (!latest) {
|
|
23502
|
-
if (
|
|
23503
|
-
return buildResult(current,
|
|
24765
|
+
if (cached2?.latestVersion) {
|
|
24766
|
+
return buildResult(current, cached2.latestVersion);
|
|
23504
24767
|
}
|
|
23505
24768
|
return null;
|
|
23506
24769
|
}
|
|
@@ -23521,10 +24784,10 @@ var init_registry = __esm({
|
|
|
23521
24784
|
// src/commands/update.ts
|
|
23522
24785
|
var update_exports = {};
|
|
23523
24786
|
__export(update_exports, {
|
|
23524
|
-
handler: () =>
|
|
24787
|
+
handler: () => handler44
|
|
23525
24788
|
});
|
|
23526
24789
|
import { spawnSync } from "child_process";
|
|
23527
|
-
import
|
|
24790
|
+
import chalk61 from "chalk";
|
|
23528
24791
|
function tailLines(text, count = 5) {
|
|
23529
24792
|
return text.split("\n").map((line) => line.trimEnd()).filter((line) => line.length > 0).slice(-count).join("\n");
|
|
23530
24793
|
}
|
|
@@ -23540,19 +24803,19 @@ function runGlobalInstall() {
|
|
|
23540
24803
|
const output = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
23541
24804
|
return { ok: result.status === 0, output };
|
|
23542
24805
|
}
|
|
23543
|
-
async function
|
|
24806
|
+
async function handler44(_args, _ctx) {
|
|
23544
24807
|
const current = getInstalledVersion();
|
|
23545
24808
|
const latest = await fetchLatestVersion(1e4);
|
|
23546
24809
|
if (!latest) {
|
|
23547
24810
|
console.log();
|
|
23548
|
-
console.log(
|
|
23549
|
-
console.log(
|
|
24811
|
+
console.log(chalk61.yellow(" Could not reach the npm registry."));
|
|
24812
|
+
console.log(chalk61.dim(` Try manually: npm install -g ${NPM_PACKAGE}`));
|
|
23550
24813
|
console.log();
|
|
23551
24814
|
return;
|
|
23552
24815
|
}
|
|
23553
24816
|
if (!isNewerVersion(latest, current)) {
|
|
23554
24817
|
console.log();
|
|
23555
|
-
console.log(
|
|
24818
|
+
console.log(chalk61.green(` \u2713 You're on the latest version (v${current})`));
|
|
23556
24819
|
console.log();
|
|
23557
24820
|
return;
|
|
23558
24821
|
}
|
|
@@ -23561,24 +24824,24 @@ async function handler43(_args, _ctx) {
|
|
|
23561
24824
|
const { ok, output } = runGlobalInstall();
|
|
23562
24825
|
if (ok) {
|
|
23563
24826
|
invalidateUpdateCheckCache();
|
|
23564
|
-
console.log(
|
|
24827
|
+
console.log(chalk61.green(` \u2713 Updated! Restart NTRP to use v${latest}`));
|
|
23565
24828
|
console.log();
|
|
23566
24829
|
return;
|
|
23567
24830
|
}
|
|
23568
24831
|
const lower = output.toLowerCase();
|
|
23569
24832
|
if (lower.includes("eacces") || lower.includes("permission denied") || lower.includes("eperm")) {
|
|
23570
|
-
console.log(
|
|
23571
|
-
console.log(
|
|
23572
|
-
console.log(
|
|
24833
|
+
console.log(chalk61.red(` Could not install ${NPM_PACKAGE} (permission denied).`));
|
|
24834
|
+
console.log(chalk61.dim(` Try: sudo npm install -g ${NPM_PACKAGE}`));
|
|
24835
|
+
console.log(chalk61.dim(` Or fix npm global permissions: ${PERMISSIONS_URL}`));
|
|
23573
24836
|
console.log();
|
|
23574
24837
|
return;
|
|
23575
24838
|
}
|
|
23576
24839
|
const detail = tailLines(output);
|
|
23577
|
-
console.log(
|
|
24840
|
+
console.log(chalk61.red(` Could not install ${NPM_PACKAGE}.`));
|
|
23578
24841
|
if (detail) {
|
|
23579
|
-
console.log(
|
|
24842
|
+
console.log(chalk61.dim(` ${detail.split("\n").join("\n ")}`));
|
|
23580
24843
|
}
|
|
23581
|
-
console.log(
|
|
24844
|
+
console.log(chalk61.dim(` Try manually: npm install -g ${NPM_PACKAGE}`));
|
|
23582
24845
|
console.log();
|
|
23583
24846
|
}
|
|
23584
24847
|
var PERMISSIONS_URL;
|
|
@@ -23593,10 +24856,10 @@ var init_update = __esm({
|
|
|
23593
24856
|
});
|
|
23594
24857
|
|
|
23595
24858
|
// src/output/progress-report.ts
|
|
23596
|
-
import
|
|
24859
|
+
import chalk62 from "chalk";
|
|
23597
24860
|
function printCard(title, rows) {
|
|
23598
24861
|
const inner = CARD_W - 4;
|
|
23599
|
-
const border =
|
|
24862
|
+
const border = chalk62.dim;
|
|
23600
24863
|
console.log();
|
|
23601
24864
|
console.log(` ${border(`\u256D${"\u2500".repeat(CARD_W - 2)}\u256E`)}`);
|
|
23602
24865
|
console.log(` ${border("\u2502 ")}${padRight(sectionHeading(title), inner)}${border(" \u2502")}`);
|
|
@@ -23612,7 +24875,7 @@ function formatTokens(n) {
|
|
|
23612
24875
|
return String(n);
|
|
23613
24876
|
}
|
|
23614
24877
|
function sparkline(values) {
|
|
23615
|
-
if (values.length === 0) return
|
|
24878
|
+
if (values.length === 0) return chalk62.dim("(no activity yet)");
|
|
23616
24879
|
const max = Math.max(...values, 1);
|
|
23617
24880
|
const blocks = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
23618
24881
|
return values.map((v) => {
|
|
@@ -23621,7 +24884,7 @@ function sparkline(values) {
|
|
|
23621
24884
|
}).join("");
|
|
23622
24885
|
}
|
|
23623
24886
|
function formatMemberSince(iso) {
|
|
23624
|
-
if (!iso) return
|
|
24887
|
+
if (!iso) return chalk62.dim("\u2014");
|
|
23625
24888
|
const d = new Date(iso);
|
|
23626
24889
|
return d.toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" });
|
|
23627
24890
|
}
|
|
@@ -23638,49 +24901,49 @@ function renderProgressReport() {
|
|
|
23638
24901
|
state.milestones_unlocked.length,
|
|
23639
24902
|
TIME_MILESTONES.length
|
|
23640
24903
|
);
|
|
23641
|
-
const { usage:
|
|
24904
|
+
const { usage: usage3 } = summary;
|
|
23642
24905
|
const nextLabel = bank.next_milestone ? `${formatHoursLabel(bank.total_hours)} \u2192 ${formatHoursLabel(bank.next_milestone.hours)}` : `${formatHoursLabel(bank.total_hours)} saved`;
|
|
23643
24906
|
const bar = inlineBar(bank.progress_pct, 18);
|
|
23644
24907
|
printCard("Progress", [
|
|
23645
|
-
`${
|
|
23646
|
-
`${
|
|
23647
|
-
`${
|
|
23648
|
-
`${
|
|
24908
|
+
`${chalk62.dim("Hours saved")} ${paint("accent", formatHoursLabel(bank.total_hours))} ${bar}`,
|
|
24909
|
+
`${chalk62.dim("Next milestone")} ${bank.next_milestone ? paint("accent", bank.next_milestone.title) : chalk62.dim("top of ladder")}`,
|
|
24910
|
+
`${chalk62.dim("Member since")} ${formatMemberSince(usage3.first_active_at)}`,
|
|
24911
|
+
`${chalk62.dim("Last active")} ${formatMemberSince(usage3.last_active_at)}`
|
|
23649
24912
|
]);
|
|
23650
24913
|
if (bank.perspective_line) {
|
|
23651
|
-
console.log(` ${
|
|
24914
|
+
console.log(` ${chalk62.dim.italic(bank.perspective_line)}`);
|
|
23652
24915
|
}
|
|
23653
24916
|
printCard("Activity", [
|
|
23654
|
-
`${
|
|
23655
|
-
`${
|
|
23656
|
-
`${
|
|
23657
|
-
`${
|
|
23658
|
-
`${
|
|
24917
|
+
`${chalk62.dim("Sessions")} ${chalk62.bold(String(summary.total_sessions_on_disk))} total \xB7 ${summary.sessions_with_work} with work \xB7 ${usage3.sessions_closed} closed`,
|
|
24918
|
+
`${chalk62.dim("Diagnoses")} ${chalk62.bold(String(usage3.diagnoses))}`,
|
|
24919
|
+
`${chalk62.dim("Metrics runs")} ${chalk62.bold(String(usage3.metrics_runs))}`,
|
|
24920
|
+
`${chalk62.dim("Deliverables")} ${chalk62.bold(String(usage3.deliverables))}`,
|
|
24921
|
+
`${chalk62.dim("AI exchanges")} ${chalk62.bold(String(usage3.nl_exchanges))}`
|
|
23659
24922
|
]);
|
|
23660
|
-
const totalTokens =
|
|
24923
|
+
const totalTokens = usage3.input_tokens + usage3.output_tokens;
|
|
23661
24924
|
printCard("AI usage", [
|
|
23662
|
-
`${
|
|
23663
|
-
`${
|
|
24925
|
+
`${chalk62.dim("LLM calls")} ${chalk62.bold(String(usage3.llm_calls))}`,
|
|
24926
|
+
`${chalk62.dim("Tokens")} ${chalk62.bold(formatTokens(totalTokens))} in+out (${formatTokens(usage3.input_tokens)} in \xB7 ${formatTokens(usage3.output_tokens)} out)`
|
|
23664
24927
|
]);
|
|
23665
|
-
const weeks = [...
|
|
24928
|
+
const weeks = [...usage3.weekly].sort((a, b) => a.week.localeCompare(b.week)).slice(-8);
|
|
23666
24929
|
const weekHours = weeks.map((w) => w.minutes_saved / 60);
|
|
23667
24930
|
const weekLabels = weeks.map((w) => w.week.replace(/^\d{4}-/, ""));
|
|
23668
24931
|
console.log();
|
|
23669
24932
|
console.log(` ${sectionHeading("Weekly hours saved")}`);
|
|
23670
24933
|
console.log(` ${sparkline(weekHours)}`);
|
|
23671
24934
|
if (weeks.length > 0) {
|
|
23672
|
-
console.log(` ${
|
|
24935
|
+
console.log(` ${chalk62.dim(weekLabels.join(" "))}`);
|
|
23673
24936
|
}
|
|
23674
24937
|
console.log();
|
|
23675
24938
|
console.log(` ${sectionHeading("Milestone ladder")}`);
|
|
23676
24939
|
for (const m of TIME_MILESTONES) {
|
|
23677
24940
|
const unlocked = state.milestones_unlocked.includes(m.id);
|
|
23678
24941
|
const pct = Math.min(100, bank.total_hours / m.hours * 100);
|
|
23679
|
-
const mark = unlocked ? badge("DONE", "success") : bank.total_hours >= m.hours * 0.85 ? badge("NEAR", "warning") :
|
|
24942
|
+
const mark = unlocked ? badge("DONE", "success") : bank.total_hours >= m.hours * 0.85 ? badge("NEAR", "warning") : chalk62.dim("\u25CB");
|
|
23680
24943
|
const barW = 12;
|
|
23681
|
-
const mBar = unlocked ?
|
|
24944
|
+
const mBar = unlocked ? chalk62.hex("#22c55e")("\u2588".repeat(barW)) : scoreBar(pct, bank.total_hours >= m.hours ? "green" : pct >= 50 ? "yellow" : "red", barW);
|
|
23682
24945
|
const label = `${m.title}`.padEnd(16);
|
|
23683
|
-
console.log(` ${mark} ${
|
|
24946
|
+
console.log(` ${mark} ${chalk62.dim(label)} ${mBar} ${chalk62.dim(`${m.hours}h`)}`);
|
|
23684
24947
|
}
|
|
23685
24948
|
console.log();
|
|
23686
24949
|
}
|
|
@@ -23702,17 +24965,17 @@ var init_progress_report = __esm({
|
|
|
23702
24965
|
// src/commands/progress.ts
|
|
23703
24966
|
var progress_exports = {};
|
|
23704
24967
|
__export(progress_exports, {
|
|
23705
|
-
handler: () =>
|
|
24968
|
+
handler: () => handler45
|
|
23706
24969
|
});
|
|
23707
|
-
import
|
|
24970
|
+
import chalk63 from "chalk";
|
|
23708
24971
|
function printProgressResetPreamble() {
|
|
23709
24972
|
console.log();
|
|
23710
|
-
console.log(" " +
|
|
23711
|
-
console.log(" " +
|
|
23712
|
-
console.log(" " +
|
|
23713
|
-
console.log(" " +
|
|
24973
|
+
console.log(" " + chalk63.yellow.bold("This will permanently remove:"));
|
|
24974
|
+
console.log(" " + chalk63.dim(" \u2022 Hours saved and milestone unlocks"));
|
|
24975
|
+
console.log(" " + chalk63.dim(" \u2022 Usage counters and weekly activity rollups"));
|
|
24976
|
+
console.log(" " + chalk63.dim(" \u2022 Credit history used for dedup"));
|
|
23714
24977
|
console.log();
|
|
23715
|
-
console.log(" " +
|
|
24978
|
+
console.log(" " + chalk63.dim("Preserved: install identity (install.json)"));
|
|
23716
24979
|
console.log();
|
|
23717
24980
|
}
|
|
23718
24981
|
function showProgress() {
|
|
@@ -23730,7 +24993,7 @@ async function handleReset(ctx, confirmedFlag) {
|
|
|
23730
24993
|
const bank = getTimeBankSummary();
|
|
23731
24994
|
if (bank.total_minutes <= 0) {
|
|
23732
24995
|
console.log();
|
|
23733
|
-
console.log(" " +
|
|
24996
|
+
console.log(" " + chalk63.dim("No progress to reset."));
|
|
23734
24997
|
console.log();
|
|
23735
24998
|
return "No progress to reset";
|
|
23736
24999
|
}
|
|
@@ -23747,11 +25010,11 @@ async function handleReset(ctx, confirmedFlag) {
|
|
|
23747
25010
|
}
|
|
23748
25011
|
resetProgress();
|
|
23749
25012
|
console.log();
|
|
23750
|
-
console.log(" " + paint("accent", "\u2713 Progress reset") +
|
|
25013
|
+
console.log(" " + paint("accent", "\u2713 Progress reset") + chalk63.dim(" \u2014 hours and milestones cleared."));
|
|
23751
25014
|
console.log();
|
|
23752
25015
|
return "Progress reset";
|
|
23753
25016
|
}
|
|
23754
|
-
async function
|
|
25017
|
+
async function handler45(args, ctx) {
|
|
23755
25018
|
const { positional, flags } = parseArgs2(args, ["confirm"]);
|
|
23756
25019
|
const sub = positional[0]?.toLowerCase();
|
|
23757
25020
|
if (sub === "reset") {
|
|
@@ -23759,7 +25022,7 @@ async function handler44(args, ctx) {
|
|
|
23759
25022
|
}
|
|
23760
25023
|
if (sub && sub !== "reset") {
|
|
23761
25024
|
console.log();
|
|
23762
|
-
console.log(" " +
|
|
25025
|
+
console.log(" " + chalk63.dim("Unknown subcommand. Try ") + paint("accent", "/progress") + chalk63.dim(" or ") + paint("accent", "/progress reset") + chalk63.dim("."));
|
|
23763
25026
|
console.log();
|
|
23764
25027
|
return;
|
|
23765
25028
|
}
|
|
@@ -23866,10 +25129,10 @@ async function resolveHandler(name) {
|
|
|
23866
25129
|
try {
|
|
23867
25130
|
const mod = await importHandler(runtimePath);
|
|
23868
25131
|
if (!mod) return null;
|
|
23869
|
-
const
|
|
23870
|
-
if (typeof
|
|
23871
|
-
entry.handler =
|
|
23872
|
-
return
|
|
25132
|
+
const handler46 = mod.handler;
|
|
25133
|
+
if (typeof handler46 !== "function") return null;
|
|
25134
|
+
entry.handler = handler46;
|
|
25135
|
+
return handler46;
|
|
23873
25136
|
} catch (err) {
|
|
23874
25137
|
console.error(`Failed to load handler for /${name}:`, err);
|
|
23875
25138
|
return null;
|
|
@@ -23955,6 +25218,8 @@ async function importHandler(runtimePath) {
|
|
|
23955
25218
|
return Promise.resolve().then(() => (init_switch(), switch_exports));
|
|
23956
25219
|
case "../commands/backmeup.js":
|
|
23957
25220
|
return Promise.resolve().then(() => (init_backmeup(), backmeup_exports));
|
|
25221
|
+
case "../commands/connect.js":
|
|
25222
|
+
return Promise.resolve().then(() => (init_connect2(), connect_exports2));
|
|
23958
25223
|
case "../commands/provider.js":
|
|
23959
25224
|
return Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
23960
25225
|
case "../commands/tier.js":
|
|
@@ -24082,7 +25347,9 @@ handler: ../commands/setup.ts
|
|
|
24082
25347
|
|
|
24083
25348
|
Validate local readiness or configure NTRP non-interactively for automation.
|
|
24084
25349
|
\`setup check --json\` reports license, profile, API key, database, and writable
|
|
24085
|
-
directory state. \`setup agent\` accepts a profile JSON file or direct flags
|
|
25350
|
+
directory state. \`setup agent\` accepts a profile JSON file or direct flags \u2014
|
|
25351
|
+
\`--llm-key <key>\` auto-detects the provider from any pasted key
|
|
25352
|
+
(\`--llm-provider <id>\` to force one).`
|
|
24086
25353
|
},
|
|
24087
25354
|
{
|
|
24088
25355
|
name: "update",
|
|
@@ -24518,6 +25785,25 @@ handler: ../commands/profile.ts
|
|
|
24518
25785
|
|
|
24519
25786
|
Choose a sales motion preset (PLG, SMB Velocity, Mid-Market, Enterprise). Each
|
|
24520
25787
|
preset adjusts the vital-sign thresholds to match your deal cycle.`
|
|
25788
|
+
},
|
|
25789
|
+
{
|
|
25790
|
+
name: "connect",
|
|
25791
|
+
raw: `---
|
|
25792
|
+
name: connect
|
|
25793
|
+
description: Connect an AI provider (paste any key)
|
|
25794
|
+
section: Settings
|
|
25795
|
+
args: [provider] [--key <key>] [--base-url <url> --id <name>]
|
|
25796
|
+
handler: ../commands/connect.ts
|
|
25797
|
+
---
|
|
25798
|
+
|
|
25799
|
+
Paste any provider's API key \u2014 NTRP identifies the provider from the key
|
|
25800
|
+
format (probing ambiguous ones), validates it, discovers which models the key
|
|
25801
|
+
can use, and builds the HIGH/MEDIUM/LOW tier stack automatically.
|
|
25802
|
+
|
|
25803
|
+
Works with Anthropic, OpenAI, Google Gemini, Groq, Mistral, DeepSeek, xAI,
|
|
25804
|
+
OpenRouter, Together, and Fireworks out of the box. \`/connect ollama\` wires a
|
|
25805
|
+
local Ollama; \`/connect --base-url <url> --id <name>\` registers any other
|
|
25806
|
+
OpenAI-compatible endpoint.`
|
|
24521
25807
|
},
|
|
24522
25808
|
{
|
|
24523
25809
|
name: "config",
|
|
@@ -24530,10 +25816,12 @@ handler: ../commands/config.ts
|
|
|
24530
25816
|
---
|
|
24531
25817
|
|
|
24532
25818
|
Manage CLI configuration stored at \`~/.ntrp/config.json\`. Useful keys:
|
|
24533
|
-
\`api-key\` (Anthropic), \`openai-api-key\`, \`
|
|
24534
|
-
\`llm-tier\`, \`llm-auto-failover\`,
|
|
25819
|
+
\`api-key\` (Anthropic), \`openai-api-key\` (and \`groq-api-key\`, \`google-api-key\`, ...),
|
|
25820
|
+
\`llm-primary\` (default engine), \`llm-tier\`, \`llm-auto-failover\`,
|
|
25821
|
+
\`default-format\`, \`export-dir\`.
|
|
24535
25822
|
|
|
24536
|
-
|
|
25823
|
+
Setting a provider key opens a hidden prompt and auto-discovers that
|
|
25824
|
+
provider's models. Prefer \`/connect\` \u2014 it detects the provider for you.`
|
|
24537
25825
|
},
|
|
24538
25826
|
{
|
|
24539
25827
|
name: "provider",
|
|
@@ -24541,13 +25829,14 @@ Manage CLI configuration stored at \`~/.ntrp/config.json\`. Useful keys:
|
|
|
24541
25829
|
name: provider
|
|
24542
25830
|
description: Switch active LLM engine
|
|
24543
25831
|
section: Settings
|
|
24544
|
-
args: [
|
|
25832
|
+
args: [<id>|list|reset|save|failover on|off]
|
|
24545
25833
|
handler: ../commands/provider.ts
|
|
24546
25834
|
---
|
|
24547
25835
|
|
|
24548
|
-
Choose which engine answers this session \u2014
|
|
24549
|
-
|
|
24550
|
-
|
|
25836
|
+
Choose which connected engine answers this session \u2014 any provider added via
|
|
25837
|
+
\`/connect\` (anthropic, openai, groq, google, ollama, custom endpoints, ...).
|
|
25838
|
+
Session-scoped by default; \`/provider save\` writes the default to config.
|
|
25839
|
+
\`/provider failover on\` enables rate-limit auto-failover.`
|
|
24551
25840
|
},
|
|
24552
25841
|
{
|
|
24553
25842
|
name: "tier",
|
|
@@ -24569,12 +25858,14 @@ active stack. Add \`--default\` to persist to config.`
|
|
|
24569
25858
|
name: model
|
|
24570
25859
|
description: Override the active LLM model
|
|
24571
25860
|
section: Settings
|
|
24572
|
-
args: [set <id>|clear] [--default]
|
|
25861
|
+
args: [list|set <id>|refresh|clear] [--default]
|
|
24573
25862
|
handler: ../commands/model.ts
|
|
24574
25863
|
---
|
|
24575
25864
|
|
|
24576
|
-
|
|
24577
|
-
|
|
25865
|
+
\`/model list\` shows the models discovered for the active engine with their
|
|
25866
|
+
tier assignments. \`/model refresh\` re-discovers the live list. \`/model set <id>\`
|
|
25867
|
+
pins a model on the **active engine**; cross-provider IDs are rejected \u2014
|
|
25868
|
+
switch with \`/provider\` first.`
|
|
24578
25869
|
},
|
|
24579
25870
|
{
|
|
24580
25871
|
name: "activate",
|
|
@@ -24633,7 +25924,7 @@ paragraph that flows into all AI surfaces.`
|
|
|
24633
25924
|
});
|
|
24634
25925
|
|
|
24635
25926
|
// src/license/activation.ts
|
|
24636
|
-
import
|
|
25927
|
+
import chalk64 from "chalk";
|
|
24637
25928
|
function hasValidLicense() {
|
|
24638
25929
|
return checkLicense().valid;
|
|
24639
25930
|
}
|
|
@@ -24641,10 +25932,10 @@ async function ensureLicenseActivated(ctx) {
|
|
|
24641
25932
|
if (hasValidLicense()) return false;
|
|
24642
25933
|
if (!process.stdin.isTTY) {
|
|
24643
25934
|
console.error();
|
|
24644
|
-
console.error(
|
|
24645
|
-
console.error(
|
|
24646
|
-
console.error(
|
|
24647
|
-
console.error(
|
|
25935
|
+
console.error(chalk64.red(" A license key is required."));
|
|
25936
|
+
console.error(chalk64.dim(` Sign up: ${getCheckoutUrl()}`));
|
|
25937
|
+
console.error(chalk64.dim(" Then run: ntrp activate <key>"));
|
|
25938
|
+
console.error(chalk64.dim(" Or set NTRP_LICENSE_KEY for headless use."));
|
|
24648
25939
|
console.error();
|
|
24649
25940
|
process.exit(1);
|
|
24650
25941
|
}
|
|
@@ -24654,7 +25945,7 @@ async function ensureLicenseActivated(ctx) {
|
|
|
24654
25945
|
}
|
|
24655
25946
|
printCenteredLogo();
|
|
24656
25947
|
console.log(" " + bold("Activate your license"));
|
|
24657
|
-
console.log(" " +
|
|
25948
|
+
console.log(" " + chalk64.dim("Don't have a key yet? Sign up (free trial or Pro), then paste it below."));
|
|
24658
25949
|
console.log();
|
|
24659
25950
|
await promptOpenCheckout(ctx);
|
|
24660
25951
|
return promptForLicenseKey(ctx);
|
|
@@ -24681,6 +25972,7 @@ var init_gate2 = __esm({
|
|
|
24681
25972
|
UNGATED_COMMANDS = /* @__PURE__ */ new Set([
|
|
24682
25973
|
"activate",
|
|
24683
25974
|
"config",
|
|
25975
|
+
"connect",
|
|
24684
25976
|
"profile",
|
|
24685
25977
|
"onboard",
|
|
24686
25978
|
"setup",
|
|
@@ -24705,7 +25997,7 @@ var router_exports = {};
|
|
|
24705
25997
|
__export(router_exports, {
|
|
24706
25998
|
conversationRouter: () => conversationRouter
|
|
24707
25999
|
});
|
|
24708
|
-
import
|
|
26000
|
+
import chalk65 from "chalk";
|
|
24709
26001
|
async function conversationRouter(input, ctx) {
|
|
24710
26002
|
if (ctx.oneShot || (ctx.wizardDepth ?? 0) > 0) {
|
|
24711
26003
|
return { handled: false };
|
|
@@ -24715,7 +26007,7 @@ async function conversationRouter(input, ctx) {
|
|
|
24715
26007
|
if (FRESH_START_RE.test(line)) {
|
|
24716
26008
|
console.log();
|
|
24717
26009
|
console.log(
|
|
24718
|
-
" " +
|
|
26010
|
+
" " + chalk65.dim("Start a fresh analysis? This keeps prior sessions \u2014 say ") + chalk65.cyan("yes") + chalk65.dim(" to confirm or ") + chalk65.cyan("/home") + chalk65.dim(" for the dashboard.")
|
|
24719
26011
|
);
|
|
24720
26012
|
console.log();
|
|
24721
26013
|
return { handled: true };
|
|
@@ -24749,7 +26041,7 @@ async function conversationRouter(input, ctx) {
|
|
|
24749
26041
|
}
|
|
24750
26042
|
if (phase === "compute") {
|
|
24751
26043
|
console.log();
|
|
24752
|
-
console.log(" " +
|
|
26044
|
+
console.log(" " + chalk65.dim("Analysis running \u2014 wait for it to finish before typing another question."));
|
|
24753
26045
|
console.log();
|
|
24754
26046
|
return { handled: true };
|
|
24755
26047
|
}
|
|
@@ -24789,7 +26081,7 @@ var init_router = __esm({
|
|
|
24789
26081
|
});
|
|
24790
26082
|
|
|
24791
26083
|
// src/cli/dispatch.ts
|
|
24792
|
-
import
|
|
26084
|
+
import chalk66 from "chalk";
|
|
24793
26085
|
function printLicenseRequired(command) {
|
|
24794
26086
|
printLicenseBlocked(command);
|
|
24795
26087
|
}
|
|
@@ -24855,7 +26147,7 @@ async function dispatch(input, ctx) {
|
|
|
24855
26147
|
if (tokens.length === 1) {
|
|
24856
26148
|
if (/^\d$/.test(first)) {
|
|
24857
26149
|
console.log(
|
|
24858
|
-
" " +
|
|
26150
|
+
" " + chalk66.dim("Looks like a menu pick \u2014 run ") + paint("accent", "/new") + chalk66.dim(" to start (pick Demo, then choose your analysis type).")
|
|
24859
26151
|
);
|
|
24860
26152
|
return { kind: "handled" };
|
|
24861
26153
|
}
|
|
@@ -24878,22 +26170,22 @@ async function dispatch(input, ctx) {
|
|
|
24878
26170
|
return { kind: "handled", summary };
|
|
24879
26171
|
}
|
|
24880
26172
|
console.log(
|
|
24881
|
-
" " +
|
|
26173
|
+
" " + chalk66.dim("Not in Q&A yet \u2014 confirm scope, load data, and run analysis first. Type ") + paint("accent", "/home") + chalk66.dim(" for status.")
|
|
24882
26174
|
);
|
|
24883
26175
|
return { kind: "handled" };
|
|
24884
26176
|
}
|
|
24885
26177
|
console.log(
|
|
24886
|
-
" " +
|
|
26178
|
+
" " + chalk66.dim("Natural-language questions run in the interactive REPL. Start with ") + paint("accent", "ntrp") + chalk66.dim(" and ask after analysis.")
|
|
24887
26179
|
);
|
|
24888
26180
|
return { kind: "handled" };
|
|
24889
26181
|
}
|
|
24890
26182
|
async function runSlashCommand(name, args, ctx) {
|
|
24891
|
-
const
|
|
24892
|
-
if (!
|
|
24893
|
-
console.error(
|
|
26183
|
+
const handler46 = await resolveHandler(name);
|
|
26184
|
+
if (!handler46) {
|
|
26185
|
+
console.error(chalk66.red(` Unknown command: /${name}`));
|
|
24894
26186
|
return void 0;
|
|
24895
26187
|
}
|
|
24896
|
-
const result = await
|
|
26188
|
+
const result = await handler46(args, ctx);
|
|
24897
26189
|
return result ?? void 0;
|
|
24898
26190
|
}
|
|
24899
26191
|
async function runNaturalLanguage2(input, ctx) {
|
|
@@ -24922,7 +26214,7 @@ __export(welcome_exports, {
|
|
|
24922
26214
|
GRADIENT: () => GRADIENT,
|
|
24923
26215
|
printWelcome: () => printWelcome
|
|
24924
26216
|
});
|
|
24925
|
-
import
|
|
26217
|
+
import chalk67 from "chalk";
|
|
24926
26218
|
function resolveSessionSummary(input) {
|
|
24927
26219
|
if (input.scope?.intent_summary?.trim()) return input.scope.intent_summary.trim();
|
|
24928
26220
|
if (input.summary?.trim()) return input.summary.trim();
|
|
@@ -24956,19 +26248,19 @@ function sessionSummaryText(s) {
|
|
|
24956
26248
|
summary: s.summary,
|
|
24957
26249
|
dataset: s.dataset
|
|
24958
26250
|
});
|
|
24959
|
-
return summary === NO_SUMMARY ?
|
|
26251
|
+
return summary === NO_SUMMARY ? chalk67.dim(summary) : summary;
|
|
24960
26252
|
}
|
|
24961
26253
|
function formatLastSessionLine(s, colW, ctx, opts) {
|
|
24962
26254
|
const phase = sessionPhaseLabel(s, ctx);
|
|
24963
|
-
const current = opts?.markCurrent && s.id === ctx?.sessionId ?
|
|
24964
|
-
const meta = `${formatSessionId(s.id, s.name)} ${
|
|
26255
|
+
const current = opts?.markCurrent && s.id === ctx?.sessionId ? chalk67.dim(" \xB7 current") : "";
|
|
26256
|
+
const meta = `${formatSessionId(s.id, s.name)} ${chalk67.dim("\xB7")} ${chalk67.dim(lensBadgeLabel(s.analysis))} ${chalk67.dim("\xB7")} ${paint("accent", phase)} ${chalk67.dim("\xB7")} ${sessionSummaryText(s)}${current}`;
|
|
24965
26257
|
return truncateVisible(` ${meta}`, colW);
|
|
24966
26258
|
}
|
|
24967
26259
|
function formatActiveSessionLine(s, colW, ctx, opts) {
|
|
24968
26260
|
const indent = " ";
|
|
24969
26261
|
const idPart = formatSessionId(s.id, s.name);
|
|
24970
|
-
const status =
|
|
24971
|
-
const current = opts?.markCurrent && s.id === ctx?.sessionId ?
|
|
26262
|
+
const status = chalk67.dim(` \xB7 ${sessionStatusSuffix(s)}`);
|
|
26263
|
+
const current = opts?.markCurrent && s.id === ctx?.sessionId ? chalk67.dim(" \xB7 current") : "";
|
|
24972
26264
|
const suffix = `${status}${current}`;
|
|
24973
26265
|
const summaryBudget = Math.max(8, colW - visibleWidth(indent) - visibleWidth(idPart) - visibleWidth(suffix) - 2);
|
|
24974
26266
|
const summaryPart = truncateVisible(sessionSummaryText(s), summaryBudget);
|
|
@@ -25005,13 +26297,13 @@ function buildSystemLines(colW, statusRows, recent) {
|
|
|
25005
26297
|
const lines = [""];
|
|
25006
26298
|
lines.push(sectionHeading("System"));
|
|
25007
26299
|
for (const item of statusRows) {
|
|
25008
|
-
const label =
|
|
26300
|
+
const label = chalk67.dim(padRight(item.label, 8));
|
|
25009
26301
|
const state = padRight(item.state, 10);
|
|
25010
26302
|
const detailW = Math.max(1, colW - 21);
|
|
25011
|
-
lines.push(`${label} ${state} ${
|
|
26303
|
+
lines.push(`${label} ${state} ${chalk67.dim(truncateVisible(item.detail, detailW))}`);
|
|
25012
26304
|
}
|
|
25013
26305
|
if (recent) {
|
|
25014
|
-
lines.push(`${
|
|
26306
|
+
lines.push(`${chalk67.dim(padRight("last used", 8))} ${chalk67.dim(recent)}`);
|
|
25015
26307
|
}
|
|
25016
26308
|
return lines;
|
|
25017
26309
|
}
|
|
@@ -25021,7 +26313,7 @@ function buildHelpLines(colW, unfinishedCount) {
|
|
|
25021
26313
|
lines.push(sectionHeading(section.heading));
|
|
25022
26314
|
for (const entry of section.entries) {
|
|
25023
26315
|
const desc = entry.dynamicDescription ? entry.dynamicDescription(unfinishedCount) : entry.description;
|
|
25024
|
-
const text = ` ${paint("accent", entry.command)} ${
|
|
26316
|
+
const text = ` ${paint("accent", entry.command)} ${chalk67.dim(desc)}`;
|
|
25025
26317
|
lines.push(truncateVisible(text, colW));
|
|
25026
26318
|
}
|
|
25027
26319
|
}
|
|
@@ -25031,10 +26323,10 @@ function buildLastSessionLines(colW, ctx, lastSession, nextAction, emptyDataHint
|
|
|
25031
26323
|
const lines = [""];
|
|
25032
26324
|
lines.push(sectionHeading("Last Session"));
|
|
25033
26325
|
if (!lastSession) {
|
|
25034
|
-
lines.push(` ${
|
|
26326
|
+
lines.push(` ${chalk67.dim("(none yet)")}`);
|
|
25035
26327
|
lines.push(
|
|
25036
26328
|
truncateVisible(
|
|
25037
|
-
` ${nextAction.command ? actionHint(nextAction.label, nextAction.command, nextAction.detail) : `${
|
|
26329
|
+
` ${nextAction.command ? actionHint(nextAction.label, nextAction.command, nextAction.detail) : `${chalk67.dim(nextAction.label)} ${chalk67.dim(nextAction.detail)}`}`,
|
|
25038
26330
|
colW
|
|
25039
26331
|
)
|
|
25040
26332
|
);
|
|
@@ -25045,7 +26337,7 @@ function buildLastSessionLines(colW, ctx, lastSession, nextAction, emptyDataHint
|
|
|
25045
26337
|
if (!isCurrent) {
|
|
25046
26338
|
lines.push(
|
|
25047
26339
|
truncateVisible(
|
|
25048
|
-
` ${
|
|
26340
|
+
` ${chalk67.dim("Resume:")} ${paint("accent", `/session ${lastSession.id.slice(-4)}`)}`,
|
|
25049
26341
|
colW
|
|
25050
26342
|
)
|
|
25051
26343
|
);
|
|
@@ -25054,7 +26346,7 @@ function buildLastSessionLines(colW, ctx, lastSession, nextAction, emptyDataHint
|
|
|
25054
26346
|
truncateVisible(` ${actionHint(nextAction.label, nextAction.command, nextAction.detail)}`, colW)
|
|
25055
26347
|
);
|
|
25056
26348
|
} else {
|
|
25057
|
-
lines.push(truncateVisible(` ${
|
|
26349
|
+
lines.push(truncateVisible(` ${chalk67.dim(nextAction.label)} ${chalk67.dim(nextAction.detail)}`, colW));
|
|
25058
26350
|
}
|
|
25059
26351
|
if (isCurrent && emptyDataHint) {
|
|
25060
26352
|
lines.push(truncateVisible(` ${emptyDataHint}`, colW));
|
|
@@ -25065,14 +26357,14 @@ function buildActiveSessionsLines(colW, ctx, activeSessions) {
|
|
|
25065
26357
|
const lines = [""];
|
|
25066
26358
|
lines.push(sectionHeading("Active Sessions"));
|
|
25067
26359
|
if (activeSessions.length === 0) {
|
|
25068
|
-
lines.push(` ${
|
|
26360
|
+
lines.push(` ${chalk67.dim("(none in progress)")}`);
|
|
25069
26361
|
return lines;
|
|
25070
26362
|
}
|
|
25071
26363
|
for (const s of activeSessions.slice(0, 5)) {
|
|
25072
26364
|
lines.push(formatActiveSessionLine(s, colW, ctx, { markCurrent: true }));
|
|
25073
26365
|
}
|
|
25074
26366
|
if (activeSessions.length > 5) {
|
|
25075
|
-
lines.push(` ${
|
|
26367
|
+
lines.push(` ${chalk67.dim(`+${activeSessions.length - 5} more \xB7 `)}${paint("accent", "/session")}`);
|
|
25076
26368
|
}
|
|
25077
26369
|
return lines;
|
|
25078
26370
|
}
|
|
@@ -25080,7 +26372,7 @@ function buildProgressLines(colW) {
|
|
|
25080
26372
|
const summary = getTimeBankSummary();
|
|
25081
26373
|
const lines = [""];
|
|
25082
26374
|
const heading = sectionHeading("Progress");
|
|
25083
|
-
const hint = `${paint("accent", "/progress")}${
|
|
26375
|
+
const hint = `${paint("accent", "/progress")}${chalk67.dim(" for usage metrics")}`;
|
|
25084
26376
|
const gap = colW - visibleWidth(heading) - visibleWidth(hint);
|
|
25085
26377
|
if (gap > 2) {
|
|
25086
26378
|
lines.push(truncateVisible(`${heading}${" ".repeat(gap)}${hint}`, colW));
|
|
@@ -25090,7 +26382,7 @@ function buildProgressLines(colW) {
|
|
|
25090
26382
|
if (summary.total_minutes <= 0) {
|
|
25091
26383
|
lines.push(
|
|
25092
26384
|
truncateVisible(
|
|
25093
|
-
` ${
|
|
26385
|
+
` ${chalk67.dim("Run /diagnose or ask a question to start banking hours.")}`,
|
|
25094
26386
|
colW
|
|
25095
26387
|
)
|
|
25096
26388
|
);
|
|
@@ -25101,7 +26393,7 @@ function buildProgressLines(colW) {
|
|
|
25101
26393
|
const bar = inlineBar(summary.progress_pct, 16);
|
|
25102
26394
|
lines.push(truncateVisible(` ${bar} ${nextLabel}`, colW));
|
|
25103
26395
|
if (summary.perspective_line) {
|
|
25104
|
-
lines.push(truncateVisible(` ${
|
|
26396
|
+
lines.push(truncateVisible(` ${chalk67.dim.italic(summary.perspective_line)}`, colW));
|
|
25105
26397
|
}
|
|
25106
26398
|
return lines;
|
|
25107
26399
|
}
|
|
@@ -25111,7 +26403,7 @@ async function printWelcome(ctx, version) {
|
|
|
25111
26403
|
const innerW = cardW - 2;
|
|
25112
26404
|
const contentW = innerW - 2;
|
|
25113
26405
|
const outerPad = " ".repeat(Math.max(0, Math.floor((width - cardW) / 2)));
|
|
25114
|
-
const border = (ch) =>
|
|
26406
|
+
const border = (ch) => chalk67.dim(ch);
|
|
25115
26407
|
const push = (line) => console.log(outerPad + line);
|
|
25116
26408
|
const fitCell = (content, width2) => {
|
|
25117
26409
|
if (visibleWidth(content) > width2) return truncateVisible(content, width2);
|
|
@@ -25149,11 +26441,9 @@ async function printWelcome(ctx, version) {
|
|
|
25149
26441
|
(s) => s.id !== ctx.sessionId && (s.exchange_count > 0 || s.stage === "analyzed" || s.stage === "delivered" || !!s.dataset?.label)
|
|
25150
26442
|
);
|
|
25151
26443
|
const datasetDetail = hasData ? ctx.dataset?.label ?? countStr : savedSessions.length > 0 ? `none loaded \xB7 ${savedSessions.length} saved` : "none loaded";
|
|
25152
|
-
const { describeLlmReadiness: describeLlmReadiness2 } = await Promise.resolve().then(() => (init_repl_api(), repl_api_exports));
|
|
25153
26444
|
const { countAvailableEngines: countAvailableEngines2, formatActiveStack: formatActiveStack2, availableEngineLabels: availableEngineLabels2 } = await Promise.resolve().then(() => (init_session_state(), session_state_exports));
|
|
25154
|
-
const llmReady = describeLlmReadiness2();
|
|
25155
26445
|
const engineCount = countAvailableEngines2();
|
|
25156
|
-
const llmDetail = engineCount === 0 ? "
|
|
26446
|
+
const llmDetail = engineCount === 0 ? "run /connect (any key)" : engineCount === 1 ? `1 engine \xB7 ${availableEngineLabels2()[0]}` : `${engineCount} engines \xB7 ${availableEngineLabels2().slice(0, 3).join(", ")}${engineCount > 3 ? ", \u2026" : ""}`;
|
|
25157
26447
|
const llmState = engineCount >= 2 ? badge("READY", "success") : engineCount === 1 ? badge("READY", "success") : badge("MISSING", "warning");
|
|
25158
26448
|
const inferenceDetail = engineCount > 0 ? `active: ${formatActiveStack2(ctx)}` : "not configured";
|
|
25159
26449
|
const license = checkLicense();
|
|
@@ -25209,7 +26499,7 @@ async function printWelcome(ctx, version) {
|
|
|
25209
26499
|
ctx,
|
|
25210
26500
|
unfinishedCount: unfinishedSessions.length
|
|
25211
26501
|
});
|
|
25212
|
-
const emptyDataHint = !hasData ? savedSessions.length > 0 ?
|
|
26502
|
+
const emptyDataHint = !hasData ? savedSessions.length > 0 ? chalk67.dim("Run ") + paint("accent", "/session") + chalk67.dim(" to resume a saved analysis, or ") + paint("accent", "/new") + chalk67.dim(" for a fresh start") : chalk67.dim("Run ") + paint("accent", "/new") + chalk67.dim(" \u2192 pick Demo to explore sample data") : null;
|
|
25213
26503
|
const colW = useWideLayout ? leftW : contentW;
|
|
25214
26504
|
const rightColW = useWideLayout ? rightW : contentW;
|
|
25215
26505
|
const systemLines = buildSystemLines(colW, statusRows, recent);
|
|
@@ -25225,14 +26515,14 @@ async function printWelcome(ctx, version) {
|
|
|
25225
26515
|
const logoOffset = " ".repeat(Math.max(0, Math.floor((cardW - maxLogoW) / 2)));
|
|
25226
26516
|
for (const line of logo) push(logoOffset + line);
|
|
25227
26517
|
const taglineOffset = " ".repeat(Math.max(0, Math.floor((cardW - visibleWidth(TAGLINE)) / 2)));
|
|
25228
|
-
push(taglineOffset +
|
|
26518
|
+
push(taglineOffset + chalk67.dim(TAGLINE));
|
|
25229
26519
|
push("");
|
|
25230
26520
|
}
|
|
25231
26521
|
const versionTag = ` v${version} `;
|
|
25232
26522
|
const gap = Math.max(0, innerW - versionTag.length);
|
|
25233
26523
|
const gapL = Math.floor(gap / 2);
|
|
25234
26524
|
push(
|
|
25235
|
-
border(`\u256D${"\u2500".repeat(gapL)}`) +
|
|
26525
|
+
border(`\u256D${"\u2500".repeat(gapL)}`) + chalk67.dim(versionTag) + border(`${"\u2500".repeat(gap - gapL)}\u256E`)
|
|
25236
26526
|
);
|
|
25237
26527
|
if (useWideLayout) {
|
|
25238
26528
|
const leftLines = [...systemLines, ...helpLines];
|
|
@@ -25332,8 +26622,8 @@ __export(repl_exports, {
|
|
|
25332
26622
|
});
|
|
25333
26623
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
25334
26624
|
import { clearLine as clearLine2, cursorTo as cursorTo2 } from "readline";
|
|
25335
|
-
import
|
|
25336
|
-
import
|
|
26625
|
+
import ora19 from "ora";
|
|
26626
|
+
import chalk68 from "chalk";
|
|
25337
26627
|
function buildPrompt(ctx) {
|
|
25338
26628
|
return buildConversationPrompt(ctx);
|
|
25339
26629
|
}
|
|
@@ -25346,10 +26636,10 @@ function randomGoodbye() {
|
|
|
25346
26636
|
return GOODBYES[Math.floor(Math.random() * GOODBYES.length)] ?? "Bye.";
|
|
25347
26637
|
}
|
|
25348
26638
|
function commandCompletionCandidates() {
|
|
25349
|
-
return [
|
|
26639
|
+
return [.../* @__PURE__ */ new Set([
|
|
25350
26640
|
...REPL_BUILTINS,
|
|
25351
|
-
...listCommandNames(
|
|
25352
|
-
];
|
|
26641
|
+
...listCommandNames(true).map((name) => `/${name}`)
|
|
26642
|
+
])];
|
|
25353
26643
|
}
|
|
25354
26644
|
function completeCommand(line) {
|
|
25355
26645
|
const firstToken2 = line.split(/\s/, 1)[0] ?? "";
|
|
@@ -25361,21 +26651,39 @@ function completeCommand(line) {
|
|
|
25361
26651
|
function inlineCommandSuggestion(line) {
|
|
25362
26652
|
if (!line.startsWith("/") || /\s/.test(line)) return null;
|
|
25363
26653
|
const matches = commandCompletionCandidates().filter((command) => command.startsWith(line));
|
|
25364
|
-
if (matches.length
|
|
25365
|
-
|
|
26654
|
+
if (matches.length === 0) return null;
|
|
26655
|
+
if (matches.length === 1) {
|
|
26656
|
+
return matches[0] === line ? null : matches[0].slice(line.length);
|
|
26657
|
+
}
|
|
26658
|
+
let common = matches[0];
|
|
26659
|
+
for (const match of matches) {
|
|
26660
|
+
let i = 0;
|
|
26661
|
+
while (i < common.length && i < match.length && common[i] === match[i]) i++;
|
|
26662
|
+
common = common.slice(0, i);
|
|
26663
|
+
}
|
|
26664
|
+
return common.length > line.length ? common.slice(line.length) : null;
|
|
25366
26665
|
}
|
|
25367
26666
|
function renderInlineSuggestion(rl, prompt) {
|
|
25368
26667
|
const line = rl.line;
|
|
25369
26668
|
const cursor = rl.cursor;
|
|
25370
26669
|
const suffix = cursor === line.length ? inlineCommandSuggestion(line) : null;
|
|
26670
|
+
const promptWidth = visibleLength(prompt);
|
|
26671
|
+
const columns = process.stdout.columns ?? 80;
|
|
26672
|
+
const fitsOneRow = promptWidth + line.length + (suffix?.length ?? 0) < columns;
|
|
26673
|
+
const shouldPaint = (suffix !== null || suggestionPainted) && fitsOneRow;
|
|
26674
|
+
if (!shouldPaint) {
|
|
26675
|
+
suggestionPainted = false;
|
|
26676
|
+
return;
|
|
26677
|
+
}
|
|
26678
|
+
suggestionPainted = suffix !== null;
|
|
25371
26679
|
clearLine2(process.stdout, 0);
|
|
25372
26680
|
cursorTo2(process.stdout, 0);
|
|
25373
|
-
process.stdout.write(prompt + line + (suffix ?
|
|
25374
|
-
cursorTo2(process.stdout,
|
|
26681
|
+
process.stdout.write(prompt + line + (suffix ? chalk68.dim(suffix) : ""));
|
|
26682
|
+
cursorTo2(process.stdout, promptWidth + cursor);
|
|
25375
26683
|
}
|
|
25376
26684
|
function appendTurnLine(current, promptLabel, currentSummary) {
|
|
25377
|
-
const currentLine = currentSummary ? `${promptLabel} ${current} ${
|
|
25378
|
-
console.log(" " +
|
|
26685
|
+
const currentLine = currentSummary ? `${promptLabel} ${current} ${chalk68.white("\u2192")} ${currentSummary}` : `${promptLabel} ${current}`;
|
|
26686
|
+
console.log(" " + chalk68.dim(currentLine));
|
|
25379
26687
|
console.log();
|
|
25380
26688
|
}
|
|
25381
26689
|
async function goHome(ctx, version, history, opts) {
|
|
@@ -25385,7 +26693,7 @@ async function goHome(ctx, version, history, opts) {
|
|
|
25385
26693
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
25386
26694
|
if (opts?.banner) {
|
|
25387
26695
|
console.log();
|
|
25388
|
-
console.log(" " + paint("accent", "\u2713") + " " +
|
|
26696
|
+
console.log(" " + paint("accent", "\u2713") + " " + chalk68.dim(opts.banner));
|
|
25389
26697
|
}
|
|
25390
26698
|
await printWelcome(ctx, version);
|
|
25391
26699
|
}
|
|
@@ -25408,11 +26716,11 @@ async function handleDispatchResult(result, ctx, version, history) {
|
|
|
25408
26716
|
case "unknown":
|
|
25409
26717
|
if (result.suggestion) {
|
|
25410
26718
|
console.log(
|
|
25411
|
-
" " +
|
|
26719
|
+
" " + chalk68.red(`Unknown command: ${result.token}.`) + chalk68.dim(" Did you mean ") + paint("accent", result.suggestion) + chalk68.dim("?")
|
|
25412
26720
|
);
|
|
25413
26721
|
} else {
|
|
25414
26722
|
console.log(
|
|
25415
|
-
" " +
|
|
26723
|
+
" " + chalk68.red(`Unknown command: ${result.token}`) + chalk68.dim(" Type ") + paint("accent", "/help") + chalk68.dim(" to see available commands.")
|
|
25416
26724
|
);
|
|
25417
26725
|
}
|
|
25418
26726
|
break;
|
|
@@ -25436,7 +26744,7 @@ async function runRepl(ctx, version) {
|
|
|
25436
26744
|
ctx.rl = rl;
|
|
25437
26745
|
console.log();
|
|
25438
26746
|
console.log(
|
|
25439
|
-
" " +
|
|
26747
|
+
" " + chalk68.dim("What do you want to look at? ") + chalk68.dim('(e.g. "pipeline health", "is NRR real?", "board deck on Q3")')
|
|
25440
26748
|
);
|
|
25441
26749
|
console.log();
|
|
25442
26750
|
if (ctx.pendingUpdateCheck) {
|
|
@@ -25466,7 +26774,7 @@ async function runRepl(ctx, version) {
|
|
|
25466
26774
|
return;
|
|
25467
26775
|
}
|
|
25468
26776
|
sigintPrimed = true;
|
|
25469
|
-
console.log("\n " +
|
|
26777
|
+
console.log("\n " + chalk68.dim("Type /exit to quit, or press Ctrl+C again."));
|
|
25470
26778
|
};
|
|
25471
26779
|
rl.on("SIGINT", sigintHandler);
|
|
25472
26780
|
function shutdownRepl() {
|
|
@@ -25537,7 +26845,7 @@ async function runRepl(ctx, version) {
|
|
|
25537
26845
|
}
|
|
25538
26846
|
}
|
|
25539
26847
|
} else {
|
|
25540
|
-
console.error(" " +
|
|
26848
|
+
console.error(" " + chalk68.red("Error: " + String(err.message ?? err)));
|
|
25541
26849
|
}
|
|
25542
26850
|
}
|
|
25543
26851
|
history.push({ input: line, summary });
|
|
@@ -25545,7 +26853,7 @@ async function runRepl(ctx, version) {
|
|
|
25545
26853
|
shutdownRepl();
|
|
25546
26854
|
const exchangeCount = Math.floor(ctx.messages.length / 2);
|
|
25547
26855
|
if (exchangeCount > 0) {
|
|
25548
|
-
const spinner =
|
|
26856
|
+
const spinner = ora19({ text: "Saving session\u2026", color: "cyan", discardStdin: false }).start();
|
|
25549
26857
|
const summary = await closeSession(ctx);
|
|
25550
26858
|
if (summary) {
|
|
25551
26859
|
spinner.succeed(`Session saved (${summary})`);
|
|
@@ -25555,7 +26863,7 @@ async function runRepl(ctx, version) {
|
|
|
25555
26863
|
} else {
|
|
25556
26864
|
await closeSession(ctx);
|
|
25557
26865
|
}
|
|
25558
|
-
console.log(" " +
|
|
26866
|
+
console.log(" " + chalk68.dim(randomGoodbye()));
|
|
25559
26867
|
}
|
|
25560
26868
|
function printHelpOneShot() {
|
|
25561
26869
|
printHelp();
|
|
@@ -25563,10 +26871,10 @@ function printHelpOneShot() {
|
|
|
25563
26871
|
function printHelp() {
|
|
25564
26872
|
console.log();
|
|
25565
26873
|
console.log(" " + sectionHeading("Conversation"));
|
|
25566
|
-
console.log(" " +
|
|
25567
|
-
console.log(" " +
|
|
25568
|
-
console.log(" " +
|
|
25569
|
-
console.log(" " +
|
|
26874
|
+
console.log(" " + chalk68.dim("Type what you want to investigate \u2014 no slash needed."));
|
|
26875
|
+
console.log(" " + chalk68.dim("Paste a CSV path or say ") + paint("accent", '"use demo data"') + chalk68.dim(" to load data."));
|
|
26876
|
+
console.log(" " + chalk68.dim("After analysis, ask questions in plain English."));
|
|
26877
|
+
console.log(" " + chalk68.dim("Say ") + paint("accent", '"ship a board deck"') + chalk68.dim(" to draft a handoff prompt."));
|
|
25570
26878
|
console.log();
|
|
25571
26879
|
console.log(" " + sectionHeading("Shortcuts"));
|
|
25572
26880
|
const shortcuts = [
|
|
@@ -25581,7 +26889,7 @@ function printHelp() {
|
|
|
25581
26889
|
];
|
|
25582
26890
|
const maxW = Math.max(...shortcuts.map(([c]) => c.length)) + 2;
|
|
25583
26891
|
for (const [cmd, desc] of shortcuts) {
|
|
25584
|
-
console.log(` ${paint("accent", padRight(cmd, maxW))} ${
|
|
26892
|
+
console.log(` ${paint("accent", padRight(cmd, maxW))} ${chalk68.dim(desc)}`);
|
|
25585
26893
|
}
|
|
25586
26894
|
console.log();
|
|
25587
26895
|
console.log(" " + sectionHeading("Admin"));
|
|
@@ -25592,13 +26900,13 @@ function printHelp() {
|
|
|
25592
26900
|
];
|
|
25593
26901
|
const adminMaxW = Math.max(...admin.map(([c]) => c.length)) + 2;
|
|
25594
26902
|
for (const [cmd, desc] of admin) {
|
|
25595
|
-
console.log(` ${paint("accent", padRight(cmd, adminMaxW))} ${
|
|
26903
|
+
console.log(` ${paint("accent", padRight(cmd, adminMaxW))} ${chalk68.dim(desc)}`);
|
|
25596
26904
|
}
|
|
25597
26905
|
console.log();
|
|
25598
|
-
console.log(" " +
|
|
26906
|
+
console.log(" " + chalk68.dim("Power-user commands (") + paint("accent", "/new") + chalk68.dim(", ") + paint("accent", "/diagnose") + chalk68.dim(", ") + paint("accent", "/metrics") + chalk68.dim(") remain available."));
|
|
25599
26907
|
console.log();
|
|
25600
26908
|
}
|
|
25601
|
-
var REPL_BUILTINS, ANSI_PATTERN, GOODBYES;
|
|
26909
|
+
var REPL_BUILTINS, ANSI_PATTERN, GOODBYES, suggestionPainted;
|
|
25602
26910
|
var init_repl = __esm({
|
|
25603
26911
|
"src/cli/repl.ts"() {
|
|
25604
26912
|
"use strict";
|
|
@@ -25658,6 +26966,7 @@ var init_repl = __esm({
|
|
|
25658
26966
|
"May your pipeline stay hydrated.",
|
|
25659
26967
|
"Don't let the zombie deals bite."
|
|
25660
26968
|
];
|
|
26969
|
+
suggestionPainted = false;
|
|
25661
26970
|
}
|
|
25662
26971
|
});
|
|
25663
26972
|
|
|
@@ -25678,7 +26987,7 @@ init_emit();
|
|
|
25678
26987
|
init_errors2();
|
|
25679
26988
|
init_types2();
|
|
25680
26989
|
init_version();
|
|
25681
|
-
import
|
|
26990
|
+
import chalk69 from "chalk";
|
|
25682
26991
|
var VERSION = getInstalledVersion();
|
|
25683
26992
|
var UNGATED = UNGATED_COMMANDS;
|
|
25684
26993
|
var DB_COMMANDS = /* @__PURE__ */ new Set(["actions", "ask", "backmeup", "demo", "diagnose", "export", "handoff", "ingest", "metrics", "new", "playbook", "publish", "report", "reset", "segment", "session", "status", "strategy"]);
|
|
@@ -25705,7 +27014,7 @@ async function main() {
|
|
|
25705
27014
|
quiet: args.globals.quiet
|
|
25706
27015
|
});
|
|
25707
27016
|
if (!ctx.execution.color) {
|
|
25708
|
-
|
|
27017
|
+
chalk69.level = 0;
|
|
25709
27018
|
}
|
|
25710
27019
|
if (args.globals.stdin) {
|
|
25711
27020
|
args.input = (await readStdin()).trim();
|
|
@@ -25718,16 +27027,16 @@ async function main() {
|
|
|
25718
27027
|
if (isStructuredOutput(ctx.execution)) {
|
|
25719
27028
|
emitError(cmd || "ntrp", new NtrpError("license_invalid", lic2.message, 3 /* Auth */));
|
|
25720
27029
|
}
|
|
25721
|
-
console.error(
|
|
27030
|
+
console.error(chalk69.red(`
|
|
25722
27031
|
${lic2.message}`));
|
|
25723
|
-
console.error(
|
|
27032
|
+
console.error(chalk69.dim(" Trial's over \u2014 /upgrade and paste your key.\n"));
|
|
25724
27033
|
process.exit(1);
|
|
25725
27034
|
}
|
|
25726
27035
|
}
|
|
25727
|
-
const PROFILE_HINT_SKIP = /* @__PURE__ */ new Set(["onboard", "setup", "config", "activate", "help", "home", "exit", "quit", "clear", "profile", "progress"]);
|
|
27036
|
+
const PROFILE_HINT_SKIP = /* @__PURE__ */ new Set(["onboard", "setup", "config", "connect", "activate", "help", "home", "exit", "quit", "clear", "profile", "progress"]);
|
|
25728
27037
|
if (!isProfileConfigured() && !PROFILE_HINT_SKIP.has(cmd) && !ctx.execution.quiet) {
|
|
25729
27038
|
console.error(
|
|
25730
|
-
" " +
|
|
27039
|
+
" " + chalk69.dim("Tip: run ") + paint("accent", "ntrp") + chalk69.dim(" interactively to set up your company profile for richer answers.")
|
|
25731
27040
|
);
|
|
25732
27041
|
}
|
|
25733
27042
|
const result = await dispatch(args.input, ctx);
|
|
@@ -25739,12 +27048,12 @@ async function main() {
|
|
|
25739
27048
|
}
|
|
25740
27049
|
if (result.suggestion) {
|
|
25741
27050
|
console.error(
|
|
25742
|
-
|
|
27051
|
+
chalk69.red(` Unknown command: ${result.token}.`) + chalk69.dim(" Did you mean ") + paint("accent", result.suggestion) + chalk69.dim("?")
|
|
25743
27052
|
);
|
|
25744
27053
|
} else {
|
|
25745
|
-
console.error(
|
|
27054
|
+
console.error(chalk69.red(` Unknown command: ${result.token}`));
|
|
25746
27055
|
}
|
|
25747
|
-
console.error(
|
|
27056
|
+
console.error(chalk69.dim(" Run 'ntrp' for the interactive prompt."));
|
|
25748
27057
|
process.exit(1);
|
|
25749
27058
|
break;
|
|
25750
27059
|
case "help":
|
|
@@ -25771,6 +27080,7 @@ async function main() {
|
|
|
25771
27080
|
const { printTrialNudge: printTrialNudge2 } = await Promise.resolve().then(() => (init_upgrade(), upgrade_exports));
|
|
25772
27081
|
printTrialNudge2(lic);
|
|
25773
27082
|
}
|
|
27083
|
+
void Promise.resolve().then(() => (init_discovery(), discovery_exports)).then((m) => m.refreshStaleProviderCaches()).catch(() => void 0);
|
|
25774
27084
|
if (!isProfileConfigured()) {
|
|
25775
27085
|
try {
|
|
25776
27086
|
const { handler: onboard } = await Promise.resolve().then(() => (init_onboard(), onboard_exports));
|