githits 0.4.10 → 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 +12493 -11337
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-p9ak72j0.js → chunk-1045f3nq.js} +305 -631
- package/dist/shared/{chunk-mw1910qd.js → chunk-cp1jj1n1.js} +2 -2
- package/dist/shared/{chunk-fvpvx4x4.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,53 +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
|
-
function isWindowsAbsolutePath(command) {
|
|
3485
|
-
return /^[a-zA-Z]:[\\/]/.test(command) || command.startsWith("\\\\");
|
|
3486
|
-
}
|
|
3487
|
-
function normalizeSpawnCommand(command, args, platform = process.platform) {
|
|
3488
|
-
if (platform !== "win32") {
|
|
3489
|
-
return { command, args };
|
|
3490
|
-
}
|
|
3491
|
-
if (isWindowsAbsolutePath(command) && /\s/.test(command)) {
|
|
3492
|
-
return {
|
|
3493
|
-
command: `"${command.replaceAll('"', "\\\"")}"`,
|
|
3494
|
-
args,
|
|
3495
|
-
shell: true
|
|
3496
|
-
};
|
|
3497
|
-
}
|
|
3498
|
-
return { command, args, shell: true };
|
|
3499
|
-
}
|
|
3500
3352
|
|
|
3501
|
-
class ExecServiceImpl {
|
|
3502
|
-
async exec(command, args) {
|
|
3503
|
-
return new Promise((resolve, reject) => {
|
|
3504
|
-
const spawnCommand = normalizeSpawnCommand(command, args);
|
|
3505
|
-
const child = spawn(spawnCommand.command, spawnCommand.args, {
|
|
3506
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3507
|
-
env: { ...process.env },
|
|
3508
|
-
...spawnCommand.shell !== undefined && { shell: spawnCommand.shell }
|
|
3509
|
-
});
|
|
3510
|
-
const stdoutChunks = [];
|
|
3511
|
-
const stderrChunks = [];
|
|
3512
|
-
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
3513
|
-
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
3514
|
-
child.on("error", (error) => {
|
|
3515
|
-
reject(error);
|
|
3516
|
-
});
|
|
3517
|
-
child.on("close", (code) => {
|
|
3518
|
-
resolve({
|
|
3519
|
-
exitCode: code ?? 1,
|
|
3520
|
-
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
3521
|
-
stderr: Buffer.concat(stderrChunks).toString("utf-8")
|
|
3522
|
-
});
|
|
3523
|
-
});
|
|
3524
|
-
});
|
|
3525
|
-
}
|
|
3526
|
-
}
|
|
3527
3353
|
// src/services/filesystem-service.ts
|
|
3528
|
-
import { randomUUID
|
|
3354
|
+
import { randomUUID } from "node:crypto";
|
|
3529
3355
|
import {
|
|
3530
3356
|
mkdir,
|
|
3531
3357
|
readdir,
|
|
@@ -3589,7 +3415,7 @@ class FileSystemServiceImpl {
|
|
|
3589
3415
|
}
|
|
3590
3416
|
}
|
|
3591
3417
|
async atomicWriteFile(path, contents) {
|
|
3592
|
-
const tmpPath = `${path}.${process.pid}.${
|
|
3418
|
+
const tmpPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
3593
3419
|
let mode = 384;
|
|
3594
3420
|
try {
|
|
3595
3421
|
const existing = await stat(path);
|
|
@@ -3606,6 +3432,7 @@ class FileSystemServiceImpl {
|
|
|
3606
3432
|
}
|
|
3607
3433
|
}
|
|
3608
3434
|
}
|
|
3435
|
+
|
|
3609
3436
|
// src/services/keychain-auth-storage.ts
|
|
3610
3437
|
var SERVICE_NAME = "githits";
|
|
3611
3438
|
var TOKEN_PREFIX = "v1:tokens:";
|
|
@@ -3704,6 +3531,7 @@ class KeychainAuthStorage {
|
|
|
3704
3531
|
}
|
|
3705
3532
|
}
|
|
3706
3533
|
}
|
|
3534
|
+
|
|
3707
3535
|
// src/services/keyring-service.ts
|
|
3708
3536
|
import { Entry } from "@napi-rs/keyring";
|
|
3709
3537
|
|
|
@@ -3742,14 +3570,16 @@ class KeyringServiceImpl {
|
|
|
3742
3570
|
}
|
|
3743
3571
|
}
|
|
3744
3572
|
}
|
|
3573
|
+
|
|
3745
3574
|
// src/services/locked-auth-storage.ts
|
|
3575
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3746
3576
|
import { execFile } from "node:child_process";
|
|
3747
|
-
import { randomUUID as
|
|
3577
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
3748
3578
|
import { mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
3749
3579
|
import { dirname as dirname2 } from "node:path";
|
|
3750
3580
|
import { promisify } from "node:util";
|
|
3751
3581
|
var LOCK_DIR = "auth.lock";
|
|
3752
|
-
var LOCK_TIMEOUT_MS = 1e4;
|
|
3582
|
+
var LOCK_TIMEOUT_MS = DEFAULT_FETCH_TIMEOUT_MS * 2 + 1e4;
|
|
3753
3583
|
var LOCK_RETRY_MS = 25;
|
|
3754
3584
|
var ORPHANED_LOCK_MS = 5000;
|
|
3755
3585
|
var OWNER_FILE = "owner.json";
|
|
@@ -3761,12 +3591,16 @@ class AuthStorageLockTimeoutError extends Error {
|
|
|
3761
3591
|
this.name = "AuthStorageLockTimeoutError";
|
|
3762
3592
|
}
|
|
3763
3593
|
}
|
|
3594
|
+
function withAuthStorageLock(storage, fn) {
|
|
3595
|
+
return storage.withAuthStorageLock(fn);
|
|
3596
|
+
}
|
|
3764
3597
|
|
|
3765
3598
|
class LockedAuthStorage {
|
|
3766
3599
|
storage;
|
|
3767
3600
|
lockPath;
|
|
3768
3601
|
lockTimeoutMs;
|
|
3769
3602
|
isOwnerAlive;
|
|
3603
|
+
lockContext = new AsyncLocalStorage;
|
|
3770
3604
|
currentOwner = null;
|
|
3771
3605
|
constructor(storage, fileSystemService, options = {}) {
|
|
3772
3606
|
this.storage = storage;
|
|
@@ -3778,52 +3612,63 @@ class LockedAuthStorage {
|
|
|
3778
3612
|
return this.storage.loadTokens(baseUrl2);
|
|
3779
3613
|
}
|
|
3780
3614
|
saveTokens(baseUrl2, data) {
|
|
3781
|
-
return this.
|
|
3615
|
+
return this.withAuthStorageLock(() => this.storage.saveTokens(baseUrl2, data));
|
|
3782
3616
|
}
|
|
3783
3617
|
saveTokensIfUnchanged(baseUrl2, expected, data) {
|
|
3784
|
-
return this.
|
|
3618
|
+
return this.withAuthStorageLock(() => this.storage.saveTokensIfUnchanged(baseUrl2, expected, data));
|
|
3785
3619
|
}
|
|
3786
3620
|
clearTokens(baseUrl2) {
|
|
3787
|
-
return this.
|
|
3621
|
+
return this.withAuthStorageLock(() => this.storage.clearTokens(baseUrl2));
|
|
3788
3622
|
}
|
|
3789
3623
|
clearTokensIfUnchanged(baseUrl2, expected) {
|
|
3790
|
-
return this.
|
|
3624
|
+
return this.withAuthStorageLock(() => this.storage.clearTokensIfUnchanged(baseUrl2, expected));
|
|
3791
3625
|
}
|
|
3792
3626
|
loadClient(baseUrl2) {
|
|
3793
3627
|
return this.storage.loadClient(baseUrl2);
|
|
3794
3628
|
}
|
|
3795
3629
|
saveClient(baseUrl2, data) {
|
|
3796
|
-
return this.
|
|
3630
|
+
return this.withAuthStorageLock(() => this.storage.saveClient(baseUrl2, data));
|
|
3797
3631
|
}
|
|
3798
3632
|
clearClient(baseUrl2) {
|
|
3799
|
-
return this.
|
|
3633
|
+
return this.withAuthStorageLock(() => this.storage.clearClient(baseUrl2));
|
|
3800
3634
|
}
|
|
3801
3635
|
saveAuthSession(baseUrl2, client, tokens) {
|
|
3802
|
-
return this.
|
|
3636
|
+
return this.withAuthStorageLock(() => this.storage.saveAuthSession(baseUrl2, client, tokens));
|
|
3803
3637
|
}
|
|
3804
3638
|
clearAuthSession(baseUrl2) {
|
|
3805
|
-
return this.
|
|
3639
|
+
return this.withAuthStorageLock(() => this.storage.clearAuthSession(baseUrl2));
|
|
3806
3640
|
}
|
|
3807
3641
|
getStorageLocation() {
|
|
3808
3642
|
return this.storage.getStorageLocation();
|
|
3809
3643
|
}
|
|
3810
|
-
async
|
|
3644
|
+
async withAuthStorageLock(fn) {
|
|
3645
|
+
const ownerId = this.lockContext.getStore();
|
|
3646
|
+
if (ownerId && this.currentOwner?.id === ownerId) {
|
|
3647
|
+
return fn();
|
|
3648
|
+
}
|
|
3811
3649
|
await this.acquireLock();
|
|
3650
|
+
const acquiredOwnerId = this.currentOwner?.id;
|
|
3812
3651
|
try {
|
|
3813
|
-
return await fn
|
|
3652
|
+
return await this.lockContext.run(acquiredOwnerId ?? "", fn);
|
|
3814
3653
|
} finally {
|
|
3815
3654
|
await this.releaseLock();
|
|
3816
3655
|
}
|
|
3817
3656
|
}
|
|
3818
3657
|
async acquireLock() {
|
|
3658
|
+
const processStartedAt = await getProcessStartedAt(process.pid);
|
|
3819
3659
|
const startedAt = Date.now();
|
|
3820
3660
|
await mkdir2(dirname2(this.lockPath), { recursive: true, mode: 448 });
|
|
3821
3661
|
while (true) {
|
|
3822
3662
|
try {
|
|
3823
3663
|
await mkdir2(this.lockPath, { recursive: false, mode: 448 });
|
|
3824
3664
|
try {
|
|
3825
|
-
await this.writeOwner();
|
|
3665
|
+
await this.writeOwner(processStartedAt);
|
|
3826
3666
|
} catch (error) {
|
|
3667
|
+
this.currentOwner = null;
|
|
3668
|
+
if (error.code === "EEXIST") {
|
|
3669
|
+
await sleep(LOCK_RETRY_MS);
|
|
3670
|
+
continue;
|
|
3671
|
+
}
|
|
3827
3672
|
await rm(this.lockPath, { recursive: true, force: true }).catch(() => {
|
|
3828
3673
|
return;
|
|
3829
3674
|
});
|
|
@@ -3841,15 +3686,18 @@ class LockedAuthStorage {
|
|
|
3841
3686
|
}
|
|
3842
3687
|
}
|
|
3843
3688
|
}
|
|
3844
|
-
async writeOwner() {
|
|
3689
|
+
async writeOwner(processStartedAt) {
|
|
3845
3690
|
const owner = {
|
|
3846
|
-
id:
|
|
3691
|
+
id: randomUUID2(),
|
|
3847
3692
|
pid: process.pid,
|
|
3848
3693
|
createdAt: new Date().toISOString(),
|
|
3849
|
-
processStartedAt
|
|
3694
|
+
processStartedAt
|
|
3850
3695
|
};
|
|
3696
|
+
await writeFile2(this.ownerPath(), JSON.stringify(owner), {
|
|
3697
|
+
mode: 384,
|
|
3698
|
+
flag: "wx"
|
|
3699
|
+
});
|
|
3851
3700
|
this.currentOwner = owner;
|
|
3852
|
-
await writeFile2(this.ownerPath(), JSON.stringify(owner), { mode: 384 });
|
|
3853
3701
|
}
|
|
3854
3702
|
async reclaimStaleLock() {
|
|
3855
3703
|
const owner = await this.readOwner();
|
|
@@ -3959,6 +3807,7 @@ async function lockCreatedAtMs(path) {
|
|
|
3959
3807
|
return 0;
|
|
3960
3808
|
}
|
|
3961
3809
|
}
|
|
3810
|
+
|
|
3962
3811
|
// src/services/mode-aware-file-auth-storage.ts
|
|
3963
3812
|
class AuthStoragePolicyError extends Error {
|
|
3964
3813
|
constructor(message) {
|
|
@@ -4390,6 +4239,7 @@ class MigratingAuthStorage {
|
|
|
4390
4239
|
return a.accessToken === b.accessToken && a.refreshToken === b.refreshToken && a.expiresAt === b.expiresAt && a.createdAt === b.createdAt;
|
|
4391
4240
|
}
|
|
4392
4241
|
}
|
|
4242
|
+
|
|
4393
4243
|
// src/services/package-intelligence-service.ts
|
|
4394
4244
|
import { z as z3 } from "zod";
|
|
4395
4245
|
|
|
@@ -5389,10 +5239,12 @@ class PackageIntelligenceServiceImpl {
|
|
|
5389
5239
|
endpointUrl;
|
|
5390
5240
|
tokenProvider;
|
|
5391
5241
|
fetchFn;
|
|
5392
|
-
|
|
5242
|
+
runtime;
|
|
5243
|
+
constructor(endpointUrl, tokenProvider, fetchFn = globalThis.fetch, runtime = {}) {
|
|
5393
5244
|
this.endpointUrl = endpointUrl;
|
|
5394
5245
|
this.tokenProvider = tokenProvider;
|
|
5395
5246
|
this.fetchFn = fetchFn;
|
|
5247
|
+
this.runtime = runtime;
|
|
5396
5248
|
}
|
|
5397
5249
|
async packageSummary(params) {
|
|
5398
5250
|
return withTelemetrySpan("pkg-intel.summary.request", () => executeWithTokenRefresh({
|
|
@@ -5413,7 +5265,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
5413
5265
|
registry: params.registry,
|
|
5414
5266
|
name: params.packageName
|
|
5415
5267
|
},
|
|
5416
|
-
fetchFn: this.fetchFn
|
|
5268
|
+
fetchFn: this.fetchFn,
|
|
5269
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5270
|
+
userAgent: this.runtime.userAgent
|
|
5417
5271
|
});
|
|
5418
5272
|
} catch (cause) {
|
|
5419
5273
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -5441,7 +5295,7 @@ class PackageIntelligenceServiceImpl {
|
|
|
5441
5295
|
const status = response.status;
|
|
5442
5296
|
const detail = parseDetail3(response.responseBody);
|
|
5443
5297
|
if (status === 401) {
|
|
5444
|
-
return new AuthenticationError(
|
|
5298
|
+
return new AuthenticationError(SERVER_AUTHENTICATION_REJECTED_MESSAGE, "server");
|
|
5445
5299
|
}
|
|
5446
5300
|
if (status === 403) {
|
|
5447
5301
|
return new PackageIntelligenceAccessError(detail ?? "Access denied.");
|
|
@@ -5463,7 +5317,7 @@ class PackageIntelligenceServiceImpl {
|
|
|
5463
5317
|
const code = typeof extensions?.code === "string" ? extensions.code : undefined;
|
|
5464
5318
|
const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
|
|
5465
5319
|
if (isClientUpdateRequiredGraphQLError({ message, code })) {
|
|
5466
|
-
return new ClientUpdateRequiredError;
|
|
5320
|
+
return new ClientUpdateRequiredError(undefined, undefined, this.runtime.clientVersion);
|
|
5467
5321
|
}
|
|
5468
5322
|
if (isGraphQLSchemaMismatchError({ message, code })) {
|
|
5469
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.";
|
|
@@ -5486,7 +5340,7 @@ class PackageIntelligenceServiceImpl {
|
|
|
5486
5340
|
case "FEATURE_FLAG_REQUIRED":
|
|
5487
5341
|
return new PackageIntelligenceFeatureFlagRequiredError(message);
|
|
5488
5342
|
case "UNAUTHORIZED":
|
|
5489
|
-
return new AuthenticationError(
|
|
5343
|
+
return new AuthenticationError(SERVER_AUTHENTICATION_REJECTED_MESSAGE, "server");
|
|
5490
5344
|
case "FORBIDDEN":
|
|
5491
5345
|
return new PackageIntelligenceAccessError("Access denied. This feature may not be enabled for your account.");
|
|
5492
5346
|
case "UPSTREAM_ERROR":
|
|
@@ -5624,7 +5478,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
5624
5478
|
scope: params.advisoryScope,
|
|
5625
5479
|
after
|
|
5626
5480
|
},
|
|
5627
|
-
fetchFn: this.fetchFn
|
|
5481
|
+
fetchFn: this.fetchFn,
|
|
5482
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5483
|
+
userAgent: this.runtime.userAgent
|
|
5628
5484
|
});
|
|
5629
5485
|
} catch (cause) {
|
|
5630
5486
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -5728,7 +5584,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
5728
5584
|
lifecycle: params.includeGroups === true ? ["peer"] : undefined,
|
|
5729
5585
|
minSeverity: params.minSeverity
|
|
5730
5586
|
},
|
|
5731
|
-
fetchFn: this.fetchFn
|
|
5587
|
+
fetchFn: this.fetchFn,
|
|
5588
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5589
|
+
userAgent: this.runtime.userAgent
|
|
5732
5590
|
});
|
|
5733
5591
|
} catch (cause) {
|
|
5734
5592
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -5767,7 +5625,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
5767
5625
|
maxDepth: params.maxDepth,
|
|
5768
5626
|
lifecycle: params.lifecycle && params.lifecycle.length > 0 ? params.lifecycle : undefined
|
|
5769
5627
|
},
|
|
5770
|
-
fetchFn: this.fetchFn
|
|
5628
|
+
fetchFn: this.fetchFn,
|
|
5629
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5630
|
+
userAgent: this.runtime.userAgent
|
|
5771
5631
|
});
|
|
5772
5632
|
} catch (cause) {
|
|
5773
5633
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -6004,7 +5864,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
6004
5864
|
toVersion: params.toVersion,
|
|
6005
5865
|
limit: params.limit
|
|
6006
5866
|
},
|
|
6007
|
-
fetchFn: this.fetchFn
|
|
5867
|
+
fetchFn: this.fetchFn,
|
|
5868
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5869
|
+
userAgent: this.runtime.userAgent
|
|
6008
5870
|
});
|
|
6009
5871
|
} catch (cause) {
|
|
6010
5872
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -6079,7 +5941,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
6079
5941
|
limit: params.limit,
|
|
6080
5942
|
after: params.after
|
|
6081
5943
|
},
|
|
6082
|
-
fetchFn: this.fetchFn
|
|
5944
|
+
fetchFn: this.fetchFn,
|
|
5945
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
5946
|
+
userAgent: this.runtime.userAgent
|
|
6083
5947
|
});
|
|
6084
5948
|
} catch (cause) {
|
|
6085
5949
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -6148,7 +6012,9 @@ class PackageIntelligenceServiceImpl {
|
|
|
6148
6012
|
variables: {
|
|
6149
6013
|
pageId: params.pageId
|
|
6150
6014
|
},
|
|
6151
|
-
fetchFn: this.fetchFn
|
|
6015
|
+
fetchFn: this.fetchFn,
|
|
6016
|
+
clientHeaders: this.runtime.clientHeaders,
|
|
6017
|
+
userAgent: this.runtime.userAgent
|
|
6152
6018
|
});
|
|
6153
6019
|
} catch (cause) {
|
|
6154
6020
|
if (cause instanceof PkgseerTransportError) {
|
|
@@ -6233,40 +6099,18 @@ function parseVersionList(raw) {
|
|
|
6233
6099
|
}
|
|
6234
6100
|
return versions.length > 0 ? versions : undefined;
|
|
6235
6101
|
}
|
|
6236
|
-
// src/services/prompt-service.ts
|
|
6237
|
-
import { checkbox, select } from "@inquirer/prompts";
|
|
6238
6102
|
|
|
6239
|
-
class PromptServiceImpl {
|
|
6240
|
-
async select(message, choices, defaultValue) {
|
|
6241
|
-
return select({ message, choices, default: defaultValue });
|
|
6242
|
-
}
|
|
6243
|
-
async checkbox(message, choices) {
|
|
6244
|
-
return checkbox({ message, choices });
|
|
6245
|
-
}
|
|
6246
|
-
async confirm3(message) {
|
|
6247
|
-
return select({
|
|
6248
|
-
message,
|
|
6249
|
-
choices: [
|
|
6250
|
-
{ value: "yes", name: "Yes" },
|
|
6251
|
-
{ value: "no", name: "No" },
|
|
6252
|
-
{
|
|
6253
|
-
value: "always",
|
|
6254
|
-
name: "Yes to all",
|
|
6255
|
-
description: "Skip confirmation for remaining agents"
|
|
6256
|
-
}
|
|
6257
|
-
]
|
|
6258
|
-
});
|
|
6259
|
-
}
|
|
6260
|
-
}
|
|
6261
6103
|
// src/services/refreshing-githits-service.ts
|
|
6262
6104
|
class RefreshingGitHitsService {
|
|
6263
6105
|
apiUrl;
|
|
6264
6106
|
tokenProvider;
|
|
6265
6107
|
serviceFactory;
|
|
6266
|
-
|
|
6108
|
+
runtime;
|
|
6109
|
+
constructor(apiUrl, tokenProvider, serviceFactory, runtime = {}) {
|
|
6267
6110
|
this.apiUrl = apiUrl;
|
|
6268
6111
|
this.tokenProvider = tokenProvider;
|
|
6269
6112
|
this.serviceFactory = serviceFactory;
|
|
6113
|
+
this.runtime = runtime;
|
|
6270
6114
|
}
|
|
6271
6115
|
async search(params) {
|
|
6272
6116
|
return this.withTokenRefresh((service) => service.search(params));
|
|
@@ -6283,12 +6127,13 @@ class RefreshingGitHitsService {
|
|
|
6283
6127
|
forceRefresh: () => this.tokenProvider.forceRefresh(),
|
|
6284
6128
|
shouldRefresh: (error) => error instanceof AuthenticationError,
|
|
6285
6129
|
executeWithToken: async (token) => {
|
|
6286
|
-
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);
|
|
6287
6131
|
return operation(service);
|
|
6288
6132
|
}
|
|
6289
6133
|
});
|
|
6290
6134
|
}
|
|
6291
6135
|
}
|
|
6136
|
+
|
|
6292
6137
|
// src/services/token-manager.ts
|
|
6293
6138
|
var PROACTIVE_REFRESH_RATIO = 0.9;
|
|
6294
6139
|
function shouldRefreshToken(token, ratio, now) {
|
|
@@ -6327,11 +6172,19 @@ class TokenManager {
|
|
|
6327
6172
|
}
|
|
6328
6173
|
async getToken() {
|
|
6329
6174
|
return withTelemetrySpan("token-manager.get-token", async () => {
|
|
6330
|
-
|
|
6331
|
-
|
|
6175
|
+
const activeForceRefresh = this.forceRefreshPromise;
|
|
6176
|
+
if (activeForceRefresh) {
|
|
6177
|
+
return (await activeForceRefresh).accessToken;
|
|
6332
6178
|
}
|
|
6333
6179
|
if (!this.cachedToken) {
|
|
6334
|
-
|
|
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
|
+
}
|
|
6335
6188
|
if (!this.cachedToken)
|
|
6336
6189
|
return;
|
|
6337
6190
|
}
|
|
@@ -6340,9 +6193,7 @@ class TokenManager {
|
|
|
6340
6193
|
if (!shouldRefresh) {
|
|
6341
6194
|
return currentToken;
|
|
6342
6195
|
}
|
|
6343
|
-
const refreshedToken = await this.
|
|
6344
|
-
allowFreshExternalToken: true
|
|
6345
|
-
});
|
|
6196
|
+
const refreshedToken = await this.refreshFromGetToken();
|
|
6346
6197
|
if (refreshedToken) {
|
|
6347
6198
|
return refreshedToken;
|
|
6348
6199
|
}
|
|
@@ -6353,48 +6204,52 @@ class TokenManager {
|
|
|
6353
6204
|
});
|
|
6354
6205
|
}
|
|
6355
6206
|
async forceRefresh() {
|
|
6356
|
-
return withTelemetrySpan("token-manager.force-refresh", () => this.
|
|
6207
|
+
return withTelemetrySpan("token-manager.force-refresh", () => this.refreshAfterAuthFailure());
|
|
6357
6208
|
}
|
|
6358
|
-
async
|
|
6359
|
-
const result = await this.
|
|
6209
|
+
async refreshFromGetToken() {
|
|
6210
|
+
const result = await this.softRefresh();
|
|
6360
6211
|
return result.accessToken;
|
|
6361
6212
|
}
|
|
6362
|
-
async
|
|
6363
|
-
if (!options.allowFreshExternalToken) {
|
|
6364
|
-
if (this.forceRefreshPromise)
|
|
6365
|
-
return this.forceRefreshPromise;
|
|
6366
|
-
this.forceRefreshPromise = (async () => {
|
|
6367
|
-
const softResult = await this.softRefreshPromise?.catch(() => {
|
|
6368
|
-
return;
|
|
6369
|
-
});
|
|
6370
|
-
if (softResult?.accessToken && softResult.refreshedViaEndpoint) {
|
|
6371
|
-
return softResult;
|
|
6372
|
-
}
|
|
6373
|
-
return this.executeRefresh(options);
|
|
6374
|
-
})();
|
|
6375
|
-
try {
|
|
6376
|
-
return await this.forceRefreshPromise;
|
|
6377
|
-
} finally {
|
|
6378
|
-
this.forceRefreshPromise = null;
|
|
6379
|
-
}
|
|
6380
|
-
}
|
|
6213
|
+
async softRefresh() {
|
|
6381
6214
|
if (this.forceRefreshPromise)
|
|
6382
6215
|
return this.forceRefreshPromise;
|
|
6383
6216
|
if (this.softRefreshPromise)
|
|
6384
6217
|
return this.softRefreshPromise;
|
|
6385
|
-
this.softRefreshPromise = this.executeRefresh(
|
|
6218
|
+
this.softRefreshPromise = this.executeRefresh();
|
|
6386
6219
|
try {
|
|
6387
6220
|
return await this.softRefreshPromise;
|
|
6388
6221
|
} finally {
|
|
6389
6222
|
this.softRefreshPromise = null;
|
|
6390
6223
|
}
|
|
6391
6224
|
}
|
|
6392
|
-
async
|
|
6393
|
-
|
|
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 () => {
|
|
6394
6249
|
const candidate = await this.loadRefreshCandidate();
|
|
6395
6250
|
if (!candidate)
|
|
6396
6251
|
return refreshResult(undefined, false);
|
|
6397
|
-
if (candidate.externallyUpdated
|
|
6252
|
+
if (candidate.externallyUpdated) {
|
|
6398
6253
|
const { shouldRefresh } = shouldRefreshToken(candidate.tokens, PROACTIVE_REFRESH_RATIO, new Date);
|
|
6399
6254
|
if (!shouldRefresh)
|
|
6400
6255
|
return refreshResult(candidate.tokens.accessToken, false);
|
|
@@ -6447,7 +6302,7 @@ class TokenManager {
|
|
|
6447
6302
|
}
|
|
6448
6303
|
this.cachedToken = newTokenData;
|
|
6449
6304
|
return refreshResult(response.accessToken, true);
|
|
6450
|
-
});
|
|
6305
|
+
}));
|
|
6451
6306
|
}
|
|
6452
6307
|
async resolveSuccessfulRefreshConflict(refreshedFrom, response, newTokenData) {
|
|
6453
6308
|
const currentToken = await withTelemetrySpan("token-manager.reload-tokens", () => this.authStorage.loadTokens(this.mcpUrl));
|
|
@@ -6500,330 +6355,139 @@ function areSameTokenData(a, b) {
|
|
|
6500
6355
|
function refreshResult(accessToken, refreshedViaEndpoint) {
|
|
6501
6356
|
return { accessToken, refreshedViaEndpoint };
|
|
6502
6357
|
}
|
|
6503
|
-
// src/services/update-check-service.ts
|
|
6504
|
-
import semver from "semver";
|
|
6505
|
-
var NPM_DIST_TAGS_URL = "https://registry.npmjs.org/-/package/githits/dist-tags";
|
|
6506
|
-
var NPM_PACKAGE_VERSION_URL = "https://registry.npmjs.org/githits";
|
|
6507
|
-
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
6508
|
-
var FETCH_TIMEOUT_MS = 1000;
|
|
6509
|
-
var DIR_MODE3 = 448;
|
|
6510
|
-
var UPDATE_COMMAND = "npm i -g githits@latest";
|
|
6511
|
-
var MAX_DEPRECATION_REASON_LENGTH = 200;
|
|
6512
6358
|
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
const cache = await this.loadCache();
|
|
6536
|
-
const latestDue = !cache || this.isCheckDue(cache);
|
|
6537
|
-
const currentVersionStatusDue = this.isCurrentVersionStatusDue(cache?.currentVersionStatus);
|
|
6538
|
-
const currentVersionStatus = await this.refreshCurrentVersionStatusIfDue(cache?.currentVersionStatus, signal);
|
|
6539
|
-
const currentVersionStatusChanged = currentVersionStatusDue && !sameCurrentVersionStatus(currentVersionStatus, cache?.currentVersionStatus);
|
|
6540
|
-
if (cache && !latestDue) {
|
|
6541
|
-
if (currentVersionStatusChanged) {
|
|
6542
|
-
await this.saveCache({
|
|
6543
|
-
...cache,
|
|
6544
|
-
currentVersionStatus
|
|
6545
|
-
});
|
|
6546
|
-
}
|
|
6547
|
-
return this.noticeFromLatest(cache.latestVersion);
|
|
6548
|
-
}
|
|
6549
|
-
const latestVersion = await this.fetchLatestVersion(signal);
|
|
6550
|
-
if (!latestVersion) {
|
|
6551
|
-
if (currentVersionStatusChanged) {
|
|
6552
|
-
await this.saveCache({
|
|
6553
|
-
...cache,
|
|
6554
|
-
currentVersionStatus
|
|
6555
|
-
});
|
|
6556
|
-
}
|
|
6557
|
-
return this.noticeFromLatest(cache?.latestVersion);
|
|
6558
|
-
}
|
|
6559
|
-
await this.saveCache({
|
|
6560
|
-
checkedAt: this.now().toISOString(),
|
|
6561
|
-
latestVersion,
|
|
6562
|
-
currentVersionStatus
|
|
6563
|
-
});
|
|
6564
|
-
return this.noticeFromLatest(latestVersion);
|
|
6565
|
-
}
|
|
6566
|
-
async refreshRequiredUpdateStatus(signal) {
|
|
6567
|
-
const cache = await this.loadCache();
|
|
6568
|
-
const currentVersionStatusDue = this.isCurrentVersionStatusDue(cache?.currentVersionStatus);
|
|
6569
|
-
const currentVersionStatus = await this.refreshCurrentVersionStatusIfDue(cache?.currentVersionStatus, signal);
|
|
6570
|
-
if (currentVersionStatusDue && !sameCurrentVersionStatus(currentVersionStatus, cache?.currentVersionStatus)) {
|
|
6571
|
-
await this.saveCache({
|
|
6572
|
-
...cache,
|
|
6573
|
-
currentVersionStatus
|
|
6574
|
-
});
|
|
6575
|
-
}
|
|
6576
|
-
}
|
|
6577
|
-
async getRequiredUpdateNotice() {
|
|
6578
|
-
const cache = await this.loadCache();
|
|
6579
|
-
const status = cache?.currentVersionStatus;
|
|
6580
|
-
if (!status || status.version !== this.currentVersion || !status.deprecatedReason) {
|
|
6581
|
-
return;
|
|
6582
|
-
}
|
|
6583
|
-
return {
|
|
6584
|
-
currentVersion: this.currentVersion,
|
|
6585
|
-
...cache.latestVersion ? { latestKnownVersion: cache.latestVersion } : {},
|
|
6586
|
-
reason: status.deprecatedReason,
|
|
6587
|
-
updateCommand: formatUpdateCommand(this.env)
|
|
6588
|
-
};
|
|
6589
|
-
}
|
|
6590
|
-
noticeFromLatest(latestVersion) {
|
|
6591
|
-
if (!latestVersion || !semver.valid(latestVersion) || !semver.valid(this.currentVersion) || !semver.gt(latestVersion, this.currentVersion)) {
|
|
6592
|
-
return;
|
|
6593
|
-
}
|
|
6594
|
-
return {
|
|
6595
|
-
currentVersion: this.currentVersion,
|
|
6596
|
-
latestVersion,
|
|
6597
|
-
updateCommand: UPDATE_COMMAND
|
|
6598
|
-
};
|
|
6599
|
-
}
|
|
6600
|
-
isCheckDue(cache) {
|
|
6601
|
-
if (!cache.checkedAt || !cache.latestVersion) {
|
|
6602
|
-
return true;
|
|
6603
|
-
}
|
|
6604
|
-
const checkedAtMs = Date.parse(cache.checkedAt);
|
|
6605
|
-
if (Number.isNaN(checkedAtMs)) {
|
|
6606
|
-
return true;
|
|
6607
|
-
}
|
|
6608
|
-
return this.now().getTime() - checkedAtMs >= this.checkIntervalMs;
|
|
6609
|
-
}
|
|
6610
|
-
isCurrentVersionStatusDue(status) {
|
|
6611
|
-
if (!status || status.version !== this.currentVersion) {
|
|
6612
|
-
return true;
|
|
6613
|
-
}
|
|
6614
|
-
const checkedAtMs = Date.parse(status.checkedAt);
|
|
6615
|
-
if (Number.isNaN(checkedAtMs)) {
|
|
6616
|
-
return true;
|
|
6617
|
-
}
|
|
6618
|
-
return this.now().getTime() - checkedAtMs >= this.checkIntervalMs;
|
|
6619
|
-
}
|
|
6620
|
-
async refreshCurrentVersionStatusIfDue(cached, signal) {
|
|
6621
|
-
const reusableCached = cached?.version === this.currentVersion ? cached : undefined;
|
|
6622
|
-
if (!this.isCurrentVersionStatusDue(reusableCached)) {
|
|
6623
|
-
return reusableCached;
|
|
6624
|
-
}
|
|
6625
|
-
const remote = await this.fetchCurrentVersionStatus(signal);
|
|
6626
|
-
if (!remote) {
|
|
6627
|
-
return reusableCached;
|
|
6628
|
-
}
|
|
6629
|
-
return remote;
|
|
6630
|
-
}
|
|
6631
|
-
async fetchLatestVersion(signal) {
|
|
6632
|
-
try {
|
|
6633
|
-
const timeoutSignal = AbortSignal.timeout(this.fetchTimeoutMs);
|
|
6634
|
-
const response = await this.fetcher(NPM_DIST_TAGS_URL, {
|
|
6635
|
-
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
|
|
6636
|
-
});
|
|
6637
|
-
if (!response.ok) {
|
|
6638
|
-
return;
|
|
6639
|
-
}
|
|
6640
|
-
const body = await response.json();
|
|
6641
|
-
if (!body || typeof body !== "object" || typeof body.latest !== "string") {
|
|
6642
|
-
return;
|
|
6643
|
-
}
|
|
6644
|
-
const latest = body.latest;
|
|
6645
|
-
return semver.valid(latest) ? latest : undefined;
|
|
6646
|
-
} catch {
|
|
6647
|
-
return;
|
|
6648
|
-
}
|
|
6649
|
-
}
|
|
6650
|
-
async fetchCurrentVersionStatus(signal) {
|
|
6651
|
-
try {
|
|
6652
|
-
const timeoutSignal = AbortSignal.timeout(this.fetchTimeoutMs);
|
|
6653
|
-
const response = await this.fetcher(`${NPM_PACKAGE_VERSION_URL}/${this.currentVersion}`, {
|
|
6654
|
-
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
|
|
6655
|
-
});
|
|
6656
|
-
if (!response.ok) {
|
|
6657
|
-
return;
|
|
6658
|
-
}
|
|
6659
|
-
const body = await response.json();
|
|
6660
|
-
if (!body || typeof body !== "object") {
|
|
6661
|
-
return;
|
|
6662
|
-
}
|
|
6663
|
-
const rawDeprecated = body.deprecated;
|
|
6664
|
-
const deprecatedReason = typeof rawDeprecated === "string" ? sanitizeDeprecationReason(rawDeprecated) : undefined;
|
|
6665
|
-
return {
|
|
6666
|
-
version: this.currentVersion,
|
|
6667
|
-
checkedAt: this.now().toISOString(),
|
|
6668
|
-
...deprecatedReason ? { deprecatedReason } : {}
|
|
6669
|
-
};
|
|
6670
|
-
} catch {
|
|
6671
|
-
return;
|
|
6672
|
-
}
|
|
6673
|
-
}
|
|
6674
|
-
async loadCache() {
|
|
6675
|
-
try {
|
|
6676
|
-
if (!await this.fs.exists(this.cachePath)) {
|
|
6677
|
-
return;
|
|
6678
|
-
}
|
|
6679
|
-
const raw = await this.fs.readFile(this.cachePath);
|
|
6680
|
-
const parsed = JSON.parse(raw);
|
|
6681
|
-
const currentVersionStatus = parseCurrentVersionStatus(parsed.currentVersionStatus);
|
|
6682
|
-
const hasLatest = typeof parsed.checkedAt === "string" && typeof parsed.latestVersion === "string";
|
|
6683
|
-
if (!hasLatest && !currentVersionStatus) {
|
|
6684
|
-
return;
|
|
6685
|
-
}
|
|
6686
|
-
return {
|
|
6687
|
-
...hasLatest ? {
|
|
6688
|
-
checkedAt: parsed.checkedAt,
|
|
6689
|
-
latestVersion: parsed.latestVersion
|
|
6690
|
-
} : {},
|
|
6691
|
-
currentVersionStatus
|
|
6692
|
-
};
|
|
6693
|
-
} catch {
|
|
6694
|
-
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();
|
|
6695
6381
|
}
|
|
6696
6382
|
}
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
await this.fs.ensureDir(this.configDir, DIR_MODE3);
|
|
6700
|
-
if (typeof this.fs.atomicWriteFile === "function") {
|
|
6701
|
-
await this.fs.atomicWriteFile(this.cachePath, `${JSON.stringify(cache, null, 2)}
|
|
6702
|
-
`);
|
|
6703
|
-
return;
|
|
6704
|
-
}
|
|
6705
|
-
await this.fs.writeFile(this.cachePath, `${JSON.stringify(cache, null, 2)}
|
|
6706
|
-
`, 384);
|
|
6707
|
-
} catch {}
|
|
6708
|
-
}
|
|
6709
|
-
}
|
|
6710
|
-
function resolveConfigHome(env, fs) {
|
|
6711
|
-
const xdgConfigHome = env.XDG_CONFIG_HOME?.trim();
|
|
6712
|
-
if (xdgConfigHome) {
|
|
6713
|
-
return xdgConfigHome;
|
|
6383
|
+
if (typeof ppid === "number" && !Number.isNaN(ppid) && ppid > 0) {
|
|
6384
|
+
return String(ppid);
|
|
6714
6385
|
}
|
|
6715
|
-
return
|
|
6386
|
+
return randomUUID3();
|
|
6716
6387
|
}
|
|
6717
|
-
function
|
|
6718
|
-
if (
|
|
6719
|
-
return
|
|
6720
|
-
}
|
|
6721
|
-
const env = input.env ?? process.env;
|
|
6722
|
-
if (env.CI || env.GITHITS_DISABLE_UPDATE_CHECK) {
|
|
6723
|
-
return false;
|
|
6724
|
-
}
|
|
6725
|
-
if (isHelpOrVersionInvocation(input.args)) {
|
|
6726
|
-
return false;
|
|
6727
|
-
}
|
|
6728
|
-
if (isLikelyEphemeralPackageRunner(env)) {
|
|
6729
|
-
return false;
|
|
6388
|
+
function getSessionId(env, ppid) {
|
|
6389
|
+
if (cachedSessionId !== undefined && env === undefined && ppid === undefined) {
|
|
6390
|
+
return cachedSessionId;
|
|
6730
6391
|
}
|
|
6731
|
-
|
|
6732
|
-
|
|
6392
|
+
const raw = resolveRawSessionId(env, ppid);
|
|
6393
|
+
const hashed = hashValue(raw);
|
|
6394
|
+
if (env === undefined && ppid === undefined) {
|
|
6395
|
+
cachedSessionId = hashed;
|
|
6733
6396
|
}
|
|
6734
|
-
return
|
|
6397
|
+
return hashed;
|
|
6735
6398
|
}
|
|
6736
|
-
function
|
|
6737
|
-
|
|
6738
|
-
return false;
|
|
6739
|
-
}
|
|
6740
|
-
const env = input.env ?? process.env;
|
|
6741
|
-
if (isLikelyEphemeralPackageRunner(env)) {
|
|
6742
|
-
return false;
|
|
6743
|
-
}
|
|
6744
|
-
return true;
|
|
6399
|
+
function hashValue(input) {
|
|
6400
|
+
return createHash2("sha256").update(input).digest("hex").slice(0, 16);
|
|
6745
6401
|
}
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
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 };
|
|
6749
6422
|
}
|
|
6750
|
-
function
|
|
6751
|
-
|
|
6752
|
-
`Update required: ${notice.reason}`,
|
|
6753
|
-
"",
|
|
6754
|
-
`Installed githits ${notice.currentVersion} is no longer supported.`
|
|
6755
|
-
];
|
|
6756
|
-
if (notice.latestKnownVersion) {
|
|
6757
|
-
lines.push(`Latest known version: ${notice.latestKnownVersion}`);
|
|
6758
|
-
}
|
|
6759
|
-
lines.push("Update with:", ` ${notice.updateCommand}`);
|
|
6760
|
-
return [...lines].join(`
|
|
6761
|
-
`);
|
|
6423
|
+
function formatAgentInfo(info) {
|
|
6424
|
+
return info.version ? `${info.name}/${info.version}` : info.name;
|
|
6762
6425
|
}
|
|
6763
|
-
function
|
|
6764
|
-
const
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
if (signal.includes("pnpm")) {
|
|
6768
|
-
return "pnpm add -g githits@latest";
|
|
6769
|
-
}
|
|
6770
|
-
if (signal.includes("yarn")) {
|
|
6771
|
-
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);
|
|
6772
6430
|
}
|
|
6773
|
-
|
|
6774
|
-
|
|
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
|
+
}
|
|
6775
6436
|
}
|
|
6776
|
-
return
|
|
6437
|
+
return;
|
|
6777
6438
|
}
|
|
6778
|
-
|
|
6779
|
-
|
|
6439
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
6440
|
+
function sanitizeHeaderValue(value) {
|
|
6441
|
+
if (value === undefined || value === null || typeof value !== "string") {
|
|
6780
6442
|
return;
|
|
6781
6443
|
}
|
|
6782
|
-
const
|
|
6783
|
-
if (
|
|
6444
|
+
const cleaned = value.replace(CONTROL_CHARS, "").trim();
|
|
6445
|
+
if (cleaned.length === 0)
|
|
6784
6446
|
return;
|
|
6785
|
-
|
|
6786
|
-
if (typeof status.checkedAt !== "string") {
|
|
6447
|
+
if (Buffer.byteLength(cleaned, "utf8") > MAX_HEADER_BYTES)
|
|
6787
6448
|
return;
|
|
6788
|
-
|
|
6789
|
-
const deprecatedReason = typeof status.deprecatedReason === "string" ? sanitizeDeprecationReason(status.deprecatedReason) : undefined;
|
|
6790
|
-
return {
|
|
6791
|
-
version: status.version,
|
|
6792
|
-
checkedAt: status.checkedAt,
|
|
6793
|
-
...deprecatedReason ? { deprecatedReason } : {}
|
|
6794
|
-
};
|
|
6795
|
-
}
|
|
6796
|
-
function sameCurrentVersionStatus(left, right) {
|
|
6797
|
-
return left?.version === right?.version && left?.checkedAt === right?.checkedAt && left?.deprecatedReason === right?.deprecatedReason;
|
|
6798
|
-
}
|
|
6799
|
-
function sanitizeDeprecationReason(value) {
|
|
6800
|
-
const sanitized = Array.from(value).map((character) => isControlCharacter(character) ? " " : character).join("").replace(/\s+/g, " ").trim().slice(0, MAX_DEPRECATION_REASON_LENGTH).trim();
|
|
6801
|
-
return sanitized.length > 0 ? sanitized : undefined;
|
|
6802
|
-
}
|
|
6803
|
-
function isControlCharacter(value) {
|
|
6804
|
-
const code = value.charCodeAt(0);
|
|
6805
|
-
return code <= 31 || code >= 127 && code <= 159;
|
|
6806
|
-
}
|
|
6807
|
-
function isHelpOrVersionInvocation(args) {
|
|
6808
|
-
return args.length === 0 || args[0] === "help" || args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-V");
|
|
6449
|
+
return cleaned;
|
|
6809
6450
|
}
|
|
6810
|
-
function
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
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
|
+
});
|
|
6817
6459
|
}
|
|
6818
|
-
function
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
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 {};
|
|
6822
6485
|
}
|
|
6823
|
-
const remainingArgs = args.slice(firstTokenIndex + 1);
|
|
6824
|
-
return remainingArgs.includes("start") || !stdinIsTTY || !stdoutIsTTY;
|
|
6825
6486
|
}
|
|
6487
|
+
|
|
6826
6488
|
// src/container.ts
|
|
6489
|
+
var BASE_CLIENT_NAME = "githits-cli";
|
|
6490
|
+
var USER_AGENT = `${BASE_CLIENT_NAME}/${version}`;
|
|
6827
6491
|
async function createAuthStorage(fileSystemService) {
|
|
6828
6492
|
return withTelemetrySpan("container.create-auth-storage", async () => {
|
|
6829
6493
|
const authConfig = await loadAuthConfig(fileSystemService);
|
|
@@ -6903,12 +6567,22 @@ async function createContainer(options = {}) {
|
|
|
6903
6567
|
const fileSystemService = new FileSystemServiceImpl;
|
|
6904
6568
|
const authService = new AuthServiceImpl;
|
|
6905
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
|
+
};
|
|
6906
6580
|
const envToken = getEnvApiToken();
|
|
6907
6581
|
if (envToken) {
|
|
6908
6582
|
const authStorage2 = createAuthStorageForMode(fileSystemService, "keychain");
|
|
6909
6583
|
const tokenProvider = createStaticTokenProvider(envToken);
|
|
6910
|
-
const codeNavigationService2 = new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider);
|
|
6911
|
-
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);
|
|
6912
6586
|
return {
|
|
6913
6587
|
authStorage: authStorage2,
|
|
6914
6588
|
authService,
|
|
@@ -6922,7 +6596,7 @@ async function createContainer(options = {}) {
|
|
|
6922
6596
|
codeNavigationUrl,
|
|
6923
6597
|
codeNavigationService: codeNavigationService2,
|
|
6924
6598
|
packageIntelligenceService: packageIntelligenceService2,
|
|
6925
|
-
githitsService: new GitHitsServiceImpl(apiUrl, envToken)
|
|
6599
|
+
githitsService: new GitHitsServiceImpl(apiUrl, envToken, undefined, undefined, serviceRuntime)
|
|
6926
6600
|
};
|
|
6927
6601
|
}
|
|
6928
6602
|
const authStorage = await createAuthStorage(fileSystemService);
|
|
@@ -6931,8 +6605,8 @@ async function createContainer(options = {}) {
|
|
|
6931
6605
|
if (resolveStoredToken && apiToken === undefined) {
|
|
6932
6606
|
await new AuthSessionMetadataStorage(fileSystemService).clear(mcpUrl);
|
|
6933
6607
|
}
|
|
6934
|
-
const codeNavigationService = new CodeNavigationServiceImpl(codeNavigationUrl, tokenManager);
|
|
6935
|
-
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);
|
|
6936
6610
|
return {
|
|
6937
6611
|
authStorage,
|
|
6938
6612
|
authService,
|
|
@@ -6946,9 +6620,9 @@ async function createContainer(options = {}) {
|
|
|
6946
6620
|
codeNavigationUrl,
|
|
6947
6621
|
codeNavigationService,
|
|
6948
6622
|
packageIntelligenceService,
|
|
6949
|
-
githitsService: new RefreshingGitHitsService(apiUrl, tokenManager)
|
|
6623
|
+
githitsService: new RefreshingGitHitsService(apiUrl, tokenManager, undefined, serviceRuntime)
|
|
6950
6624
|
};
|
|
6951
6625
|
});
|
|
6952
6626
|
}
|
|
6953
6627
|
|
|
6954
|
-
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 };
|