@codemap-ai/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +12 -0
- package/.codex-plugin/plugin.json +35 -0
- package/.cursor-plugin/plugin.json +16 -0
- package/agent-pack/README.md +44 -0
- package/agent-pack/agents/codemap-explorer.md +12 -0
- package/agent-pack/commands/feature-area.md +8 -0
- package/agent-pack/rules/install.md +35 -0
- package/agent-pack/rules/mcp-first.md +18 -0
- package/agent-pack/rules/task-lifecycle.md +11 -0
- package/agent-pack/rules/workflow-skills.md +97 -0
- package/agent-pack/skills/brainstorming/SKILL.md +41 -0
- package/agent-pack/skills/executing-plans/SKILL.md +26 -0
- package/agent-pack/skills/feature-area-investigation/SKILL.md +15 -0
- package/agent-pack/skills/interpreting-codemap-output/SKILL.md +29 -0
- package/agent-pack/skills/mcp-first-exploration/SKILL.md +10 -0
- package/agent-pack/skills/safe-edit-and-reimport/SKILL.md +18 -0
- package/agent-pack/skills/symbol-level-debugging/SKILL.md +15 -0
- package/agent-pack/skills/test-driven-development/SKILL.md +36 -0
- package/agent-pack/skills/token-efficient-code-review/SKILL.md +15 -0
- package/agent-pack/skills/verification-before-completion/SKILL.md +25 -0
- package/agent-pack/skills/writing-plans/SKILL.md +32 -0
- package/agent-pack/templates/AGENTS.md +21 -0
- package/agent-pack/templates/CLAUDE.md +19 -0
- package/agent-pack/templates/COPILOT.md +75 -0
- package/agent-pack/templates/GEMINI.md +77 -0
- package/agent-pack/templates/design-spec.md +34 -0
- package/agent-pack/templates/implementation-plan.md +26 -0
- package/agent-pack/templates/opencode-AGENTS.md +76 -0
- package/agent-pack/templates/opencode-INSTALL.md +5 -0
- package/agent-pack/templates/verification-report.md +18 -0
- package/dist/chat-terminal-DFFYQ6AF.js +1743 -0
- package/dist/chunk-A2QHID6K.js +34 -0
- package/dist/chunk-AXGGL47C.js +457 -0
- package/dist/chunk-FFFJKKKM.js +218262 -0
- package/dist/chunk-KWYP3ADN.js +1742 -0
- package/dist/chunk-WNJNT3FC.js +216 -0
- package/dist/chunk-YRXVMFXS.js +99 -0
- package/dist/index.js +9225 -0
- package/dist/login-screen-2DJBDM47.js +143 -0
- package/dist/pi-tui-app-GVZ3AN42.js +2073 -0
- package/package.json +59 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import{createRequire as __cmr}from"node:module";import{fileURLToPath as __f2p}from"node:url";import{dirname as __dn}from"node:path";var require=__cmr(import.meta.url);var __filename=__f2p(import.meta.url);var __dirname=__dn(__filename);
|
|
2
|
+
|
|
3
|
+
// src/cli-agent/chat/ui/stderr-interceptor.ts
|
|
4
|
+
var _gatewayOffline = false;
|
|
5
|
+
function isGatewayOffline() {
|
|
6
|
+
return _gatewayOffline;
|
|
7
|
+
}
|
|
8
|
+
function installStderrInterceptor() {
|
|
9
|
+
const original = process.stderr.write.bind(process.stderr);
|
|
10
|
+
process.stderr.write = function(chunk, encodingOrCb, cb) {
|
|
11
|
+
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
12
|
+
if (isGatewayNoise(text)) {
|
|
13
|
+
_gatewayOffline = true;
|
|
14
|
+
const callback = typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
15
|
+
callback?.();
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
return original(chunk, encodingOrCb, cb);
|
|
19
|
+
};
|
|
20
|
+
return () => {
|
|
21
|
+
process.stderr.write = original;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function isGatewayNoise(text) {
|
|
25
|
+
return text.includes("[GatewayRegistry]") || text.includes("[GatewaySync]") || text.includes("models.dev") && (text.includes("fetch failed") || text.includes("ENOTFOUND")) || // Mastra logs raw LLM API errors (timeout, connection refused, etc.) directly to
|
|
26
|
+
// stderr. The actual error propagates through the harness error event and is
|
|
27
|
+
// already surfaced as a UI message — swallow the redundant raw dump here.
|
|
28
|
+
text.includes("Upstream LLM API error");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
isGatewayOffline,
|
|
33
|
+
installStderrInterceptor
|
|
34
|
+
};
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import{createRequire as __cmr}from"node:module";import{fileURLToPath as __f2p}from"node:url";import{dirname as __dn}from"node:path";var require=__cmr(import.meta.url);var __filename=__f2p(import.meta.url);var __dirname=__dn(__filename);
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
// src/config.ts
|
|
35
|
+
import { homedir } from "os";
|
|
36
|
+
import path from "path";
|
|
37
|
+
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
38
|
+
var DEFAULT_API_URL = "https://api.codemap.dev";
|
|
39
|
+
function readOptionalEnv(name) {
|
|
40
|
+
const value = process.env[name]?.trim();
|
|
41
|
+
return value ? value : null;
|
|
42
|
+
}
|
|
43
|
+
function normalizeConfigFile(input) {
|
|
44
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
const record = input;
|
|
48
|
+
const apiUrl = typeof record.apiUrl === "string" && record.apiUrl.trim() ? record.apiUrl.trim() : null;
|
|
49
|
+
const apiToken = typeof record.apiToken === "string" && record.apiToken.trim() ? record.apiToken.trim() : null;
|
|
50
|
+
const userRecord = record.user && typeof record.user === "object" && !Array.isArray(record.user) ? record.user : null;
|
|
51
|
+
const authRecord = record.auth && typeof record.auth === "object" && !Array.isArray(record.auth) ? record.auth : null;
|
|
52
|
+
const user = userRecord ? {
|
|
53
|
+
id: typeof userRecord.id === "string" && userRecord.id.trim() ? userRecord.id.trim() : void 0,
|
|
54
|
+
email: typeof userRecord.email === "string" && userRecord.email.trim() ? userRecord.email.trim() : void 0,
|
|
55
|
+
name: typeof userRecord.name === "string" && userRecord.name.trim() ? userRecord.name.trim() : void 0
|
|
56
|
+
} : null;
|
|
57
|
+
const auth = authRecord ? {
|
|
58
|
+
method: "api_key",
|
|
59
|
+
createdAt: typeof authRecord.createdAt === "string" && authRecord.createdAt.trim() ? authRecord.createdAt.trim() : void 0,
|
|
60
|
+
lastValidatedAt: typeof authRecord.lastValidatedAt === "string" && authRecord.lastValidatedAt.trim() ? authRecord.lastValidatedAt.trim() : void 0
|
|
61
|
+
} : null;
|
|
62
|
+
return {
|
|
63
|
+
...apiUrl ? { apiUrl } : {},
|
|
64
|
+
...apiToken ? { apiToken } : {},
|
|
65
|
+
...user ? { user } : {},
|
|
66
|
+
...auth ? { auth } : {}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async function readConfigFile(configPath) {
|
|
70
|
+
try {
|
|
71
|
+
const rawContent = await readFile(configPath, "utf8");
|
|
72
|
+
return normalizeConfigFile(JSON.parse(rawContent));
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function getConfigPaths(cwd = process.cwd()) {
|
|
81
|
+
return {
|
|
82
|
+
projectConfigPath: path.join(cwd, ".codemap", "mcp.json"),
|
|
83
|
+
globalConfigPath: path.join(homedir(), ".codemap", "mcp.json")
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function applyLayer(resolved, layer) {
|
|
87
|
+
if (!layer) {
|
|
88
|
+
return resolved;
|
|
89
|
+
}
|
|
90
|
+
const nextResolved = { ...resolved };
|
|
91
|
+
const nextApiUrl = layer.apiUrl?.trim() || null;
|
|
92
|
+
if (nextApiUrl && nextApiUrl !== nextResolved.apiUrl) {
|
|
93
|
+
nextResolved.apiUrl = nextApiUrl;
|
|
94
|
+
if (!layer.apiToken) {
|
|
95
|
+
nextResolved.apiToken = null;
|
|
96
|
+
nextResolved.user = null;
|
|
97
|
+
nextResolved.auth = null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (layer.apiToken !== void 0) {
|
|
101
|
+
nextResolved.apiToken = layer.apiToken ?? null;
|
|
102
|
+
}
|
|
103
|
+
if (layer.user !== void 0) {
|
|
104
|
+
nextResolved.user = layer.user ?? null;
|
|
105
|
+
}
|
|
106
|
+
if (layer.auth !== void 0) {
|
|
107
|
+
nextResolved.auth = layer.auth ?? null;
|
|
108
|
+
}
|
|
109
|
+
return nextResolved;
|
|
110
|
+
}
|
|
111
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
112
|
+
const { projectConfigPath, globalConfigPath } = getConfigPaths(cwd);
|
|
113
|
+
const [projectConfig, globalConfig] = await Promise.all([
|
|
114
|
+
readConfigFile(projectConfigPath),
|
|
115
|
+
readConfigFile(globalConfigPath)
|
|
116
|
+
]);
|
|
117
|
+
const envConfig = {
|
|
118
|
+
apiUrl: readOptionalEnv("CODEMAP_API_URL"),
|
|
119
|
+
apiToken: readOptionalEnv("CODEMAP_API_KEY")
|
|
120
|
+
};
|
|
121
|
+
const toolModeRaw = readOptionalEnv("CODEMAP_TOOL_MODE");
|
|
122
|
+
const toolMode = toolModeRaw === "lite" || toolModeRaw === "full" ? toolModeRaw : "standard";
|
|
123
|
+
let resolved = {
|
|
124
|
+
apiUrl: DEFAULT_API_URL,
|
|
125
|
+
apiToken: null,
|
|
126
|
+
user: null,
|
|
127
|
+
auth: null,
|
|
128
|
+
projectConfigPath,
|
|
129
|
+
globalConfigPath,
|
|
130
|
+
toolMode
|
|
131
|
+
};
|
|
132
|
+
resolved = applyLayer(resolved, envConfig);
|
|
133
|
+
resolved = applyLayer(resolved, globalConfig);
|
|
134
|
+
resolved = applyLayer(resolved, projectConfig);
|
|
135
|
+
return resolved;
|
|
136
|
+
}
|
|
137
|
+
async function writeConfigFile(configPath, config) {
|
|
138
|
+
await mkdir(path.dirname(configPath), { recursive: true });
|
|
139
|
+
await writeFile(configPath, `${JSON.stringify(config, null, 2)}
|
|
140
|
+
`, "utf8");
|
|
141
|
+
}
|
|
142
|
+
async function saveGlobalConfig(config) {
|
|
143
|
+
await writeConfigFile(config.globalConfigPath, {
|
|
144
|
+
apiUrl: config.apiUrl,
|
|
145
|
+
apiToken: config.apiToken,
|
|
146
|
+
user: config.user,
|
|
147
|
+
auth: config.auth
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
async function clearGlobalAuthConfig(config) {
|
|
151
|
+
const existingConfig = await readConfigFile(config.globalConfigPath);
|
|
152
|
+
if (!existingConfig) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const nextConfig = {
|
|
156
|
+
apiUrl: existingConfig.apiUrl ?? null
|
|
157
|
+
};
|
|
158
|
+
await writeConfigFile(config.globalConfigPath, nextConfig);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/lib/mcp-auth.ts
|
|
162
|
+
import os from "os";
|
|
163
|
+
|
|
164
|
+
// src/lib/codemap-api.ts
|
|
165
|
+
function createCodeMapClient(config) {
|
|
166
|
+
function buildUrl(path2, query) {
|
|
167
|
+
const url = new URL(path2, config.apiUrl);
|
|
168
|
+
for (const [key, value] of Object.entries(query ?? {})) {
|
|
169
|
+
if (value !== void 0) url.searchParams.set(key, value);
|
|
170
|
+
}
|
|
171
|
+
return url;
|
|
172
|
+
}
|
|
173
|
+
function buildAuthHeaders(extra) {
|
|
174
|
+
const headers = { ...extra };
|
|
175
|
+
if (config.apiToken) {
|
|
176
|
+
headers["x-api-key"] = config.apiToken;
|
|
177
|
+
headers["Authorization"] = `Bearer ${config.apiToken}`;
|
|
178
|
+
}
|
|
179
|
+
return headers;
|
|
180
|
+
}
|
|
181
|
+
async function handleResponse(response) {
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
const body = await response.text().catch(() => "");
|
|
184
|
+
throw new Error(
|
|
185
|
+
`CodeMap API returned ${response.status} ${response.statusText}. ${body}`.trim()
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
const json = await response.json();
|
|
189
|
+
if (json.data === void 0) throw new Error("Unexpected response from API.");
|
|
190
|
+
return json.data;
|
|
191
|
+
}
|
|
192
|
+
function checkAuth(required) {
|
|
193
|
+
if (required && !config.apiToken) {
|
|
194
|
+
throw new Error("Not authenticated. Run `codemap login`.");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function request(path2, options) {
|
|
198
|
+
checkAuth(options?.authRequired);
|
|
199
|
+
const url = buildUrl(path2, options?.query);
|
|
200
|
+
const hasBody = options?.body !== void 0;
|
|
201
|
+
const headers = buildAuthHeaders(hasBody ? { "Content-Type": "application/json" } : void 0);
|
|
202
|
+
let response;
|
|
203
|
+
try {
|
|
204
|
+
response = await fetch(url.toString(), {
|
|
205
|
+
method: options?.method ?? "GET",
|
|
206
|
+
headers,
|
|
207
|
+
body: hasBody ? JSON.stringify(options.body) : void 0
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`Failed to reach CodeMap API at ${config.apiUrl}. ${error instanceof Error ? error.message : String(error)}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return handleResponse(response);
|
|
215
|
+
}
|
|
216
|
+
async function upload(path2, buffer, options) {
|
|
217
|
+
checkAuth(options?.authRequired);
|
|
218
|
+
const url = buildUrl(path2, options?.query);
|
|
219
|
+
const headers = buildAuthHeaders({ "Content-Type": "application/zip" });
|
|
220
|
+
let response;
|
|
221
|
+
try {
|
|
222
|
+
response = await fetch(url.toString(), {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers,
|
|
225
|
+
body: Buffer.from(buffer)
|
|
226
|
+
});
|
|
227
|
+
} catch (error) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Failed to reach CodeMap API at ${config.apiUrl}. ${error instanceof Error ? error.message : String(error)}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return handleResponse(response);
|
|
233
|
+
}
|
|
234
|
+
return { request, upload };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/lib/open-url.ts
|
|
238
|
+
import { spawn } from "child_process";
|
|
239
|
+
function getOpenCommand(url) {
|
|
240
|
+
switch (process.platform) {
|
|
241
|
+
case "darwin":
|
|
242
|
+
return {
|
|
243
|
+
command: "open",
|
|
244
|
+
args: [url]
|
|
245
|
+
};
|
|
246
|
+
case "win32":
|
|
247
|
+
return {
|
|
248
|
+
command: "cmd",
|
|
249
|
+
args: ["/c", "start", "", url]
|
|
250
|
+
};
|
|
251
|
+
default:
|
|
252
|
+
return {
|
|
253
|
+
command: "xdg-open",
|
|
254
|
+
args: [url]
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async function openUrlInBrowser(url) {
|
|
259
|
+
const launcher = getOpenCommand(url);
|
|
260
|
+
const child = spawn(launcher.command, launcher.args, {
|
|
261
|
+
detached: true,
|
|
262
|
+
stdio: "ignore"
|
|
263
|
+
});
|
|
264
|
+
child.unref();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/lib/mcp-auth.ts
|
|
268
|
+
function sleep(ms) {
|
|
269
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
270
|
+
}
|
|
271
|
+
async function startMcpLogin(client) {
|
|
272
|
+
return client.request("/mcp/auth/start", {
|
|
273
|
+
method: "POST",
|
|
274
|
+
body: {
|
|
275
|
+
clientName: "CodeMap MCP",
|
|
276
|
+
deviceName: os.hostname()
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
async function getMcpAuthStatus(client, sessionId) {
|
|
281
|
+
return client.request("/mcp/auth/status", {
|
|
282
|
+
query: { sessionId }
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async function getMcpWhoAmI(client) {
|
|
286
|
+
return client.request("/mcp/auth/me", {
|
|
287
|
+
authRequired: true
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
async function claimMcpAuthSession(client, sessionId) {
|
|
291
|
+
return client.request("/mcp/auth/claim", {
|
|
292
|
+
method: "POST",
|
|
293
|
+
body: { sessionId }
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
async function persistAuthorizedSession(config, status) {
|
|
297
|
+
if (!status.apiKey) {
|
|
298
|
+
throw new Error("Cannot persist MCP auth session without an API key.");
|
|
299
|
+
}
|
|
300
|
+
const auth = {
|
|
301
|
+
method: "api_key",
|
|
302
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
303
|
+
lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
304
|
+
};
|
|
305
|
+
const apiUrl = status.apiUrl || config.apiUrl;
|
|
306
|
+
await saveGlobalConfig({
|
|
307
|
+
globalConfigPath: config.globalConfigPath,
|
|
308
|
+
apiUrl,
|
|
309
|
+
apiToken: status.apiKey,
|
|
310
|
+
user: status.user ?? null,
|
|
311
|
+
auth
|
|
312
|
+
});
|
|
313
|
+
config.apiUrl = apiUrl;
|
|
314
|
+
config.apiToken = status.apiKey;
|
|
315
|
+
config.user = status.user ?? null;
|
|
316
|
+
config.auth = auth;
|
|
317
|
+
}
|
|
318
|
+
async function pollMcpAuthUntilDone(config, sessionId, options) {
|
|
319
|
+
const client = createCodeMapClient(config);
|
|
320
|
+
const pollIntervalMs = options?.pollIntervalMs && options.pollIntervalMs > 0 ? options.pollIntervalMs : 2e3;
|
|
321
|
+
let effectiveExpiresAt = options?.expiresAt ?? null;
|
|
322
|
+
const startedAtMs = Date.now();
|
|
323
|
+
const maxWaitMs = options?.maxWaitMs !== void 0 && options.maxWaitMs !== null ? Math.max(0, options.maxWaitMs) : null;
|
|
324
|
+
while (true) {
|
|
325
|
+
const status = await getMcpAuthStatus(client, sessionId);
|
|
326
|
+
if (!effectiveExpiresAt && status.expiresAt) {
|
|
327
|
+
effectiveExpiresAt = status.expiresAt;
|
|
328
|
+
}
|
|
329
|
+
const expiresAtMs = effectiveExpiresAt ? new Date(effectiveExpiresAt).getTime() : null;
|
|
330
|
+
if (status.status === "authorized") {
|
|
331
|
+
try {
|
|
332
|
+
const claimResult = await claimMcpAuthSession(client, sessionId);
|
|
333
|
+
await persistAuthorizedSession(config, claimResult);
|
|
334
|
+
return {
|
|
335
|
+
authenticated: true,
|
|
336
|
+
status: "authorized",
|
|
337
|
+
apiUrl: claimResult.apiUrl || config.apiUrl,
|
|
338
|
+
user: claimResult.user ?? null,
|
|
339
|
+
expiresAt: claimResult.expiresAt ?? effectiveExpiresAt
|
|
340
|
+
};
|
|
341
|
+
} catch (error) {
|
|
342
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
343
|
+
if (message.includes("already been claimed")) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
"Authorization completed, but the CodeMap MCP API key was already claimed by another client or request."
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (status.status === "expired") {
|
|
352
|
+
return {
|
|
353
|
+
authenticated: false,
|
|
354
|
+
status: "expired",
|
|
355
|
+
apiUrl: status.apiUrl || config.apiUrl,
|
|
356
|
+
user: status.user ?? null,
|
|
357
|
+
expiresAt: effectiveExpiresAt
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
if (status.status === "denied") {
|
|
361
|
+
return {
|
|
362
|
+
authenticated: false,
|
|
363
|
+
status: "denied",
|
|
364
|
+
apiUrl: status.apiUrl || config.apiUrl,
|
|
365
|
+
user: status.user ?? null,
|
|
366
|
+
expiresAt: effectiveExpiresAt
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
const nowMs = Date.now();
|
|
370
|
+
if (expiresAtMs !== null && nowMs >= expiresAtMs) {
|
|
371
|
+
return {
|
|
372
|
+
authenticated: false,
|
|
373
|
+
status: "expired",
|
|
374
|
+
apiUrl: status.apiUrl || config.apiUrl,
|
|
375
|
+
user: status.user ?? null,
|
|
376
|
+
expiresAt: effectiveExpiresAt
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
if (maxWaitMs !== null && nowMs - startedAtMs >= maxWaitMs) {
|
|
380
|
+
return {
|
|
381
|
+
authenticated: false,
|
|
382
|
+
status: "pending",
|
|
383
|
+
apiUrl: status.apiUrl || config.apiUrl,
|
|
384
|
+
user: status.user ?? null,
|
|
385
|
+
expiresAt: effectiveExpiresAt,
|
|
386
|
+
timedOut: true
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
const sleepMs = Math.min(
|
|
390
|
+
pollIntervalMs,
|
|
391
|
+
expiresAtMs !== null ? Math.max(0, expiresAtMs - nowMs) : pollIntervalMs,
|
|
392
|
+
maxWaitMs !== null ? Math.max(0, maxWaitMs - (nowMs - startedAtMs)) : pollIntervalMs
|
|
393
|
+
);
|
|
394
|
+
if (sleepMs <= 0) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
await sleep(sleepMs);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async function runLoginFlow(config) {
|
|
401
|
+
const client = createCodeMapClient(config);
|
|
402
|
+
const startResponse = await startMcpLogin(client);
|
|
403
|
+
const openedBrowser = await tryOpenLoginBrowser(startResponse.authorizeUrl);
|
|
404
|
+
const result = await waitForLoginAuthorization(config, startResponse);
|
|
405
|
+
return {
|
|
406
|
+
openedBrowser,
|
|
407
|
+
authorizeUrl: startResponse.authorizeUrl,
|
|
408
|
+
user: result.user ?? null,
|
|
409
|
+
apiUrl: result.apiUrl || config.apiUrl
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
async function tryOpenLoginBrowser(authorizeUrl) {
|
|
413
|
+
try {
|
|
414
|
+
await openUrlInBrowser(authorizeUrl);
|
|
415
|
+
return true;
|
|
416
|
+
} catch {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async function waitForLoginAuthorization(config, startResponse) {
|
|
421
|
+
const result = await pollMcpAuthUntilDone(config, startResponse.sessionId, {
|
|
422
|
+
expiresAt: startResponse.expiresAt,
|
|
423
|
+
pollIntervalMs: startResponse.pollIntervalMs
|
|
424
|
+
});
|
|
425
|
+
if (result.status === "authorized") {
|
|
426
|
+
return {
|
|
427
|
+
sessionId: startResponse.sessionId,
|
|
428
|
+
status: "authorized",
|
|
429
|
+
expiresAt: result.expiresAt,
|
|
430
|
+
apiUrl: result.apiUrl,
|
|
431
|
+
user: result.user
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
if (result.status === "expired") {
|
|
435
|
+
throw new Error("MCP login session expired before authorization completed.");
|
|
436
|
+
}
|
|
437
|
+
if (result.status === "denied") {
|
|
438
|
+
throw new Error("MCP login was denied.");
|
|
439
|
+
}
|
|
440
|
+
throw new Error("MCP login timed out before authorization completed.");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export {
|
|
444
|
+
__require,
|
|
445
|
+
__commonJS,
|
|
446
|
+
__toESM,
|
|
447
|
+
loadConfig,
|
|
448
|
+
clearGlobalAuthConfig,
|
|
449
|
+
createCodeMapClient,
|
|
450
|
+
openUrlInBrowser,
|
|
451
|
+
startMcpLogin,
|
|
452
|
+
getMcpWhoAmI,
|
|
453
|
+
pollMcpAuthUntilDone,
|
|
454
|
+
runLoginFlow,
|
|
455
|
+
tryOpenLoginBrowser,
|
|
456
|
+
waitForLoginAuthorization
|
|
457
|
+
};
|