githits 0.4.11 → 0.4.12
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/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.plugin/plugin.json +1 -1
- package/dist/cli.js +12442 -11997
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-5xd5rcm9.js → chunk-1045f3nq.js} +305 -665
- package/dist/shared/{chunk-2w48kb4c.js → chunk-cp1jj1n1.js} +2 -2
- package/dist/shared/{chunk-agpezzpd.js → chunk-tmhsq5ft.js} +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/skills/githits-package/SKILL.md +2 -2
- package/skills/githits-package/references/package.md +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__require,
|
|
3
3
|
version
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-tmhsq5ft.js";
|
|
5
5
|
|
|
6
6
|
// src/services/app-config-paths.ts
|
|
7
7
|
var APP_DIR = "githits";
|
|
@@ -57,6 +57,7 @@ function getLegacyMacAuthFileStorageDir(fs) {
|
|
|
57
57
|
function getLegacyMacAuthFileStorageDirForEnv(fs, env) {
|
|
58
58
|
return fs.joinPath(getLegacyMacAppConfigDirForEnv(fs, env), "auth");
|
|
59
59
|
}
|
|
60
|
+
|
|
60
61
|
// src/services/auth-config.ts
|
|
61
62
|
import { parse as parseToml } from "smol-toml";
|
|
62
63
|
import { z } from "zod";
|
|
@@ -126,6 +127,7 @@ async function loadAuthConfig(fs) {
|
|
|
126
127
|
throw error;
|
|
127
128
|
}
|
|
128
129
|
}
|
|
130
|
+
|
|
129
131
|
// src/services/auth-service.ts
|
|
130
132
|
import { createServer } from "node:http";
|
|
131
133
|
|
|
@@ -796,6 +798,7 @@ var HELP_CTA = ctaBlock("Explore available commands with:", [
|
|
|
796
798
|
function escapeHtml(text) {
|
|
797
799
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
798
800
|
}
|
|
801
|
+
|
|
799
802
|
// src/services/auth-storage.ts
|
|
800
803
|
var AUTH_FILE = "auth.json";
|
|
801
804
|
var CLIENT_FILE = "client.json";
|
|
@@ -1002,6 +1005,7 @@ function isAuthSessionMetadata(value) {
|
|
|
1002
1005
|
return false;
|
|
1003
1006
|
return typeof value.createdAt === "string" && value.createdAt.length > 0 && typeof value.updatedAt === "string" && value.updatedAt.length > 0 && (value.expiresAt === null || typeof value.expiresAt === "string" && value.expiresAt.length > 0);
|
|
1004
1007
|
}
|
|
1008
|
+
|
|
1005
1009
|
// src/services/browser-service.ts
|
|
1006
1010
|
import open from "open";
|
|
1007
1011
|
|
|
@@ -1010,6 +1014,7 @@ class BrowserServiceImpl {
|
|
|
1010
1014
|
await open(url);
|
|
1011
1015
|
}
|
|
1012
1016
|
}
|
|
1017
|
+
|
|
1013
1018
|
// src/services/chunking-keyring-service.ts
|
|
1014
1019
|
var WINDOWS_MAX_ENTRY_SIZE = 1200;
|
|
1015
1020
|
var CHUNKED_PREFIX = "CHUNKED:";
|
|
@@ -1121,30 +1126,7 @@ class ChunkingKeyringService {
|
|
|
1121
1126
|
}
|
|
1122
1127
|
}
|
|
1123
1128
|
}
|
|
1124
|
-
// src/services/client-update-required-error.ts
|
|
1125
|
-
var CLIENT_UPDATE_REQUIRED_REASON = "Backend protocol changed";
|
|
1126
1129
|
|
|
1127
|
-
class ClientUpdateRequiredError extends Error {
|
|
1128
|
-
reason;
|
|
1129
|
-
currentVersion;
|
|
1130
|
-
constructor(message = `Update required: ${CLIENT_UPDATE_REQUIRED_REASON}`, reason = CLIENT_UPDATE_REQUIRED_REASON, currentVersion = version) {
|
|
1131
|
-
super(message);
|
|
1132
|
-
this.reason = reason;
|
|
1133
|
-
this.currentVersion = currentVersion;
|
|
1134
|
-
this.name = "ClientUpdateRequiredError";
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
function isClientUpdateRequiredGraphQLError(input) {
|
|
1138
|
-
return input.code === "CLIENT_UPDATE_REQUIRED";
|
|
1139
|
-
}
|
|
1140
|
-
function isGraphQLSchemaMismatchError(input) {
|
|
1141
|
-
if (!isGraphQLSchemaMismatchMessage(input.message))
|
|
1142
|
-
return false;
|
|
1143
|
-
return !input.code || input.code === "GRAPHQL_VALIDATION_FAILED" || input.code === "BAD_USER_INPUT";
|
|
1144
|
-
}
|
|
1145
|
-
function isGraphQLSchemaMismatchMessage(message) {
|
|
1146
|
-
return /Cannot query field|Field .* does not exist|Unknown argument|Unknown type|Unknown field/i.test(message);
|
|
1147
|
-
}
|
|
1148
1130
|
// src/services/code-navigation-service.ts
|
|
1149
1131
|
import { z as z2 } from "zod";
|
|
1150
1132
|
|
|
@@ -1188,158 +1170,6 @@ function isExplicitOnlyArea(area) {
|
|
|
1188
1170
|
return area === "code-nav-wire";
|
|
1189
1171
|
}
|
|
1190
1172
|
|
|
1191
|
-
// src/shared/request-headers.ts
|
|
1192
|
-
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
1193
|
-
var MAX_HEADER_BYTES = 256;
|
|
1194
|
-
var SESSION_ENV_VARS = [
|
|
1195
|
-
"TERM_SESSION_ID",
|
|
1196
|
-
"ITERM_SESSION_ID",
|
|
1197
|
-
"WEZTERM_PANE",
|
|
1198
|
-
"KITTY_PID",
|
|
1199
|
-
"ALACRITTY_SOCKET",
|
|
1200
|
-
"WT_SESSION",
|
|
1201
|
-
"VSCODE_PID",
|
|
1202
|
-
"SUPERSET_PANE_ID",
|
|
1203
|
-
"SUPERSET_WORKSPACE_ID",
|
|
1204
|
-
"STARSHIP_SESSION_KEY",
|
|
1205
|
-
"SSH_CONNECTION"
|
|
1206
|
-
];
|
|
1207
|
-
var cachedSessionId;
|
|
1208
|
-
function resolveRawSessionId(env = process.env, ppid = process.ppid) {
|
|
1209
|
-
for (const key of SESSION_ENV_VARS) {
|
|
1210
|
-
const value = env[key];
|
|
1211
|
-
if (value && value.trim().length > 0) {
|
|
1212
|
-
return value.trim();
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
if (typeof ppid === "number" && !Number.isNaN(ppid) && ppid > 0) {
|
|
1216
|
-
return String(ppid);
|
|
1217
|
-
}
|
|
1218
|
-
return randomUUID();
|
|
1219
|
-
}
|
|
1220
|
-
function getSessionId(env, ppid) {
|
|
1221
|
-
if (cachedSessionId !== undefined && env === undefined && ppid === undefined) {
|
|
1222
|
-
return cachedSessionId;
|
|
1223
|
-
}
|
|
1224
|
-
const raw = resolveRawSessionId(env, ppid);
|
|
1225
|
-
const hashed = hashValue(raw);
|
|
1226
|
-
if (env === undefined && ppid === undefined) {
|
|
1227
|
-
cachedSessionId = hashed;
|
|
1228
|
-
}
|
|
1229
|
-
return hashed;
|
|
1230
|
-
}
|
|
1231
|
-
function hashValue(input) {
|
|
1232
|
-
return createHash2("sha256").update(input).digest("hex").slice(0, 16);
|
|
1233
|
-
}
|
|
1234
|
-
var AGENT_PROBES = [
|
|
1235
|
-
{ envVar: "OPENCODE", name: "opencode" },
|
|
1236
|
-
{ envVar: "CLAUDECODE", name: "claude-code" },
|
|
1237
|
-
{ envVar: "CURSOR_TRACE_ID", name: "cursor" },
|
|
1238
|
-
{ envVar: "WINDSURF_CONFIG_DIR", name: "windsurf" },
|
|
1239
|
-
{ envVar: "ZED_TERM", name: "zed" },
|
|
1240
|
-
{ envVar: "VSCODE_PID", name: "vscode" }
|
|
1241
|
-
];
|
|
1242
|
-
function parseAgentString(raw) {
|
|
1243
|
-
const trimmed = raw.trim();
|
|
1244
|
-
if (trimmed.length === 0)
|
|
1245
|
-
return;
|
|
1246
|
-
const slashIndex = trimmed.indexOf("/");
|
|
1247
|
-
if (slashIndex === -1)
|
|
1248
|
-
return { name: trimmed };
|
|
1249
|
-
const name = trimmed.slice(0, slashIndex);
|
|
1250
|
-
const ver = trimmed.slice(slashIndex + 1);
|
|
1251
|
-
if (name.length === 0)
|
|
1252
|
-
return;
|
|
1253
|
-
return { name, version: ver || undefined };
|
|
1254
|
-
}
|
|
1255
|
-
function formatAgentInfo(info) {
|
|
1256
|
-
return info.version ? `${info.name}/${info.version}` : info.name;
|
|
1257
|
-
}
|
|
1258
|
-
function resolveAgentInfo(env = process.env) {
|
|
1259
|
-
const explicit = env.GITHITS_AGENT;
|
|
1260
|
-
if (explicit && explicit.trim().length > 0) {
|
|
1261
|
-
return parseAgentString(explicit);
|
|
1262
|
-
}
|
|
1263
|
-
for (const probe of AGENT_PROBES) {
|
|
1264
|
-
const value = env[probe.envVar];
|
|
1265
|
-
if (value && value.trim().length > 0) {
|
|
1266
|
-
return { name: probe.name };
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
return;
|
|
1270
|
-
}
|
|
1271
|
-
var currentAgentInfo;
|
|
1272
|
-
var agentInfoInitialized = false;
|
|
1273
|
-
var agentInfoExplicitlySet = false;
|
|
1274
|
-
var mcpClientVersionProvider;
|
|
1275
|
-
function setMcpClientVersionProvider(provider) {
|
|
1276
|
-
mcpClientVersionProvider = provider;
|
|
1277
|
-
}
|
|
1278
|
-
function getAgentInfo(env) {
|
|
1279
|
-
if (agentInfoExplicitlySet) {
|
|
1280
|
-
return currentAgentInfo;
|
|
1281
|
-
}
|
|
1282
|
-
if (mcpClientVersionProvider) {
|
|
1283
|
-
try {
|
|
1284
|
-
const fromProvider = mcpClientVersionProvider();
|
|
1285
|
-
if (fromProvider && fromProvider.name.trim().length > 0) {
|
|
1286
|
-
return fromProvider;
|
|
1287
|
-
}
|
|
1288
|
-
} catch {}
|
|
1289
|
-
}
|
|
1290
|
-
if (!agentInfoInitialized || env !== undefined) {
|
|
1291
|
-
currentAgentInfo = resolveAgentInfo(env);
|
|
1292
|
-
if (env === undefined) {
|
|
1293
|
-
agentInfoInitialized = true;
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
return currentAgentInfo;
|
|
1297
|
-
}
|
|
1298
|
-
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
1299
|
-
function sanitizeHeaderValue(value) {
|
|
1300
|
-
if (value === undefined || value === null || typeof value !== "string") {
|
|
1301
|
-
return;
|
|
1302
|
-
}
|
|
1303
|
-
const cleaned = value.replace(CONTROL_CHARS, "").trim();
|
|
1304
|
-
if (cleaned.length === 0)
|
|
1305
|
-
return;
|
|
1306
|
-
if (Buffer.byteLength(cleaned, "utf8") > MAX_HEADER_BYTES)
|
|
1307
|
-
return;
|
|
1308
|
-
return cleaned;
|
|
1309
|
-
}
|
|
1310
|
-
var BASE_CLIENT_NAME = "githits-cli";
|
|
1311
|
-
var clientName = BASE_CLIENT_NAME;
|
|
1312
|
-
function setClientMode(mode) {
|
|
1313
|
-
clientName = `${BASE_CLIENT_NAME}/${mode}`;
|
|
1314
|
-
}
|
|
1315
|
-
function buildClientHeaders(env, ppid) {
|
|
1316
|
-
try {
|
|
1317
|
-
const headers = {};
|
|
1318
|
-
const name = sanitizeHeaderValue(clientName);
|
|
1319
|
-
if (name) {
|
|
1320
|
-
headers["x-githits-client-name"] = name;
|
|
1321
|
-
}
|
|
1322
|
-
const clientVersion = sanitizeHeaderValue(version);
|
|
1323
|
-
if (clientVersion) {
|
|
1324
|
-
headers["x-githits-client-version"] = clientVersion;
|
|
1325
|
-
}
|
|
1326
|
-
const agentInfo = getAgentInfo(env);
|
|
1327
|
-
if (agentInfo) {
|
|
1328
|
-
const agentValue = sanitizeHeaderValue(formatAgentInfo(agentInfo));
|
|
1329
|
-
if (agentValue) {
|
|
1330
|
-
headers["x-githits-agent"] = agentValue;
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
const sessionId = sanitizeHeaderValue(getSessionId(env, ppid));
|
|
1334
|
-
if (sessionId) {
|
|
1335
|
-
headers["x-githits-session-id"] = sessionId;
|
|
1336
|
-
}
|
|
1337
|
-
return headers;
|
|
1338
|
-
} catch {
|
|
1339
|
-
return {};
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
1173
|
// src/shared/pkgseer-graphql.ts
|
|
1344
1174
|
class PkgseerTransportError extends Error {
|
|
1345
1175
|
constructor(message, options) {
|
|
@@ -1351,14 +1181,14 @@ function baseUrl(endpointUrl) {
|
|
|
1351
1181
|
return endpointUrl.replace(/\/+$/, "");
|
|
1352
1182
|
}
|
|
1353
1183
|
async function postPkgseerGraphql(request) {
|
|
1354
|
-
const userAgent = request.userAgent ??
|
|
1184
|
+
const userAgent = request.userAgent ?? "githits-cli";
|
|
1355
1185
|
const timeoutMs = request.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
1356
1186
|
let response;
|
|
1357
1187
|
try {
|
|
1358
1188
|
response = await fetchWithTimeout(`${baseUrl(request.endpointUrl)}/api/graphql`, {
|
|
1359
1189
|
method: "POST",
|
|
1360
1190
|
headers: {
|
|
1361
|
-
...
|
|
1191
|
+
...request.clientHeaders?.(),
|
|
1362
1192
|
Authorization: `Bearer ${request.token}`,
|
|
1363
1193
|
"Content-Type": "application/json",
|
|
1364
1194
|
"User-Agent": userAgent
|
|
@@ -1394,6 +1224,31 @@ function parseJsonOrNull(body) {
|
|
|
1394
1224
|
}
|
|
1395
1225
|
}
|
|
1396
1226
|
|
|
1227
|
+
// src/services/client-update-required-error.ts
|
|
1228
|
+
var CLIENT_UPDATE_REQUIRED_REASON = "Backend protocol changed";
|
|
1229
|
+
|
|
1230
|
+
class ClientUpdateRequiredError extends Error {
|
|
1231
|
+
reason;
|
|
1232
|
+
currentVersion;
|
|
1233
|
+
constructor(message = `Update required: ${CLIENT_UPDATE_REQUIRED_REASON}`, reason = CLIENT_UPDATE_REQUIRED_REASON, currentVersion) {
|
|
1234
|
+
super(message);
|
|
1235
|
+
this.reason = reason;
|
|
1236
|
+
this.currentVersion = currentVersion;
|
|
1237
|
+
this.name = "ClientUpdateRequiredError";
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
function isClientUpdateRequiredGraphQLError(input) {
|
|
1241
|
+
return input.code === "CLIENT_UPDATE_REQUIRED";
|
|
1242
|
+
}
|
|
1243
|
+
function isGraphQLSchemaMismatchError(input) {
|
|
1244
|
+
if (!isGraphQLSchemaMismatchMessage(input.message))
|
|
1245
|
+
return false;
|
|
1246
|
+
return !input.code || input.code === "GRAPHQL_VALIDATION_FAILED" || input.code === "BAD_USER_INPUT";
|
|
1247
|
+
}
|
|
1248
|
+
function isGraphQLSchemaMismatchMessage(message) {
|
|
1249
|
+
return /Cannot query field|Field .* does not exist|Unknown argument|Unknown type|Unknown field/i.test(message);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1397
1252
|
// src/shared/telemetry.ts
|
|
1398
1253
|
import { writeSync } from "node:fs";
|
|
1399
1254
|
var ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
|
|
@@ -1535,10 +1390,16 @@ function formatMs(value) {
|
|
|
1535
1390
|
}
|
|
1536
1391
|
|
|
1537
1392
|
// src/services/githits-service.ts
|
|
1393
|
+
var AUTHENTICATION_REQUIRED_MESSAGE = "Authentication required.";
|
|
1394
|
+
var LOCAL_AUTHENTICATION_MISSING_MESSAGE = "No local GitHits authentication token found.";
|
|
1395
|
+
var SERVER_AUTHENTICATION_REJECTED_MESSAGE = "GitHits could not accept the authentication token.";
|
|
1396
|
+
|
|
1538
1397
|
class AuthenticationError extends Error {
|
|
1539
|
-
|
|
1398
|
+
source;
|
|
1399
|
+
constructor(message = AUTHENTICATION_REQUIRED_MESSAGE, source = "local") {
|
|
1540
1400
|
super(message);
|
|
1541
1401
|
this.name = "AuthenticationError";
|
|
1402
|
+
this.source = source;
|
|
1542
1403
|
}
|
|
1543
1404
|
}
|
|
1544
1405
|
function parseDetail(body) {
|
|
@@ -1559,11 +1420,13 @@ class GitHitsServiceImpl {
|
|
|
1559
1420
|
token;
|
|
1560
1421
|
fetchFn;
|
|
1561
1422
|
fetchTimeoutMs;
|
|
1562
|
-
|
|
1423
|
+
runtime;
|
|
1424
|
+
constructor(apiUrl, token, fetchFn, fetchTimeoutMs = DEFAULT_FETCH_TIMEOUT_MS, runtime = {}) {
|
|
1563
1425
|
this.apiUrl = apiUrl;
|
|
1564
1426
|
this.token = token;
|
|
1565
1427
|
this.fetchFn = fetchFn;
|
|
1566
1428
|
this.fetchTimeoutMs = fetchTimeoutMs;
|
|
1429
|
+
this.runtime = runtime;
|
|
1567
1430
|
}
|
|
1568
1431
|
async search(params) {
|
|
1569
1432
|
return withTelemetrySpan("githits.search.request", async () => {
|
|
@@ -1621,10 +1484,10 @@ class GitHitsServiceImpl {
|
|
|
1621
1484
|
}
|
|
1622
1485
|
headers() {
|
|
1623
1486
|
return {
|
|
1624
|
-
...
|
|
1487
|
+
...this.runtime.clientHeaders?.(),
|
|
1625
1488
|
Authorization: `Bearer ${this.token}`,
|
|
1626
1489
|
"Content-Type": "application/json",
|
|
1627
|
-
"User-Agent":
|
|
1490
|
+
"User-Agent": this.runtime.userAgent ?? "githits-cli"
|
|
1628
1491
|
};
|
|
1629
1492
|
}
|
|
1630
1493
|
fetchOptions() {
|
|
@@ -1638,7 +1501,7 @@ class GitHitsServiceImpl {
|
|
|
1638
1501
|
const body = await response.text().catch(() => "");
|
|
1639
1502
|
switch (status) {
|
|
1640
1503
|
case 401:
|
|
1641
|
-
return new AuthenticationError(
|
|
1504
|
+
return new AuthenticationError(SERVER_AUTHENTICATION_REJECTED_MESSAGE, "server");
|
|
1642
1505
|
case 403:
|
|
1643
1506
|
return new Error("Access denied.");
|
|
1644
1507
|
case 404:
|
|
@@ -1658,7 +1521,7 @@ class GitHitsServiceImpl {
|
|
|
1658
1521
|
async function executeWithTokenRefresh(options) {
|
|
1659
1522
|
const token = await options.getToken();
|
|
1660
1523
|
if (!token) {
|
|
1661
|
-
throw new AuthenticationError(
|
|
1524
|
+
throw new AuthenticationError(LOCAL_AUTHENTICATION_MISSING_MESSAGE, "local");
|
|
1662
1525
|
}
|
|
1663
1526
|
try {
|
|
1664
1527
|
return await options.executeWithToken(token);
|
|
@@ -2636,10 +2499,12 @@ class CodeNavigationServiceImpl {
|
|
|
2636
2499
|
codeNavigationUrl;
|
|
2637
2500
|
tokenProvider;
|
|
2638
2501
|
fetchFn;
|
|
2639
|
-
|
|
2502
|
+
runtime;
|
|
2503
|
+
constructor(codeNavigationUrl, tokenProvider, fetchFn = globalThis.fetch, runtime = {}) {
|
|
2640
2504
|
this.codeNavigationUrl = codeNavigationUrl;
|
|
2641
2505
|
this.tokenProvider = tokenProvider;
|
|
2642
2506
|
this.fetchFn = fetchFn;
|
|
2507
|
+
this.runtime = runtime;
|
|
2643
2508
|
}
|
|
2644
2509
|
async postGraphqlWithTargetResolutionFallback(input) {
|
|
2645
2510
|
const response = await postPkgseerGraphql({
|
|
@@ -2647,7 +2512,9 @@ class CodeNavigationServiceImpl {
|
|
|
2647
2512
|
token: input.token,
|
|
2648
2513
|
query: input.query,
|
|
2649
2514
|
variables: input.variables,
|
|
2650
|
-
fetchFn: this.fetchFn
|
|
2515
|
+
fetchFn: this.fetchFn,
|
|
2516
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
2517
|
+
userAgent: this.runtime.userAgent
|
|
2651
2518
|
});
|
|
2652
2519
|
if (response.status < 200 || response.status >= 300)
|
|
2653
2520
|
return response;
|
|
@@ -2662,7 +2529,9 @@ class CodeNavigationServiceImpl {
|
|
|
2662
2529
|
token: input.token,
|
|
2663
2530
|
query: fallbackQuery,
|
|
2664
2531
|
variables: input.variables,
|
|
2665
|
-
fetchFn: this.fetchFn
|
|
2532
|
+
fetchFn: this.fetchFn,
|
|
2533
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
2534
|
+
userAgent: this.runtime.userAgent
|
|
2666
2535
|
});
|
|
2667
2536
|
if (!hasSchemaMismatchErrors(fallbackResponse.parsedBody)) {
|
|
2668
2537
|
return fallbackResponse;
|
|
@@ -2791,7 +2660,7 @@ class CodeNavigationServiceImpl {
|
|
|
2791
2660
|
const status = response.status;
|
|
2792
2661
|
const detail = parseDetail2(response.responseBody);
|
|
2793
2662
|
if (status === 401) {
|
|
2794
|
-
return new AuthenticationError(
|
|
2663
|
+
return new AuthenticationError(SERVER_AUTHENTICATION_REJECTED_MESSAGE, "server");
|
|
2795
2664
|
}
|
|
2796
2665
|
if (status === 403) {
|
|
2797
2666
|
return new CodeNavigationAccessError(detail ?? "Code navigation access denied.");
|
|
@@ -2814,7 +2683,7 @@ class CodeNavigationServiceImpl {
|
|
|
2814
2683
|
const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
|
|
2815
2684
|
const indexingRef = getGraphQLIndexingRef(errors);
|
|
2816
2685
|
if (isClientUpdateRequiredGraphQLError({ message, code })) {
|
|
2817
|
-
return new ClientUpdateRequiredError;
|
|
2686
|
+
return new ClientUpdateRequiredError(undefined, undefined, this.runtime.clientVersion);
|
|
2818
2687
|
}
|
|
2819
2688
|
if (isGraphQLSchemaMismatchError({ message, code })) {
|
|
2820
2689
|
const sanitized = "Backend protocol mismatch. Your CLI may be newer than the server, or the server may require a newer CLI. Run `githits update-check` to verify your installed version. Set GITHITS_DEBUG=code-nav-wire to inspect GraphQL details during local development.";
|
|
@@ -2856,7 +2725,7 @@ class CodeNavigationServiceImpl {
|
|
|
2856
2725
|
case "FEATURE_FLAG_REQUIRED":
|
|
2857
2726
|
return new CodeNavigationFeatureFlagRequiredError(message);
|
|
2858
2727
|
case "UNAUTHORIZED":
|
|
2859
|
-
return new AuthenticationError(
|
|
2728
|
+
return new AuthenticationError(SERVER_AUTHENTICATION_REJECTED_MESSAGE, "server");
|
|
2860
2729
|
case "FORBIDDEN":
|
|
2861
2730
|
return new CodeNavigationAccessError("Code navigation access denied. This feature may not be enabled for your account.");
|
|
2862
2731
|
case "UPSTREAM_ERROR":
|
|
@@ -3459,6 +3328,7 @@ function isUnresolvableMessage(message) {
|
|
|
3459
3328
|
const lower = message.toLowerCase();
|
|
3460
3329
|
return lower.includes("could not resolve") || lower.includes("cannot resolve");
|
|
3461
3330
|
}
|
|
3331
|
+
|
|
3462
3332
|
// src/services/config.ts
|
|
3463
3333
|
var DEFAULT_MCP_URL = "https://mcp.githits.com";
|
|
3464
3334
|
var DEFAULT_API_URL = "https://api.githits.com";
|
|
@@ -3479,83 +3349,9 @@ function getCodeNavigationUrl() {
|
|
|
3479
3349
|
function getEnvApiToken() {
|
|
3480
3350
|
return process.env.GITHITS_API_TOKEN;
|
|
3481
3351
|
}
|
|
3482
|
-
// src/services/exec-service.ts
|
|
3483
|
-
import { spawn } from "node:child_process";
|
|
3484
|
-
var WINDOWS_CMD_META_CHARS = /([()[\]%!^"`<>&|;, *?])/g;
|
|
3485
|
-
function escapeWindowsCommand(value) {
|
|
3486
|
-
return value.replace(WINDOWS_CMD_META_CHARS, "^$1");
|
|
3487
|
-
}
|
|
3488
|
-
function escapeWindowsArgument(value) {
|
|
3489
|
-
let arg = `${value}`;
|
|
3490
|
-
arg = arg.replace(/(?=(\\+?)?)\1"/g, "$1$1\\\"");
|
|
3491
|
-
arg = arg.replace(/(?=(\\+?)?)\1$/, "$1$1");
|
|
3492
|
-
return `"${arg}"`.replace(WINDOWS_CMD_META_CHARS, "^$1");
|
|
3493
|
-
}
|
|
3494
|
-
function buildWindowsShellCommand(command, args) {
|
|
3495
|
-
return [
|
|
3496
|
-
escapeWindowsCommand(command),
|
|
3497
|
-
...args.map(escapeWindowsArgument)
|
|
3498
|
-
].join(" ");
|
|
3499
|
-
}
|
|
3500
|
-
function isWindowsCommandNotFound(exitCode, stderr, platform = process.platform) {
|
|
3501
|
-
return platform === "win32" && exitCode !== 0 && /^\s*'[^']+'\s+is not recognized as an internal or external command,/i.test(stderr);
|
|
3502
|
-
}
|
|
3503
|
-
function createCommandNotFoundError(command) {
|
|
3504
|
-
const error = new Error(`spawn ${command} ENOENT`);
|
|
3505
|
-
error.code = "ENOENT";
|
|
3506
|
-
error.syscall = "spawn";
|
|
3507
|
-
error.path = command;
|
|
3508
|
-
return error;
|
|
3509
|
-
}
|
|
3510
|
-
function normalizeSpawnCommand(command, args, platform = process.platform) {
|
|
3511
|
-
if (platform !== "win32") {
|
|
3512
|
-
return { command, args };
|
|
3513
|
-
}
|
|
3514
|
-
const shellCommand = buildWindowsShellCommand(command, args);
|
|
3515
|
-
return {
|
|
3516
|
-
command: process.env.ComSpec ?? "cmd.exe",
|
|
3517
|
-
args: ["/d", "/s", "/c", `"${shellCommand}"`],
|
|
3518
|
-
windowsVerbatimArguments: true
|
|
3519
|
-
};
|
|
3520
|
-
}
|
|
3521
3352
|
|
|
3522
|
-
class ExecServiceImpl {
|
|
3523
|
-
async exec(command, args) {
|
|
3524
|
-
return new Promise((resolve, reject) => {
|
|
3525
|
-
const spawnCommand = normalizeSpawnCommand(command, args);
|
|
3526
|
-
const child = spawn(spawnCommand.command, spawnCommand.args, {
|
|
3527
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3528
|
-
env: { ...process.env },
|
|
3529
|
-
...spawnCommand.shell !== undefined && { shell: spawnCommand.shell },
|
|
3530
|
-
...spawnCommand.windowsVerbatimArguments !== undefined && {
|
|
3531
|
-
windowsVerbatimArguments: spawnCommand.windowsVerbatimArguments
|
|
3532
|
-
}
|
|
3533
|
-
});
|
|
3534
|
-
const stdoutChunks = [];
|
|
3535
|
-
const stderrChunks = [];
|
|
3536
|
-
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
3537
|
-
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
3538
|
-
child.on("error", (error) => {
|
|
3539
|
-
reject(error);
|
|
3540
|
-
});
|
|
3541
|
-
child.on("close", (code) => {
|
|
3542
|
-
const exitCode = code ?? 1;
|
|
3543
|
-
const stderr = Buffer.concat(stderrChunks).toString("utf-8");
|
|
3544
|
-
if (isWindowsCommandNotFound(exitCode, stderr)) {
|
|
3545
|
-
reject(createCommandNotFoundError(command));
|
|
3546
|
-
return;
|
|
3547
|
-
}
|
|
3548
|
-
resolve({
|
|
3549
|
-
exitCode,
|
|
3550
|
-
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
3551
|
-
stderr
|
|
3552
|
-
});
|
|
3553
|
-
});
|
|
3554
|
-
});
|
|
3555
|
-
}
|
|
3556
|
-
}
|
|
3557
3353
|
// src/services/filesystem-service.ts
|
|
3558
|
-
import { randomUUID
|
|
3354
|
+
import { randomUUID } from "node:crypto";
|
|
3559
3355
|
import {
|
|
3560
3356
|
mkdir,
|
|
3561
3357
|
readdir,
|
|
@@ -3619,7 +3415,7 @@ class FileSystemServiceImpl {
|
|
|
3619
3415
|
}
|
|
3620
3416
|
}
|
|
3621
3417
|
async atomicWriteFile(path, contents) {
|
|
3622
|
-
const tmpPath = `${path}.${process.pid}.${
|
|
3418
|
+
const tmpPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
3623
3419
|
let mode = 384;
|
|
3624
3420
|
try {
|
|
3625
3421
|
const existing = await stat(path);
|
|
@@ -3636,6 +3432,7 @@ class FileSystemServiceImpl {
|
|
|
3636
3432
|
}
|
|
3637
3433
|
}
|
|
3638
3434
|
}
|
|
3435
|
+
|
|
3639
3436
|
// src/services/keychain-auth-storage.ts
|
|
3640
3437
|
var SERVICE_NAME = "githits";
|
|
3641
3438
|
var TOKEN_PREFIX = "v1:tokens:";
|
|
@@ -3734,6 +3531,7 @@ class KeychainAuthStorage {
|
|
|
3734
3531
|
}
|
|
3735
3532
|
}
|
|
3736
3533
|
}
|
|
3534
|
+
|
|
3737
3535
|
// src/services/keyring-service.ts
|
|
3738
3536
|
import { Entry } from "@napi-rs/keyring";
|
|
3739
3537
|
|
|
@@ -3772,14 +3570,16 @@ class KeyringServiceImpl {
|
|
|
3772
3570
|
}
|
|
3773
3571
|
}
|
|
3774
3572
|
}
|
|
3573
|
+
|
|
3775
3574
|
// src/services/locked-auth-storage.ts
|
|
3575
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3776
3576
|
import { execFile } from "node:child_process";
|
|
3777
|
-
import { randomUUID as
|
|
3577
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
3778
3578
|
import { mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
3779
3579
|
import { dirname as dirname2 } from "node:path";
|
|
3780
3580
|
import { promisify } from "node:util";
|
|
3781
3581
|
var LOCK_DIR = "auth.lock";
|
|
3782
|
-
var LOCK_TIMEOUT_MS = 1e4;
|
|
3582
|
+
var LOCK_TIMEOUT_MS = DEFAULT_FETCH_TIMEOUT_MS * 2 + 1e4;
|
|
3783
3583
|
var LOCK_RETRY_MS = 25;
|
|
3784
3584
|
var ORPHANED_LOCK_MS = 5000;
|
|
3785
3585
|
var OWNER_FILE = "owner.json";
|
|
@@ -3791,12 +3591,16 @@ class AuthStorageLockTimeoutError extends Error {
|
|
|
3791
3591
|
this.name = "AuthStorageLockTimeoutError";
|
|
3792
3592
|
}
|
|
3793
3593
|
}
|
|
3594
|
+
function withAuthStorageLock(storage, fn) {
|
|
3595
|
+
return storage.withAuthStorageLock(fn);
|
|
3596
|
+
}
|
|
3794
3597
|
|
|
3795
3598
|
class LockedAuthStorage {
|
|
3796
3599
|
storage;
|
|
3797
3600
|
lockPath;
|
|
3798
3601
|
lockTimeoutMs;
|
|
3799
3602
|
isOwnerAlive;
|
|
3603
|
+
lockContext = new AsyncLocalStorage;
|
|
3800
3604
|
currentOwner = null;
|
|
3801
3605
|
constructor(storage, fileSystemService, options = {}) {
|
|
3802
3606
|
this.storage = storage;
|
|
@@ -3808,52 +3612,63 @@ class LockedAuthStorage {
|
|
|
3808
3612
|
return this.storage.loadTokens(baseUrl2);
|
|
3809
3613
|
}
|
|
3810
3614
|
saveTokens(baseUrl2, data) {
|
|
3811
|
-
return this.
|
|
3615
|
+
return this.withAuthStorageLock(() => this.storage.saveTokens(baseUrl2, data));
|
|
3812
3616
|
}
|
|
3813
3617
|
saveTokensIfUnchanged(baseUrl2, expected, data) {
|
|
3814
|
-
return this.
|
|
3618
|
+
return this.withAuthStorageLock(() => this.storage.saveTokensIfUnchanged(baseUrl2, expected, data));
|
|
3815
3619
|
}
|
|
3816
3620
|
clearTokens(baseUrl2) {
|
|
3817
|
-
return this.
|
|
3621
|
+
return this.withAuthStorageLock(() => this.storage.clearTokens(baseUrl2));
|
|
3818
3622
|
}
|
|
3819
3623
|
clearTokensIfUnchanged(baseUrl2, expected) {
|
|
3820
|
-
return this.
|
|
3624
|
+
return this.withAuthStorageLock(() => this.storage.clearTokensIfUnchanged(baseUrl2, expected));
|
|
3821
3625
|
}
|
|
3822
3626
|
loadClient(baseUrl2) {
|
|
3823
3627
|
return this.storage.loadClient(baseUrl2);
|
|
3824
3628
|
}
|
|
3825
3629
|
saveClient(baseUrl2, data) {
|
|
3826
|
-
return this.
|
|
3630
|
+
return this.withAuthStorageLock(() => this.storage.saveClient(baseUrl2, data));
|
|
3827
3631
|
}
|
|
3828
3632
|
clearClient(baseUrl2) {
|
|
3829
|
-
return this.
|
|
3633
|
+
return this.withAuthStorageLock(() => this.storage.clearClient(baseUrl2));
|
|
3830
3634
|
}
|
|
3831
3635
|
saveAuthSession(baseUrl2, client, tokens) {
|
|
3832
|
-
return this.
|
|
3636
|
+
return this.withAuthStorageLock(() => this.storage.saveAuthSession(baseUrl2, client, tokens));
|
|
3833
3637
|
}
|
|
3834
3638
|
clearAuthSession(baseUrl2) {
|
|
3835
|
-
return this.
|
|
3639
|
+
return this.withAuthStorageLock(() => this.storage.clearAuthSession(baseUrl2));
|
|
3836
3640
|
}
|
|
3837
3641
|
getStorageLocation() {
|
|
3838
3642
|
return this.storage.getStorageLocation();
|
|
3839
3643
|
}
|
|
3840
|
-
async
|
|
3644
|
+
async withAuthStorageLock(fn) {
|
|
3645
|
+
const ownerId = this.lockContext.getStore();
|
|
3646
|
+
if (ownerId && this.currentOwner?.id === ownerId) {
|
|
3647
|
+
return fn();
|
|
3648
|
+
}
|
|
3841
3649
|
await this.acquireLock();
|
|
3650
|
+
const acquiredOwnerId = this.currentOwner?.id;
|
|
3842
3651
|
try {
|
|
3843
|
-
return await fn
|
|
3652
|
+
return await this.lockContext.run(acquiredOwnerId ?? "", fn);
|
|
3844
3653
|
} finally {
|
|
3845
3654
|
await this.releaseLock();
|
|
3846
3655
|
}
|
|
3847
3656
|
}
|
|
3848
3657
|
async acquireLock() {
|
|
3658
|
+
const processStartedAt = await getProcessStartedAt(process.pid);
|
|
3849
3659
|
const startedAt = Date.now();
|
|
3850
3660
|
await mkdir2(dirname2(this.lockPath), { recursive: true, mode: 448 });
|
|
3851
3661
|
while (true) {
|
|
3852
3662
|
try {
|
|
3853
3663
|
await mkdir2(this.lockPath, { recursive: false, mode: 448 });
|
|
3854
3664
|
try {
|
|
3855
|
-
await this.writeOwner();
|
|
3665
|
+
await this.writeOwner(processStartedAt);
|
|
3856
3666
|
} catch (error) {
|
|
3667
|
+
this.currentOwner = null;
|
|
3668
|
+
if (error.code === "EEXIST") {
|
|
3669
|
+
await sleep(LOCK_RETRY_MS);
|
|
3670
|
+
continue;
|
|
3671
|
+
}
|
|
3857
3672
|
await rm(this.lockPath, { recursive: true, force: true }).catch(() => {
|
|
3858
3673
|
return;
|
|
3859
3674
|
});
|
|
@@ -3871,15 +3686,18 @@ class LockedAuthStorage {
|
|
|
3871
3686
|
}
|
|
3872
3687
|
}
|
|
3873
3688
|
}
|
|
3874
|
-
async writeOwner() {
|
|
3689
|
+
async writeOwner(processStartedAt) {
|
|
3875
3690
|
const owner = {
|
|
3876
|
-
id:
|
|
3691
|
+
id: randomUUID2(),
|
|
3877
3692
|
pid: process.pid,
|
|
3878
3693
|
createdAt: new Date().toISOString(),
|
|
3879
|
-
processStartedAt
|
|
3694
|
+
processStartedAt
|
|
3880
3695
|
};
|
|
3696
|
+
await writeFile2(this.ownerPath(), JSON.stringify(owner), {
|
|
3697
|
+
mode: 384,
|
|
3698
|
+
flag: "wx"
|
|
3699
|
+
});
|
|
3881
3700
|
this.currentOwner = owner;
|
|
3882
|
-
await writeFile2(this.ownerPath(), JSON.stringify(owner), { mode: 384 });
|
|
3883
3701
|
}
|
|
3884
3702
|
async reclaimStaleLock() {
|
|
3885
3703
|
const owner = await this.readOwner();
|
|
@@ -3989,6 +3807,7 @@ async function lockCreatedAtMs(path) {
|
|
|
3989
3807
|
return 0;
|
|
3990
3808
|
}
|
|
3991
3809
|
}
|
|
3810
|
+
|
|
3992
3811
|
// src/services/mode-aware-file-auth-storage.ts
|
|
3993
3812
|
class AuthStoragePolicyError extends Error {
|
|
3994
3813
|
constructor(message) {
|
|
@@ -4420,6 +4239,7 @@ class MigratingAuthStorage {
|
|
|
4420
4239
|
return a.accessToken === b.accessToken && a.refreshToken === b.refreshToken && a.expiresAt === b.expiresAt && a.createdAt === b.createdAt;
|
|
4421
4240
|
}
|
|
4422
4241
|
}
|
|
4242
|
+
|
|
4423
4243
|
// src/services/package-intelligence-service.ts
|
|
4424
4244
|
import { z as z3 } from "zod";
|
|
4425
4245
|
|
|
@@ -5419,10 +5239,12 @@ class PackageIntelligenceServiceImpl {
|
|
|
5419
5239
|
endpointUrl;
|
|
5420
5240
|
tokenProvider;
|
|
5421
5241
|
fetchFn;
|
|
5422
|
-
|
|
5242
|
+
runtime;
|
|
5243
|
+
constructor(endpointUrl, tokenProvider, fetchFn = globalThis.fetch, runtime = {}) {
|
|
5423
5244
|
this.endpointUrl = endpointUrl;
|
|
5424
5245
|
this.tokenProvider = tokenProvider;
|
|
5425
5246
|
this.fetchFn = fetchFn;
|
|
5247
|
+
this.runtime = runtime;
|
|
5426
5248
|
}
|
|
5427
5249
|
async packageSummary(params) {
|
|
5428
5250
|
return withTelemetrySpan("pkg-intel.summary.request", () => executeWithTokenRefresh({
|
|
@@ -5443,7 +5265,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
5443
5265
|
registry: params.registry,
|
|
5444
5266
|
name: params.packageName
|
|
5445
5267
|
},
|
|
5446
|
-
fetchFn: this.fetchFn
|
|
5268
|
+
fetchFn: this.fetchFn,
|
|
5269
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5270
|
+
userAgent: this.runtime.userAgent
|
|
5447
5271
|
});
|
|
5448
5272
|
} catch (cause) {
|
|
5449
5273
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -5471,7 +5295,7 @@ class PackageIntelligenceServiceImpl {
|
|
|
5471
5295
|
const status = response.status;
|
|
5472
5296
|
const detail = parseDetail3(response.responseBody);
|
|
5473
5297
|
if (status === 401) {
|
|
5474
|
-
return new AuthenticationError(
|
|
5298
|
+
return new AuthenticationError(SERVER_AUTHENTICATION_REJECTED_MESSAGE, "server");
|
|
5475
5299
|
}
|
|
5476
5300
|
if (status === 403) {
|
|
5477
5301
|
return new PackageIntelligenceAccessError(detail ?? "Access denied.");
|
|
@@ -5493,7 +5317,7 @@ class PackageIntelligenceServiceImpl {
|
|
|
5493
5317
|
const code = typeof extensions?.code === "string" ? extensions.code : undefined;
|
|
5494
5318
|
const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
|
|
5495
5319
|
if (isClientUpdateRequiredGraphQLError({ message, code })) {
|
|
5496
|
-
return new ClientUpdateRequiredError;
|
|
5320
|
+
return new ClientUpdateRequiredError(undefined, undefined, this.runtime.clientVersion);
|
|
5497
5321
|
}
|
|
5498
5322
|
if (isGraphQLSchemaMismatchError({ message, code })) {
|
|
5499
5323
|
const sanitized = "Backend protocol mismatch. Your CLI may be newer than the server, or the server may require a newer CLI. Run `githits update-check` to verify your installed version. Set GITHITS_DEBUG=pkg-graphql to inspect GraphQL details during local development.";
|
|
@@ -5516,7 +5340,7 @@ class PackageIntelligenceServiceImpl {
|
|
|
5516
5340
|
case "FEATURE_FLAG_REQUIRED":
|
|
5517
5341
|
return new PackageIntelligenceFeatureFlagRequiredError(message);
|
|
5518
5342
|
case "UNAUTHORIZED":
|
|
5519
|
-
return new AuthenticationError(
|
|
5343
|
+
return new AuthenticationError(SERVER_AUTHENTICATION_REJECTED_MESSAGE, "server");
|
|
5520
5344
|
case "FORBIDDEN":
|
|
5521
5345
|
return new PackageIntelligenceAccessError("Access denied. This feature may not be enabled for your account.");
|
|
5522
5346
|
case "UPSTREAM_ERROR":
|
|
@@ -5654,7 +5478,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
5654
5478
|
scope: params.advisoryScope,
|
|
5655
5479
|
after
|
|
5656
5480
|
},
|
|
5657
|
-
fetchFn: this.fetchFn
|
|
5481
|
+
fetchFn: this.fetchFn,
|
|
5482
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5483
|
+
userAgent: this.runtime.userAgent
|
|
5658
5484
|
});
|
|
5659
5485
|
} catch (cause) {
|
|
5660
5486
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -5758,7 +5584,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
5758
5584
|
lifecycle: params.includeGroups === true ? ["peer"] : undefined,
|
|
5759
5585
|
minSeverity: params.minSeverity
|
|
5760
5586
|
},
|
|
5761
|
-
fetchFn: this.fetchFn
|
|
5587
|
+
fetchFn: this.fetchFn,
|
|
5588
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5589
|
+
userAgent: this.runtime.userAgent
|
|
5762
5590
|
});
|
|
5763
5591
|
} catch (cause) {
|
|
5764
5592
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -5797,7 +5625,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
5797
5625
|
maxDepth: params.maxDepth,
|
|
5798
5626
|
lifecycle: params.lifecycle && params.lifecycle.length > 0 ? params.lifecycle : undefined
|
|
5799
5627
|
},
|
|
5800
|
-
fetchFn: this.fetchFn
|
|
5628
|
+
fetchFn: this.fetchFn,
|
|
5629
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5630
|
+
userAgent: this.runtime.userAgent
|
|
5801
5631
|
});
|
|
5802
5632
|
} catch (cause) {
|
|
5803
5633
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -6034,7 +5864,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
6034
5864
|
toVersion: params.toVersion,
|
|
6035
5865
|
limit: params.limit
|
|
6036
5866
|
},
|
|
6037
|
-
fetchFn: this.fetchFn
|
|
5867
|
+
fetchFn: this.fetchFn,
|
|
5868
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5869
|
+
userAgent: this.runtime.userAgent
|
|
6038
5870
|
});
|
|
6039
5871
|
} catch (cause) {
|
|
6040
5872
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -6109,7 +5941,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
6109
5941
|
limit: params.limit,
|
|
6110
5942
|
after: params.after
|
|
6111
5943
|
},
|
|
6112
|
-
fetchFn: this.fetchFn
|
|
5944
|
+
fetchFn: this.fetchFn,
|
|
5945
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5946
|
+
userAgent: this.runtime.userAgent
|
|
6113
5947
|
});
|
|
6114
5948
|
} catch (cause) {
|
|
6115
5949
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -6178,7 +6012,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
6178
6012
|
variables: {
|
|
6179
6013
|
pageId: params.pageId
|
|
6180
6014
|
},
|
|
6181
|
-
fetchFn: this.fetchFn
|
|
6015
|
+
fetchFn: this.fetchFn,
|
|
6016
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
6017
|
+
userAgent: this.runtime.userAgent
|
|
6182
6018
|
});
|
|
6183
6019
|
} catch (cause) {
|
|
6184
6020
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -6263,44 +6099,18 @@ function parseVersionList(raw) {
|
|
|
6263
6099
|
}
|
|
6264
6100
|
return versions.length > 0 ? versions : undefined;
|
|
6265
6101
|
}
|
|
6266
|
-
// src/services/prompt-service.ts
|
|
6267
|
-
import { checkbox, confirm, select } from "@inquirer/prompts";
|
|
6268
6102
|
|
|
6269
|
-
class PromptServiceImpl {
|
|
6270
|
-
async select(message, choices, defaultValue) {
|
|
6271
|
-
return select({ message, choices, default: defaultValue });
|
|
6272
|
-
}
|
|
6273
|
-
async checkbox(message, choices) {
|
|
6274
|
-
return checkbox({ message, choices });
|
|
6275
|
-
}
|
|
6276
|
-
async confirm(message, defaultValue) {
|
|
6277
|
-
return confirm({ message, default: defaultValue });
|
|
6278
|
-
}
|
|
6279
|
-
async confirm3(message, defaultValue) {
|
|
6280
|
-
return select({
|
|
6281
|
-
message,
|
|
6282
|
-
default: defaultValue,
|
|
6283
|
-
choices: [
|
|
6284
|
-
{ value: "yes", name: "Yes" },
|
|
6285
|
-
{ value: "no", name: "No" },
|
|
6286
|
-
{
|
|
6287
|
-
value: "always",
|
|
6288
|
-
name: "Yes to all",
|
|
6289
|
-
description: "Skip confirmation for remaining agents"
|
|
6290
|
-
}
|
|
6291
|
-
]
|
|
6292
|
-
});
|
|
6293
|
-
}
|
|
6294
|
-
}
|
|
6295
6103
|
// src/services/refreshing-githits-service.ts
|
|
6296
6104
|
class RefreshingGitHitsService {
|
|
6297
6105
|
apiUrl;
|
|
6298
6106
|
tokenProvider;
|
|
6299
6107
|
serviceFactory;
|
|
6300
|
-
|
|
6108
|
+
runtime;
|
|
6109
|
+
constructor(apiUrl, tokenProvider, serviceFactory, runtime = {}) {
|
|
6301
6110
|
this.apiUrl = apiUrl;
|
|
6302
6111
|
this.tokenProvider = tokenProvider;
|
|
6303
6112
|
this.serviceFactory = serviceFactory;
|
|
6113
|
+
this.runtime = runtime;
|
|
6304
6114
|
}
|
|
6305
6115
|
async search(params) {
|
|
6306
6116
|
return this.withTokenRefresh((service) => service.search(params));
|
|
@@ -6317,12 +6127,13 @@ class RefreshingGitHitsService {
|
|
|
6317
6127
|
forceRefresh: () => this.tokenProvider.forceRefresh(),
|
|
6318
6128
|
shouldRefresh: (error) => error instanceof AuthenticationError,
|
|
6319
6129
|
executeWithToken: async (token) => {
|
|
6320
|
-
const service = this.serviceFactory(this.apiUrl, token);
|
|
6130
|
+
const service = this.serviceFactory ? this.serviceFactory(this.apiUrl, token) : new GitHitsServiceImpl(this.apiUrl, token, undefined, undefined, this.runtime);
|
|
6321
6131
|
return operation(service);
|
|
6322
6132
|
}
|
|
6323
6133
|
});
|
|
6324
6134
|
}
|
|
6325
6135
|
}
|
|
6136
|
+
|
|
6326
6137
|
// src/services/token-manager.ts
|
|
6327
6138
|
var PROACTIVE_REFRESH_RATIO = 0.9;
|
|
6328
6139
|
function shouldRefreshToken(token, ratio, now) {
|
|
@@ -6361,11 +6172,19 @@ class TokenManager {
|
|
|
6361
6172
|
}
|
|
6362
6173
|
async getToken() {
|
|
6363
6174
|
return withTelemetrySpan("token-manager.get-token", async () => {
|
|
6364
|
-
|
|
6365
|
-
|
|
6175
|
+
const activeForceRefresh = this.forceRefreshPromise;
|
|
6176
|
+
if (activeForceRefresh) {
|
|
6177
|
+
return (await activeForceRefresh).accessToken;
|
|
6366
6178
|
}
|
|
6367
6179
|
if (!this.cachedToken) {
|
|
6368
|
-
|
|
6180
|
+
const storedToken = await withTelemetrySpan("token-manager.load-tokens", () => this.authStorage.loadTokens(this.mcpUrl));
|
|
6181
|
+
const startedForceRefresh = this.forceRefreshPromise;
|
|
6182
|
+
if (startedForceRefresh) {
|
|
6183
|
+
return (await startedForceRefresh).accessToken;
|
|
6184
|
+
}
|
|
6185
|
+
if (!this.cachedToken) {
|
|
6186
|
+
this.cachedToken = storedToken;
|
|
6187
|
+
}
|
|
6369
6188
|
if (!this.cachedToken)
|
|
6370
6189
|
return;
|
|
6371
6190
|
}
|
|
@@ -6374,9 +6193,7 @@ class TokenManager {
|
|
|
6374
6193
|
if (!shouldRefresh) {
|
|
6375
6194
|
return currentToken;
|
|
6376
6195
|
}
|
|
6377
|
-
const refreshedToken = await this.
|
|
6378
|
-
allowFreshExternalToken: true
|
|
6379
|
-
});
|
|
6196
|
+
const refreshedToken = await this.refreshFromGetToken();
|
|
6380
6197
|
if (refreshedToken) {
|
|
6381
6198
|
return refreshedToken;
|
|
6382
6199
|
}
|
|
@@ -6387,48 +6204,52 @@ class TokenManager {
|
|
|
6387
6204
|
});
|
|
6388
6205
|
}
|
|
6389
6206
|
async forceRefresh() {
|
|
6390
|
-
return withTelemetrySpan("token-manager.force-refresh", () => this.
|
|
6207
|
+
return withTelemetrySpan("token-manager.force-refresh", () => this.refreshAfterAuthFailure());
|
|
6391
6208
|
}
|
|
6392
|
-
async
|
|
6393
|
-
const result = await this.
|
|
6209
|
+
async refreshFromGetToken() {
|
|
6210
|
+
const result = await this.softRefresh();
|
|
6394
6211
|
return result.accessToken;
|
|
6395
6212
|
}
|
|
6396
|
-
async
|
|
6397
|
-
if (!options.allowFreshExternalToken) {
|
|
6398
|
-
if (this.forceRefreshPromise)
|
|
6399
|
-
return this.forceRefreshPromise;
|
|
6400
|
-
this.forceRefreshPromise = (async () => {
|
|
6401
|
-
const softResult = await this.softRefreshPromise?.catch(() => {
|
|
6402
|
-
return;
|
|
6403
|
-
});
|
|
6404
|
-
if (softResult?.accessToken && softResult.refreshedViaEndpoint) {
|
|
6405
|
-
return softResult;
|
|
6406
|
-
}
|
|
6407
|
-
return this.executeRefresh(options);
|
|
6408
|
-
})();
|
|
6409
|
-
try {
|
|
6410
|
-
return await this.forceRefreshPromise;
|
|
6411
|
-
} finally {
|
|
6412
|
-
this.forceRefreshPromise = null;
|
|
6413
|
-
}
|
|
6414
|
-
}
|
|
6213
|
+
async softRefresh() {
|
|
6415
6214
|
if (this.forceRefreshPromise)
|
|
6416
6215
|
return this.forceRefreshPromise;
|
|
6417
6216
|
if (this.softRefreshPromise)
|
|
6418
6217
|
return this.softRefreshPromise;
|
|
6419
|
-
this.softRefreshPromise = this.executeRefresh(
|
|
6218
|
+
this.softRefreshPromise = this.executeRefresh();
|
|
6420
6219
|
try {
|
|
6421
6220
|
return await this.softRefreshPromise;
|
|
6422
6221
|
} finally {
|
|
6423
6222
|
this.softRefreshPromise = null;
|
|
6424
6223
|
}
|
|
6425
6224
|
}
|
|
6426
|
-
async
|
|
6427
|
-
|
|
6225
|
+
async refreshAfterAuthFailure() {
|
|
6226
|
+
const result = await this.forceEndpointRefresh();
|
|
6227
|
+
return result.accessToken;
|
|
6228
|
+
}
|
|
6229
|
+
async forceEndpointRefresh() {
|
|
6230
|
+
if (this.forceRefreshPromise)
|
|
6231
|
+
return this.forceRefreshPromise;
|
|
6232
|
+
this.forceRefreshPromise = (async () => {
|
|
6233
|
+
const softResult = await this.softRefreshPromise?.catch(() => {
|
|
6234
|
+
return;
|
|
6235
|
+
});
|
|
6236
|
+
if (softResult?.accessToken && softResult.refreshedViaEndpoint) {
|
|
6237
|
+
return softResult;
|
|
6238
|
+
}
|
|
6239
|
+
return this.executeRefresh();
|
|
6240
|
+
})();
|
|
6241
|
+
try {
|
|
6242
|
+
return await this.forceRefreshPromise;
|
|
6243
|
+
} finally {
|
|
6244
|
+
this.forceRefreshPromise = null;
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
async executeRefresh() {
|
|
6248
|
+
return withAuthStorageLock(this.authStorage, () => withTelemetrySpan("token-manager.refresh", async () => {
|
|
6428
6249
|
const candidate = await this.loadRefreshCandidate();
|
|
6429
6250
|
if (!candidate)
|
|
6430
6251
|
return refreshResult(undefined, false);
|
|
6431
|
-
if (candidate.externallyUpdated
|
|
6252
|
+
if (candidate.externallyUpdated) {
|
|
6432
6253
|
const { shouldRefresh } = shouldRefreshToken(candidate.tokens, PROACTIVE_REFRESH_RATIO, new Date);
|
|
6433
6254
|
if (!shouldRefresh)
|
|
6434
6255
|
return refreshResult(candidate.tokens.accessToken, false);
|
|
@@ -6481,7 +6302,7 @@ class TokenManager {
|
|
|
6481
6302
|
}
|
|
6482
6303
|
this.cachedToken = newTokenData;
|
|
6483
6304
|
return refreshResult(response.accessToken, true);
|
|
6484
|
-
});
|
|
6305
|
+
}));
|
|
6485
6306
|
}
|
|
6486
6307
|
async resolveSuccessfulRefreshConflict(refreshedFrom, response, newTokenData) {
|
|
6487
6308
|
const currentToken = await withTelemetrySpan("token-manager.reload-tokens", () => this.authStorage.loadTokens(this.mcpUrl));
|
|
@@ -6534,330 +6355,139 @@ function areSameTokenData(a, b) {
|
|
|
6534
6355
|
function refreshResult(accessToken, refreshedViaEndpoint) {
|
|
6535
6356
|
return { accessToken, refreshedViaEndpoint };
|
|
6536
6357
|
}
|
|
6537
|
-
// src/services/update-check-service.ts
|
|
6538
|
-
import semver from "semver";
|
|
6539
|
-
var NPM_DIST_TAGS_URL = "https://registry.npmjs.org/-/package/githits/dist-tags";
|
|
6540
|
-
var NPM_PACKAGE_VERSION_URL = "https://registry.npmjs.org/githits";
|
|
6541
|
-
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
6542
|
-
var FETCH_TIMEOUT_MS = 1000;
|
|
6543
|
-
var DIR_MODE3 = 448;
|
|
6544
|
-
var UPDATE_COMMAND = "npm i -g githits@latest";
|
|
6545
|
-
var MAX_DEPRECATION_REASON_LENGTH = 200;
|
|
6546
6358
|
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
const cache = await this.loadCache();
|
|
6570
|
-
const latestDue = !cache || this.isCheckDue(cache);
|
|
6571
|
-
const currentVersionStatusDue = this.isCurrentVersionStatusDue(cache?.currentVersionStatus);
|
|
6572
|
-
const currentVersionStatus = await this.refreshCurrentVersionStatusIfDue(cache?.currentVersionStatus, signal);
|
|
6573
|
-
const currentVersionStatusChanged = currentVersionStatusDue && !sameCurrentVersionStatus(currentVersionStatus, cache?.currentVersionStatus);
|
|
6574
|
-
if (cache && !latestDue) {
|
|
6575
|
-
if (currentVersionStatusChanged) {
|
|
6576
|
-
await this.saveCache({
|
|
6577
|
-
...cache,
|
|
6578
|
-
currentVersionStatus
|
|
6579
|
-
});
|
|
6580
|
-
}
|
|
6581
|
-
return this.noticeFromLatest(cache.latestVersion);
|
|
6582
|
-
}
|
|
6583
|
-
const latestVersion = await this.fetchLatestVersion(signal);
|
|
6584
|
-
if (!latestVersion) {
|
|
6585
|
-
if (currentVersionStatusChanged) {
|
|
6586
|
-
await this.saveCache({
|
|
6587
|
-
...cache,
|
|
6588
|
-
currentVersionStatus
|
|
6589
|
-
});
|
|
6590
|
-
}
|
|
6591
|
-
return this.noticeFromLatest(cache?.latestVersion);
|
|
6592
|
-
}
|
|
6593
|
-
await this.saveCache({
|
|
6594
|
-
checkedAt: this.now().toISOString(),
|
|
6595
|
-
latestVersion,
|
|
6596
|
-
currentVersionStatus
|
|
6597
|
-
});
|
|
6598
|
-
return this.noticeFromLatest(latestVersion);
|
|
6599
|
-
}
|
|
6600
|
-
async refreshRequiredUpdateStatus(signal) {
|
|
6601
|
-
const cache = await this.loadCache();
|
|
6602
|
-
const currentVersionStatusDue = this.isCurrentVersionStatusDue(cache?.currentVersionStatus);
|
|
6603
|
-
const currentVersionStatus = await this.refreshCurrentVersionStatusIfDue(cache?.currentVersionStatus, signal);
|
|
6604
|
-
if (currentVersionStatusDue && !sameCurrentVersionStatus(currentVersionStatus, cache?.currentVersionStatus)) {
|
|
6605
|
-
await this.saveCache({
|
|
6606
|
-
...cache,
|
|
6607
|
-
currentVersionStatus
|
|
6608
|
-
});
|
|
6609
|
-
}
|
|
6610
|
-
}
|
|
6611
|
-
async getRequiredUpdateNotice() {
|
|
6612
|
-
const cache = await this.loadCache();
|
|
6613
|
-
const status = cache?.currentVersionStatus;
|
|
6614
|
-
if (!status || status.version !== this.currentVersion || !status.deprecatedReason) {
|
|
6615
|
-
return;
|
|
6616
|
-
}
|
|
6617
|
-
return {
|
|
6618
|
-
currentVersion: this.currentVersion,
|
|
6619
|
-
...cache.latestVersion ? { latestKnownVersion: cache.latestVersion } : {},
|
|
6620
|
-
reason: status.deprecatedReason,
|
|
6621
|
-
updateCommand: formatUpdateCommand(this.env)
|
|
6622
|
-
};
|
|
6623
|
-
}
|
|
6624
|
-
noticeFromLatest(latestVersion) {
|
|
6625
|
-
if (!latestVersion || !semver.valid(latestVersion) || !semver.valid(this.currentVersion) || !semver.gt(latestVersion, this.currentVersion)) {
|
|
6626
|
-
return;
|
|
6627
|
-
}
|
|
6628
|
-
return {
|
|
6629
|
-
currentVersion: this.currentVersion,
|
|
6630
|
-
latestVersion,
|
|
6631
|
-
updateCommand: UPDATE_COMMAND
|
|
6632
|
-
};
|
|
6633
|
-
}
|
|
6634
|
-
isCheckDue(cache) {
|
|
6635
|
-
if (!cache.checkedAt || !cache.latestVersion) {
|
|
6636
|
-
return true;
|
|
6637
|
-
}
|
|
6638
|
-
const checkedAtMs = Date.parse(cache.checkedAt);
|
|
6639
|
-
if (Number.isNaN(checkedAtMs)) {
|
|
6640
|
-
return true;
|
|
6641
|
-
}
|
|
6642
|
-
return this.now().getTime() - checkedAtMs >= this.checkIntervalMs;
|
|
6643
|
-
}
|
|
6644
|
-
isCurrentVersionStatusDue(status) {
|
|
6645
|
-
if (!status || status.version !== this.currentVersion) {
|
|
6646
|
-
return true;
|
|
6647
|
-
}
|
|
6648
|
-
const checkedAtMs = Date.parse(status.checkedAt);
|
|
6649
|
-
if (Number.isNaN(checkedAtMs)) {
|
|
6650
|
-
return true;
|
|
6651
|
-
}
|
|
6652
|
-
return this.now().getTime() - checkedAtMs >= this.checkIntervalMs;
|
|
6653
|
-
}
|
|
6654
|
-
async refreshCurrentVersionStatusIfDue(cached, signal) {
|
|
6655
|
-
const reusableCached = cached?.version === this.currentVersion ? cached : undefined;
|
|
6656
|
-
if (!this.isCurrentVersionStatusDue(reusableCached)) {
|
|
6657
|
-
return reusableCached;
|
|
6658
|
-
}
|
|
6659
|
-
const remote = await this.fetchCurrentVersionStatus(signal);
|
|
6660
|
-
if (!remote) {
|
|
6661
|
-
return reusableCached;
|
|
6662
|
-
}
|
|
6663
|
-
return remote;
|
|
6664
|
-
}
|
|
6665
|
-
async fetchLatestVersion(signal) {
|
|
6666
|
-
try {
|
|
6667
|
-
const timeoutSignal = AbortSignal.timeout(this.fetchTimeoutMs);
|
|
6668
|
-
const response = await this.fetcher(NPM_DIST_TAGS_URL, {
|
|
6669
|
-
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
|
|
6670
|
-
});
|
|
6671
|
-
if (!response.ok) {
|
|
6672
|
-
return;
|
|
6673
|
-
}
|
|
6674
|
-
const body = await response.json();
|
|
6675
|
-
if (!body || typeof body !== "object" || typeof body.latest !== "string") {
|
|
6676
|
-
return;
|
|
6677
|
-
}
|
|
6678
|
-
const latest = body.latest;
|
|
6679
|
-
return semver.valid(latest) ? latest : undefined;
|
|
6680
|
-
} catch {
|
|
6681
|
-
return;
|
|
6682
|
-
}
|
|
6683
|
-
}
|
|
6684
|
-
async fetchCurrentVersionStatus(signal) {
|
|
6685
|
-
try {
|
|
6686
|
-
const timeoutSignal = AbortSignal.timeout(this.fetchTimeoutMs);
|
|
6687
|
-
const response = await this.fetcher(`${NPM_PACKAGE_VERSION_URL}/${this.currentVersion}`, {
|
|
6688
|
-
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
|
|
6689
|
-
});
|
|
6690
|
-
if (!response.ok) {
|
|
6691
|
-
return;
|
|
6692
|
-
}
|
|
6693
|
-
const body = await response.json();
|
|
6694
|
-
if (!body || typeof body !== "object") {
|
|
6695
|
-
return;
|
|
6696
|
-
}
|
|
6697
|
-
const rawDeprecated = body.deprecated;
|
|
6698
|
-
const deprecatedReason = typeof rawDeprecated === "string" ? sanitizeDeprecationReason(rawDeprecated) : undefined;
|
|
6699
|
-
return {
|
|
6700
|
-
version: this.currentVersion,
|
|
6701
|
-
checkedAt: this.now().toISOString(),
|
|
6702
|
-
...deprecatedReason ? { deprecatedReason } : {}
|
|
6703
|
-
};
|
|
6704
|
-
} catch {
|
|
6705
|
-
return;
|
|
6706
|
-
}
|
|
6707
|
-
}
|
|
6708
|
-
async loadCache() {
|
|
6709
|
-
try {
|
|
6710
|
-
if (!await this.fs.exists(this.cachePath)) {
|
|
6711
|
-
return;
|
|
6712
|
-
}
|
|
6713
|
-
const raw = await this.fs.readFile(this.cachePath);
|
|
6714
|
-
const parsed = JSON.parse(raw);
|
|
6715
|
-
const currentVersionStatus = parseCurrentVersionStatus(parsed.currentVersionStatus);
|
|
6716
|
-
const hasLatest = typeof parsed.checkedAt === "string" && typeof parsed.latestVersion === "string";
|
|
6717
|
-
if (!hasLatest && !currentVersionStatus) {
|
|
6718
|
-
return;
|
|
6719
|
-
}
|
|
6720
|
-
return {
|
|
6721
|
-
...hasLatest ? {
|
|
6722
|
-
checkedAt: parsed.checkedAt,
|
|
6723
|
-
latestVersion: parsed.latestVersion
|
|
6724
|
-
} : {},
|
|
6725
|
-
currentVersionStatus
|
|
6726
|
-
};
|
|
6727
|
-
} catch {
|
|
6728
|
-
return;
|
|
6359
|
+
// src/shared/request-headers.ts
|
|
6360
|
+
import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
|
|
6361
|
+
var MAX_HEADER_BYTES = 256;
|
|
6362
|
+
var SESSION_ENV_VARS = [
|
|
6363
|
+
"TERM_SESSION_ID",
|
|
6364
|
+
"ITERM_SESSION_ID",
|
|
6365
|
+
"WEZTERM_PANE",
|
|
6366
|
+
"KITTY_PID",
|
|
6367
|
+
"ALACRITTY_SOCKET",
|
|
6368
|
+
"WT_SESSION",
|
|
6369
|
+
"VSCODE_PID",
|
|
6370
|
+
"SUPERSET_PANE_ID",
|
|
6371
|
+
"SUPERSET_WORKSPACE_ID",
|
|
6372
|
+
"STARSHIP_SESSION_KEY",
|
|
6373
|
+
"SSH_CONNECTION"
|
|
6374
|
+
];
|
|
6375
|
+
var cachedSessionId;
|
|
6376
|
+
function resolveRawSessionId(env = process.env, ppid = process.ppid) {
|
|
6377
|
+
for (const key of SESSION_ENV_VARS) {
|
|
6378
|
+
const value = env[key];
|
|
6379
|
+
if (value && value.trim().length > 0) {
|
|
6380
|
+
return value.trim();
|
|
6729
6381
|
}
|
|
6730
6382
|
}
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
await this.fs.ensureDir(this.configDir, DIR_MODE3);
|
|
6734
|
-
if (typeof this.fs.atomicWriteFile === "function") {
|
|
6735
|
-
await this.fs.atomicWriteFile(this.cachePath, `${JSON.stringify(cache, null, 2)}
|
|
6736
|
-
`);
|
|
6737
|
-
return;
|
|
6738
|
-
}
|
|
6739
|
-
await this.fs.writeFile(this.cachePath, `${JSON.stringify(cache, null, 2)}
|
|
6740
|
-
`, 384);
|
|
6741
|
-
} catch {}
|
|
6742
|
-
}
|
|
6743
|
-
}
|
|
6744
|
-
function resolveConfigHome(env, fs) {
|
|
6745
|
-
const xdgConfigHome = env.XDG_CONFIG_HOME?.trim();
|
|
6746
|
-
if (xdgConfigHome) {
|
|
6747
|
-
return xdgConfigHome;
|
|
6383
|
+
if (typeof ppid === "number" && !Number.isNaN(ppid) && ppid > 0) {
|
|
6384
|
+
return String(ppid);
|
|
6748
6385
|
}
|
|
6749
|
-
return
|
|
6386
|
+
return randomUUID3();
|
|
6750
6387
|
}
|
|
6751
|
-
function
|
|
6752
|
-
if (
|
|
6753
|
-
return
|
|
6754
|
-
}
|
|
6755
|
-
const env = input.env ?? process.env;
|
|
6756
|
-
if (env.CI || env.GITHITS_DISABLE_UPDATE_CHECK) {
|
|
6757
|
-
return false;
|
|
6758
|
-
}
|
|
6759
|
-
if (isHelpOrVersionInvocation(input.args)) {
|
|
6760
|
-
return false;
|
|
6761
|
-
}
|
|
6762
|
-
if (isLikelyEphemeralPackageRunner(env)) {
|
|
6763
|
-
return false;
|
|
6388
|
+
function getSessionId(env, ppid) {
|
|
6389
|
+
if (cachedSessionId !== undefined && env === undefined && ppid === undefined) {
|
|
6390
|
+
return cachedSessionId;
|
|
6764
6391
|
}
|
|
6765
|
-
|
|
6766
|
-
|
|
6392
|
+
const raw = resolveRawSessionId(env, ppid);
|
|
6393
|
+
const hashed = hashValue(raw);
|
|
6394
|
+
if (env === undefined && ppid === undefined) {
|
|
6395
|
+
cachedSessionId = hashed;
|
|
6767
6396
|
}
|
|
6768
|
-
return
|
|
6397
|
+
return hashed;
|
|
6769
6398
|
}
|
|
6770
|
-
function
|
|
6771
|
-
|
|
6772
|
-
return false;
|
|
6773
|
-
}
|
|
6774
|
-
const env = input.env ?? process.env;
|
|
6775
|
-
if (isLikelyEphemeralPackageRunner(env)) {
|
|
6776
|
-
return false;
|
|
6777
|
-
}
|
|
6778
|
-
return true;
|
|
6399
|
+
function hashValue(input) {
|
|
6400
|
+
return createHash2("sha256").update(input).digest("hex").slice(0, 16);
|
|
6779
6401
|
}
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6402
|
+
var AGENT_PROBES = [
|
|
6403
|
+
{ envVar: "OPENCODE", name: "opencode" },
|
|
6404
|
+
{ envVar: "CLAUDECODE", name: "claude-code" },
|
|
6405
|
+
{ envVar: "CURSOR_TRACE_ID", name: "cursor" },
|
|
6406
|
+
{ envVar: "WINDSURF_CONFIG_DIR", name: "windsurf" },
|
|
6407
|
+
{ envVar: "ZED_TERM", name: "zed" },
|
|
6408
|
+
{ envVar: "VSCODE_PID", name: "vscode" }
|
|
6409
|
+
];
|
|
6410
|
+
function parseAgentString(raw) {
|
|
6411
|
+
const trimmed = raw.trim();
|
|
6412
|
+
if (trimmed.length === 0)
|
|
6413
|
+
return;
|
|
6414
|
+
const slashIndex = trimmed.indexOf("/");
|
|
6415
|
+
if (slashIndex === -1)
|
|
6416
|
+
return { name: trimmed };
|
|
6417
|
+
const name = trimmed.slice(0, slashIndex);
|
|
6418
|
+
const ver = trimmed.slice(slashIndex + 1);
|
|
6419
|
+
if (name.length === 0)
|
|
6420
|
+
return;
|
|
6421
|
+
return { name, version: ver || undefined };
|
|
6783
6422
|
}
|
|
6784
|
-
function
|
|
6785
|
-
|
|
6786
|
-
`Update required: ${notice.reason}`,
|
|
6787
|
-
"",
|
|
6788
|
-
`Installed githits ${notice.currentVersion} is no longer supported.`
|
|
6789
|
-
];
|
|
6790
|
-
if (notice.latestKnownVersion) {
|
|
6791
|
-
lines.push(`Latest known version: ${notice.latestKnownVersion}`);
|
|
6792
|
-
}
|
|
6793
|
-
lines.push("Update with:", ` ${notice.updateCommand}`);
|
|
6794
|
-
return [...lines].join(`
|
|
6795
|
-
`);
|
|
6423
|
+
function formatAgentInfo(info) {
|
|
6424
|
+
return info.version ? `${info.name}/${info.version}` : info.name;
|
|
6796
6425
|
}
|
|
6797
|
-
function
|
|
6798
|
-
const
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
if (signal.includes("pnpm")) {
|
|
6802
|
-
return "pnpm add -g githits@latest";
|
|
6803
|
-
}
|
|
6804
|
-
if (signal.includes("yarn")) {
|
|
6805
|
-
return "yarn global add githits@latest";
|
|
6426
|
+
function resolveAgentInfo(env = process.env) {
|
|
6427
|
+
const explicit = env.GITHITS_AGENT;
|
|
6428
|
+
if (explicit && explicit.trim().length > 0) {
|
|
6429
|
+
return parseAgentString(explicit);
|
|
6806
6430
|
}
|
|
6807
|
-
|
|
6808
|
-
|
|
6431
|
+
for (const probe of AGENT_PROBES) {
|
|
6432
|
+
const value = env[probe.envVar];
|
|
6433
|
+
if (value && value.trim().length > 0) {
|
|
6434
|
+
return { name: probe.name };
|
|
6435
|
+
}
|
|
6809
6436
|
}
|
|
6810
|
-
return
|
|
6437
|
+
return;
|
|
6811
6438
|
}
|
|
6812
|
-
|
|
6813
|
-
|
|
6439
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
6440
|
+
function sanitizeHeaderValue(value) {
|
|
6441
|
+
if (value === undefined || value === null || typeof value !== "string") {
|
|
6814
6442
|
return;
|
|
6815
6443
|
}
|
|
6816
|
-
const
|
|
6817
|
-
if (
|
|
6444
|
+
const cleaned = value.replace(CONTROL_CHARS, "").trim();
|
|
6445
|
+
if (cleaned.length === 0)
|
|
6818
6446
|
return;
|
|
6819
|
-
|
|
6820
|
-
if (typeof status.checkedAt !== "string") {
|
|
6447
|
+
if (Buffer.byteLength(cleaned, "utf8") > MAX_HEADER_BYTES)
|
|
6821
6448
|
return;
|
|
6822
|
-
|
|
6823
|
-
const deprecatedReason = typeof status.deprecatedReason === "string" ? sanitizeDeprecationReason(status.deprecatedReason) : undefined;
|
|
6824
|
-
return {
|
|
6825
|
-
version: status.version,
|
|
6826
|
-
checkedAt: status.checkedAt,
|
|
6827
|
-
...deprecatedReason ? { deprecatedReason } : {}
|
|
6828
|
-
};
|
|
6829
|
-
}
|
|
6830
|
-
function sameCurrentVersionStatus(left, right) {
|
|
6831
|
-
return left?.version === right?.version && left?.checkedAt === right?.checkedAt && left?.deprecatedReason === right?.deprecatedReason;
|
|
6832
|
-
}
|
|
6833
|
-
function sanitizeDeprecationReason(value) {
|
|
6834
|
-
const sanitized = Array.from(value).map((character) => isControlCharacter(character) ? " " : character).join("").replace(/\s+/g, " ").trim().slice(0, MAX_DEPRECATION_REASON_LENGTH).trim();
|
|
6835
|
-
return sanitized.length > 0 ? sanitized : undefined;
|
|
6836
|
-
}
|
|
6837
|
-
function isControlCharacter(value) {
|
|
6838
|
-
const code = value.charCodeAt(0);
|
|
6839
|
-
return code <= 31 || code >= 127 && code <= 159;
|
|
6840
|
-
}
|
|
6841
|
-
function isHelpOrVersionInvocation(args) {
|
|
6842
|
-
return args.length === 0 || args[0] === "help" || args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-V");
|
|
6449
|
+
return cleaned;
|
|
6843
6450
|
}
|
|
6844
|
-
function
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6451
|
+
function createClientHeaderBuilder(options) {
|
|
6452
|
+
return () => buildClientHeadersWithContext({
|
|
6453
|
+
clientName: options.clientName,
|
|
6454
|
+
clientVersion: options.clientVersion,
|
|
6455
|
+
agentProvider: options.agentProvider,
|
|
6456
|
+
env: options.env,
|
|
6457
|
+
ppid: options.ppid
|
|
6458
|
+
});
|
|
6851
6459
|
}
|
|
6852
|
-
function
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6460
|
+
function buildClientHeadersWithContext(context) {
|
|
6461
|
+
try {
|
|
6462
|
+
const headers = {};
|
|
6463
|
+
const name = sanitizeHeaderValue(context.clientName);
|
|
6464
|
+
if (name) {
|
|
6465
|
+
headers["x-githits-client-name"] = name;
|
|
6466
|
+
}
|
|
6467
|
+
const safeClientVersion = sanitizeHeaderValue(context.clientVersion);
|
|
6468
|
+
if (safeClientVersion) {
|
|
6469
|
+
headers["x-githits-client-version"] = safeClientVersion;
|
|
6470
|
+
}
|
|
6471
|
+
const agentInfo = context.agentProvider?.() ?? resolveAgentInfo(context.env);
|
|
6472
|
+
if (agentInfo) {
|
|
6473
|
+
const agentValue = sanitizeHeaderValue(formatAgentInfo(agentInfo));
|
|
6474
|
+
if (agentValue) {
|
|
6475
|
+
headers["x-githits-agent"] = agentValue;
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
const sessionId = sanitizeHeaderValue(getSessionId(context.env, context.ppid));
|
|
6479
|
+
if (sessionId) {
|
|
6480
|
+
headers["x-githits-session-id"] = sessionId;
|
|
6481
|
+
}
|
|
6482
|
+
return headers;
|
|
6483
|
+
} catch {
|
|
6484
|
+
return {};
|
|
6856
6485
|
}
|
|
6857
|
-
const remainingArgs = args.slice(firstTokenIndex + 1);
|
|
6858
|
-
return remainingArgs.includes("start") || !stdinIsTTY || !stdoutIsTTY;
|
|
6859
6486
|
}
|
|
6487
|
+
|
|
6860
6488
|
// src/container.ts
|
|
6489
|
+
var BASE_CLIENT_NAME = "githits-cli";
|
|
6490
|
+
var USER_AGENT = `${BASE_CLIENT_NAME}/${version}`;
|
|
6861
6491
|
async function createAuthStorage(fileSystemService) {
|
|
6862
6492
|
return withTelemetrySpan("container.create-auth-storage", async () => {
|
|
6863
6493
|
const authConfig = await loadAuthConfig(fileSystemService);
|
|
@@ -6937,12 +6567,22 @@ async function createContainer(options = {}) {
|
|
|
6937
6567
|
const fileSystemService = new FileSystemServiceImpl;
|
|
6938
6568
|
const authService = new AuthServiceImpl;
|
|
6939
6569
|
const browserService = new BrowserServiceImpl;
|
|
6570
|
+
const clientHeaders = createClientHeaderBuilder({
|
|
6571
|
+
clientName: options.clientName ?? BASE_CLIENT_NAME,
|
|
6572
|
+
clientVersion: version,
|
|
6573
|
+
agentProvider: options.agentProvider
|
|
6574
|
+
});
|
|
6575
|
+
const serviceRuntime = {
|
|
6576
|
+
clientHeaders,
|
|
6577
|
+
userAgent: USER_AGENT,
|
|
6578
|
+
clientVersion: version
|
|
6579
|
+
};
|
|
6940
6580
|
const envToken = getEnvApiToken();
|
|
6941
6581
|
if (envToken) {
|
|
6942
6582
|
const authStorage2 = createAuthStorageForMode(fileSystemService, "keychain");
|
|
6943
6583
|
const tokenProvider = createStaticTokenProvider(envToken);
|
|
6944
|
-
const codeNavigationService2 = new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider);
|
|
6945
|
-
const packageIntelligenceService2 = new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider);
|
|
6584
|
+
const codeNavigationService2 = new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider, globalThis.fetch, serviceRuntime);
|
|
6585
|
+
const packageIntelligenceService2 = new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider, globalThis.fetch, serviceRuntime);
|
|
6946
6586
|
return {
|
|
6947
6587
|
authStorage: authStorage2,
|
|
6948
6588
|
authService,
|
|
@@ -6956,7 +6596,7 @@ async function createContainer(options = {}) {
|
|
|
6956
6596
|
codeNavigationUrl,
|
|
6957
6597
|
codeNavigationService: codeNavigationService2,
|
|
6958
6598
|
packageIntelligenceService: packageIntelligenceService2,
|
|
6959
|
-
githitsService: new GitHitsServiceImpl(apiUrl, envToken)
|
|
6599
|
+
githitsService: new GitHitsServiceImpl(apiUrl, envToken, undefined, undefined, serviceRuntime)
|
|
6960
6600
|
};
|
|
6961
6601
|
}
|
|
6962
6602
|
const authStorage = await createAuthStorage(fileSystemService);
|
|
@@ -6965,8 +6605,8 @@ async function createContainer(options = {}) {
|
|
|
6965
6605
|
if (resolveStoredToken && apiToken === undefined) {
|
|
6966
6606
|
await new AuthSessionMetadataStorage(fileSystemService).clear(mcpUrl);
|
|
6967
6607
|
}
|
|
6968
|
-
const codeNavigationService = new CodeNavigationServiceImpl(codeNavigationUrl, tokenManager);
|
|
6969
|
-
const packageIntelligenceService = new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenManager);
|
|
6608
|
+
const codeNavigationService = new CodeNavigationServiceImpl(codeNavigationUrl, tokenManager, globalThis.fetch, serviceRuntime);
|
|
6609
|
+
const packageIntelligenceService = new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenManager, globalThis.fetch, serviceRuntime);
|
|
6970
6610
|
return {
|
|
6971
6611
|
authStorage,
|
|
6972
6612
|
authService,
|
|
@@ -6980,9 +6620,9 @@ async function createContainer(options = {}) {
|
|
|
6980
6620
|
codeNavigationUrl,
|
|
6981
6621
|
codeNavigationService,
|
|
6982
6622
|
packageIntelligenceService,
|
|
6983
|
-
githitsService: new RefreshingGitHitsService(apiUrl, tokenManager)
|
|
6623
|
+
githitsService: new RefreshingGitHitsService(apiUrl, tokenManager, undefined, serviceRuntime)
|
|
6984
6624
|
};
|
|
6985
6625
|
});
|
|
6986
6626
|
}
|
|
6987
6627
|
|
|
6988
|
-
export { getAppConfigDirForEnv, getAuthConfigPathForEnv, getAuthFileStorageDirForEnv, getLegacyAuthStorageDirForEnv, getLegacyMacAuthConfigPathForEnv, getLegacyMacAuthFileStorageDirForEnv, AuthConfigError, parseAuthStorageMode,
|
|
6628
|
+
export { getAppConfigDirForEnv, getAuthConfigPathForEnv, getAuthFileStorageDirForEnv, getLegacyAuthStorageDirForEnv, getLegacyMacAuthConfigPathForEnv, getLegacyMacAuthFileStorageDirForEnv, AuthConfigError, parseAuthStorageMode, AuthStorageLockTimeoutError, AuthStoragePolicyError, isTelemetryEnabled, withTelemetrySpan, startTelemetrySpan, endTelemetrySpan, flushTelemetry, LOCAL_AUTHENTICATION_MISSING_MESSAGE, AuthenticationError, normalizeBaseUrl, debugLog, CLIENT_UPDATE_REQUIRED_REASON, ClientUpdateRequiredError, CodeNavigationAccessError, CodeNavigationGraphQLError, CodeNavigationIndexingError, CodeNavigationUnresolvableError, MalformedCodeNavigationResponseError, CodeNavigationTargetNotFoundError, CodeNavigationFileNotFoundError, CodeNavigationVersionNotFoundError, CodeNavigationValidationError, CodeNavigationFeatureFlagRequiredError, CodeNavigationNetworkError, CodeNavigationBackendError, DEFAULT_MCP_URL, DEFAULT_API_URL, DEFAULT_CODE_NAV_URL, getCodeNavigationUrl, FileSystemServiceImpl, PackageIntelligenceAccessError, PackageIntelligenceFeatureFlagRequiredError, PackageIntelligenceNetworkError, PackageIntelligenceBackendError, PackageIntelligenceGraphQLError, PackageIntelligenceTargetNotFoundError, PackageIntelligenceValidationError, PackageIntelligenceVersionNotFoundError, MalformedPackageIntelligenceResponseError, PackageIntelligenceChangelogSourceNotFoundError, refreshExpiredToken, loadAutoLoginAuthSessionMetadata, clearAutoLoginAuthSessionMetadata, createAuthCommandDependencies, createAuthStatusDependencies, createContainer };
|