@redstone-md/mapr 0.0.4-alpha → 0.0.6-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,7 @@ This repository is public for source visibility and collaboration. The license r
10
10
 
11
11
  - Bun-only CLI/TUI with interactive setup through `@clack/prompts`
12
12
  - OpenAI and OpenAI-compatible provider support
13
+ - OpenAI Codex family support with fast/reasoning selection
13
14
  - Built-in provider presets for BlackBox AI, Nvidia NIM, and OnlySQ
14
15
  - Model discovery with searchable selection
15
16
  - Automatic context-window detection from provider model metadata when available
@@ -77,6 +78,14 @@ npx @redstone-md/mapr --help
77
78
  - `onlysq` -> `https://api.onlysq.ru/ai/openai`
78
79
  - `custom` -> any other OpenAI-compatible endpoint
79
80
 
81
+ ## OpenAI Auth Modes
82
+
83
+ - `API key` uses the standard OpenAI API at `https://api.openai.com/v1`
84
+ - `Use existing Codex CLI auth` reuses the local `codex login` browser session from `~/.codex/auth.json`
85
+ - Codex CLI auth automatically switches OpenAI requests to `https://chatgpt.com/backend-api/codex`
86
+ - When the local Codex access token expires, Mapr refreshes it through the official OpenAI refresh-token flow before retrying the request
87
+ - If Codex CLI auth is missing, run `codex login` first and then re-run Mapr
88
+
80
89
  ## Usage
81
90
 
82
91
  Interactive:
@@ -105,6 +114,17 @@ List models with detected context sizes when available:
105
114
  npx @redstone-md/mapr --list-models --headless --provider-preset nvidia-nim --api-key secret
106
115
  ```
107
116
 
117
+ List models through an existing Codex CLI login:
118
+
119
+ ```bash
120
+ npx @redstone-md/mapr \
121
+ --headless \
122
+ --list-models \
123
+ --provider-type openai \
124
+ --auth-method codex-cli \
125
+ --codex-home ~/.codex
126
+ ```
127
+
108
128
  Useful flags:
109
129
 
110
130
  - `--max-pages <n>` limits same-origin HTML pages
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { cancel, confirm, intro, isCancel, log, outro, spinner, text } from "@clack/prompts";
3
+ import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
4
4
  import pc from "picocolors";
5
5
  import packageJson from "./package.json";
6
6
 
@@ -9,11 +9,15 @@ import { AiBundleAnalyzer, chunkTextByBytes, deriveChunkSizeBytes } from "./lib/
9
9
  import { getConfigOverrides, parseCliArgs, renderHelpText } from "./lib/cli-args";
10
10
  import { ConfigManager } from "./lib/config";
11
11
  import { BundleFormatter } from "./lib/formatter";
12
- import { renderProgressBar } from "./lib/progress";
12
+ import { renderAdaptiveAnalysisProgressLine, renderProgressBar } from "./lib/progress";
13
+ import { findKnownModelInfo, isCodexModel } from "./lib/provider";
13
14
  import { ReportWriter } from "./lib/reporter";
14
15
  import { BundleScraper } from "./lib/scraper";
15
16
  import { SWARM_AGENT_ORDER } from "./lib/swarm-prompts";
16
17
 
18
+ process.env.AI_SDK_LOG_WARNINGS = "false";
19
+ (globalThis as typeof globalThis & { AI_SDK_LOG_WARNINGS?: boolean }).AI_SDK_LOG_WARNINGS = false;
20
+
17
21
  function exitIfCancelled<T>(value: T): T {
18
22
  if (isCancel(value)) {
19
23
  cancel("Operation cancelled.");
@@ -62,6 +66,30 @@ async function resolveTargetUrl(headless: boolean, prefilledUrl?: string): Promi
62
66
  );
63
67
  }
64
68
 
69
+ async function resolveAnalysisConcurrency(headless: boolean, prefilledValue: number | undefined, totalChunks: number): Promise<number> {
70
+ if (prefilledValue !== undefined) {
71
+ return prefilledValue;
72
+ }
73
+
74
+ if (headless || totalChunks <= 1) {
75
+ return 1;
76
+ }
77
+
78
+ return Number(
79
+ exitIfCancelled(
80
+ await select({
81
+ message: "Analysis concurrency",
82
+ initialValue: 2,
83
+ options: [
84
+ { value: 1, label: "1 lane", hint: "Most stable" },
85
+ { value: 2, label: "2 lanes", hint: "Recommended" },
86
+ { value: 4, label: "4 lanes", hint: "Aggressive" },
87
+ ],
88
+ }),
89
+ ),
90
+ );
91
+ }
92
+
65
93
  async function run(): Promise<void> {
66
94
  const args = parseCliArgs(process.argv.slice(2));
67
95
 
@@ -147,21 +175,41 @@ async function run(): Promise<void> {
147
175
  sum + chunkTextByBytes(artifact.formattedContent || artifact.content, deriveChunkSizeBytes(config.modelContextSize)).length,
148
176
  0,
149
177
  );
178
+ const analysisConcurrency = await resolveAnalysisConcurrency(headless, args.analysisConcurrency, totalChunks);
150
179
  const totalAgentTasks = Math.max(1, totalChunks * SWARM_AGENT_ORDER.length);
151
180
  let completedAgentTasks = 0;
181
+ const analysisStartedAt = Date.now();
152
182
 
153
183
  const analysisStep = spinner({ indicator: "timer" });
154
- analysisStep.start(formatAnalysisProgress(0, totalAgentTasks, "Starting swarm analysis"));
184
+ analysisStep.start(formatAnalysisProgress(0, totalAgentTasks, `Starting swarm analysis (${analysisConcurrency} lane${analysisConcurrency === 1 ? "" : "s"})`));
155
185
 
156
186
  const analyzer = new AiBundleAnalyzer({
157
187
  providerConfig: config,
158
188
  localRag: args.localRag,
189
+ analysisConcurrency,
159
190
  onProgress(event) {
160
191
  if (event.stage === "agent" && event.state === "completed") {
161
192
  completedAgentTasks += 1;
162
193
  }
163
194
 
164
- const progressLine = formatAnalysisProgress(completedAgentTasks, totalAgentTasks, event.message);
195
+ const progressLine =
196
+ event.stage === "agent" && event.agent
197
+ ? renderAdaptiveAnalysisProgressLine({
198
+ completed: completedAgentTasks,
199
+ total: totalAgentTasks,
200
+ elapsedMs: Date.now() - analysisStartedAt,
201
+ agent: event.agent,
202
+ state: event.state,
203
+ artifactUrl: event.artifactUrl,
204
+ ...(event.chunkIndex !== undefined ? { chunkIndex: event.chunkIndex } : {}),
205
+ ...(event.chunkCount !== undefined ? { chunkCount: event.chunkCount } : {}),
206
+ ...(event.estimatedOutputTokens !== undefined
207
+ ? { estimatedOutputTokens: event.estimatedOutputTokens }
208
+ : {}),
209
+ ...(event.outputTokens !== undefined ? { outputTokens: event.outputTokens } : {}),
210
+ ...(event.tokensPerSecond !== undefined ? { tokensPerSecond: event.tokensPerSecond } : {}),
211
+ })
212
+ : formatAnalysisProgress(completedAgentTasks, totalAgentTasks, event.message);
165
213
  analysisStep.message(progressLine);
166
214
 
167
215
  if (args.verboseAgents && event.stage === "agent" && event.state === "completed") {
@@ -218,12 +266,17 @@ async function run(): Promise<void> {
218
266
  });
219
267
  reportStep.stop(reportStatus === "partial" ? "Partial report written to disk" : "Report written to disk");
220
268
 
269
+ const selectedModelInfo = findKnownModelInfo(config.model);
221
270
  const summaryLines = [
222
271
  reportStatus === "partial" ? `${pc.yellow("Analysis incomplete.")}` : `${pc.green("Analysis complete.")}`,
223
272
  `${pc.bold("Status:")} ${reportStatus === "partial" ? "partial report saved after error" : "complete"}`,
224
273
  `${pc.bold("Target:")} ${scrapeResult.pageUrl}`,
225
274
  `${pc.bold("Provider:")} ${config.providerName} (${config.model})`,
275
+ ...(config.authMethod !== undefined ? [`${pc.bold("Auth:")} ${config.authMethod}`] : []),
226
276
  `${pc.bold("Context size:")} ${config.modelContextSize.toLocaleString()} tokens`,
277
+ ...(config.openAiMode !== undefined ? [`${pc.bold("OpenAI mode:")} ${config.openAiMode}`] : []),
278
+ ...(isCodexModel(config.model) && selectedModelInfo?.usageLimitsNote ? [`${pc.bold("Codex limits:")} ${selectedModelInfo.usageLimitsNote}`] : []),
279
+ `${pc.bold("Concurrency:")} ${analysisConcurrency}`,
227
280
  `${pc.bold("Local RAG:")} ${args.localRag ? "enabled" : "disabled"}`,
228
281
  `${pc.bold("Pages:")} ${scrapeResult.htmlPages.length}`,
229
282
  `${pc.bold("Artifacts:")} ${formattedArtifacts.length}`,