mobbdev 1.4.44 → 1.4.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/args/commands/upload_ai_blame.mjs +55 -18
- package/dist/index.mjs +432 -114
- package/package.json +1 -1
|
@@ -4405,6 +4405,8 @@ var ScanContext = {
|
|
|
4405
4405
|
};
|
|
4406
4406
|
|
|
4407
4407
|
// src/utils/proxy.ts
|
|
4408
|
+
import { Agent as HttpAgent } from "http";
|
|
4409
|
+
import { Agent as HttpsAgent } from "https";
|
|
4408
4410
|
import fetchOrig from "cross-fetch";
|
|
4409
4411
|
import Debug2 from "debug";
|
|
4410
4412
|
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
@@ -4418,31 +4420,33 @@ function httpToWsUrl(url) {
|
|
|
4418
4420
|
|
|
4419
4421
|
// src/utils/proxy.ts
|
|
4420
4422
|
var debug2 = Debug2("mobbdev:proxy");
|
|
4423
|
+
var UPLOAD_MAX_SOCKETS = 8;
|
|
4424
|
+
var uploadHttpAgent;
|
|
4425
|
+
var uploadHttpsAgent;
|
|
4426
|
+
var uploadProxyAgents = /* @__PURE__ */ new Map();
|
|
4421
4427
|
function getHttpProxy() {
|
|
4422
4428
|
return process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || "";
|
|
4423
4429
|
}
|
|
4424
4430
|
function getHttpProxyOnly() {
|
|
4425
4431
|
return process.env["HTTP_PROXY"] || "";
|
|
4426
4432
|
}
|
|
4433
|
+
function resolveProxyForUrl(url) {
|
|
4434
|
+
const parsedUrl = new URL(url);
|
|
4435
|
+
const hostname = parsedUrl.hostname.toLowerCase();
|
|
4436
|
+
const isHttp = parsedUrl.protocol === "http:";
|
|
4437
|
+
const isHttps = parsedUrl.protocol === "https:";
|
|
4438
|
+
const isLocal = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
4439
|
+
const noProxy = process.env["NO_PROXY"] || process.env["no_proxy"];
|
|
4440
|
+
const noProxyMatch = noProxy?.split(",").map((h) => h.trim().toLowerCase()).some((h) => h === hostname || h === "*") ?? false;
|
|
4441
|
+
if (isLocal || noProxyMatch) {
|
|
4442
|
+
return { proxy: "", isHttp, isHttps };
|
|
4443
|
+
}
|
|
4444
|
+
const proxy = isHttps ? getHttpProxy() : isHttp ? getHttpProxyOnly() : "";
|
|
4445
|
+
return { proxy, isHttp, isHttps };
|
|
4446
|
+
}
|
|
4427
4447
|
function getProxyAgent(url) {
|
|
4428
4448
|
try {
|
|
4429
|
-
const
|
|
4430
|
-
const hostname = parsedUrl.hostname.toLowerCase();
|
|
4431
|
-
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]") {
|
|
4432
|
-
debug2("Skipping proxy for localhost URL: %s", url);
|
|
4433
|
-
return void 0;
|
|
4434
|
-
}
|
|
4435
|
-
const noProxy = process.env["NO_PROXY"] || process.env["no_proxy"];
|
|
4436
|
-
if (noProxy) {
|
|
4437
|
-
const noProxyList = noProxy.split(",").map((h) => h.trim().toLowerCase());
|
|
4438
|
-
if (noProxyList.includes(hostname) || noProxyList.includes("*")) {
|
|
4439
|
-
debug2("Skipping proxy due to NO_PROXY for: %s", url);
|
|
4440
|
-
return void 0;
|
|
4441
|
-
}
|
|
4442
|
-
}
|
|
4443
|
-
const isHttp = parsedUrl.protocol === "http:";
|
|
4444
|
-
const isHttps = parsedUrl.protocol === "https:";
|
|
4445
|
-
const proxy = isHttps ? getHttpProxy() : isHttp ? getHttpProxyOnly() : null;
|
|
4449
|
+
const { proxy } = resolveProxyForUrl(url);
|
|
4446
4450
|
if (proxy) {
|
|
4447
4451
|
debug2("Using proxy %s for %s", proxy, url);
|
|
4448
4452
|
return new HttpsProxyAgent(proxy);
|
|
@@ -4452,6 +4456,39 @@ function getProxyAgent(url) {
|
|
|
4452
4456
|
}
|
|
4453
4457
|
return void 0;
|
|
4454
4458
|
}
|
|
4459
|
+
function getUploadAgent(url) {
|
|
4460
|
+
try {
|
|
4461
|
+
const { proxy, isHttp, isHttps } = resolveProxyForUrl(url);
|
|
4462
|
+
if (proxy) {
|
|
4463
|
+
let agent = uploadProxyAgents.get(proxy);
|
|
4464
|
+
if (!agent) {
|
|
4465
|
+
agent = new HttpsProxyAgent(proxy, {
|
|
4466
|
+
keepAlive: true,
|
|
4467
|
+
maxSockets: UPLOAD_MAX_SOCKETS
|
|
4468
|
+
});
|
|
4469
|
+
uploadProxyAgents.set(proxy, agent);
|
|
4470
|
+
}
|
|
4471
|
+
return agent;
|
|
4472
|
+
}
|
|
4473
|
+
if (isHttps) {
|
|
4474
|
+
uploadHttpsAgent ?? (uploadHttpsAgent = new HttpsAgent({
|
|
4475
|
+
keepAlive: true,
|
|
4476
|
+
maxSockets: UPLOAD_MAX_SOCKETS
|
|
4477
|
+
}));
|
|
4478
|
+
return uploadHttpsAgent;
|
|
4479
|
+
}
|
|
4480
|
+
if (isHttp) {
|
|
4481
|
+
uploadHttpAgent ?? (uploadHttpAgent = new HttpAgent({
|
|
4482
|
+
keepAlive: true,
|
|
4483
|
+
maxSockets: UPLOAD_MAX_SOCKETS
|
|
4484
|
+
}));
|
|
4485
|
+
return uploadHttpAgent;
|
|
4486
|
+
}
|
|
4487
|
+
} catch (err) {
|
|
4488
|
+
debug2(`No upload agent for ${url}. Reason: ${err.message}`);
|
|
4489
|
+
}
|
|
4490
|
+
return void 0;
|
|
4491
|
+
}
|
|
4455
4492
|
var fetchWithProxy = (url, options = {}) => {
|
|
4456
4493
|
try {
|
|
4457
4494
|
const agent = getProxyAgent(url.toString());
|
|
@@ -5659,7 +5696,7 @@ async function uploadFile({
|
|
|
5659
5696
|
)
|
|
5660
5697
|
);
|
|
5661
5698
|
}
|
|
5662
|
-
const agent =
|
|
5699
|
+
const agent = getUploadAgent(url);
|
|
5663
5700
|
const effectiveSignal = signal ?? AbortSignal.timeout(computeUploadTimeoutMs(file));
|
|
5664
5701
|
const response = await fetch3(url, {
|
|
5665
5702
|
method: "POST",
|
package/dist/index.mjs
CHANGED
|
@@ -11371,6 +11371,8 @@ __publicField(_ReportDigestError, "defaultMessage", "\u{1F575}\uFE0F\u200D\u2642
|
|
|
11371
11371
|
var ReportDigestError = _ReportDigestError;
|
|
11372
11372
|
|
|
11373
11373
|
// src/utils/proxy.ts
|
|
11374
|
+
import { Agent as HttpAgent } from "http";
|
|
11375
|
+
import { Agent as HttpsAgent } from "https";
|
|
11374
11376
|
import fetchOrig from "cross-fetch";
|
|
11375
11377
|
import Debug5 from "debug";
|
|
11376
11378
|
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
@@ -11384,31 +11386,33 @@ function httpToWsUrl(url) {
|
|
|
11384
11386
|
|
|
11385
11387
|
// src/utils/proxy.ts
|
|
11386
11388
|
var debug6 = Debug5("mobbdev:proxy");
|
|
11389
|
+
var UPLOAD_MAX_SOCKETS = 8;
|
|
11390
|
+
var uploadHttpAgent;
|
|
11391
|
+
var uploadHttpsAgent;
|
|
11392
|
+
var uploadProxyAgents = /* @__PURE__ */ new Map();
|
|
11387
11393
|
function getHttpProxy() {
|
|
11388
11394
|
return process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || "";
|
|
11389
11395
|
}
|
|
11390
11396
|
function getHttpProxyOnly() {
|
|
11391
11397
|
return process.env["HTTP_PROXY"] || "";
|
|
11392
11398
|
}
|
|
11399
|
+
function resolveProxyForUrl(url) {
|
|
11400
|
+
const parsedUrl = new URL(url);
|
|
11401
|
+
const hostname = parsedUrl.hostname.toLowerCase();
|
|
11402
|
+
const isHttp = parsedUrl.protocol === "http:";
|
|
11403
|
+
const isHttps = parsedUrl.protocol === "https:";
|
|
11404
|
+
const isLocal = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
11405
|
+
const noProxy = process.env["NO_PROXY"] || process.env["no_proxy"];
|
|
11406
|
+
const noProxyMatch = noProxy?.split(",").map((h) => h.trim().toLowerCase()).some((h) => h === hostname || h === "*") ?? false;
|
|
11407
|
+
if (isLocal || noProxyMatch) {
|
|
11408
|
+
return { proxy: "", isHttp, isHttps };
|
|
11409
|
+
}
|
|
11410
|
+
const proxy = isHttps ? getHttpProxy() : isHttp ? getHttpProxyOnly() : "";
|
|
11411
|
+
return { proxy, isHttp, isHttps };
|
|
11412
|
+
}
|
|
11393
11413
|
function getProxyAgent(url) {
|
|
11394
11414
|
try {
|
|
11395
|
-
const
|
|
11396
|
-
const hostname = parsedUrl.hostname.toLowerCase();
|
|
11397
|
-
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]") {
|
|
11398
|
-
debug6("Skipping proxy for localhost URL: %s", url);
|
|
11399
|
-
return void 0;
|
|
11400
|
-
}
|
|
11401
|
-
const noProxy = process.env["NO_PROXY"] || process.env["no_proxy"];
|
|
11402
|
-
if (noProxy) {
|
|
11403
|
-
const noProxyList = noProxy.split(",").map((h) => h.trim().toLowerCase());
|
|
11404
|
-
if (noProxyList.includes(hostname) || noProxyList.includes("*")) {
|
|
11405
|
-
debug6("Skipping proxy due to NO_PROXY for: %s", url);
|
|
11406
|
-
return void 0;
|
|
11407
|
-
}
|
|
11408
|
-
}
|
|
11409
|
-
const isHttp = parsedUrl.protocol === "http:";
|
|
11410
|
-
const isHttps = parsedUrl.protocol === "https:";
|
|
11411
|
-
const proxy = isHttps ? getHttpProxy() : isHttp ? getHttpProxyOnly() : null;
|
|
11415
|
+
const { proxy } = resolveProxyForUrl(url);
|
|
11412
11416
|
if (proxy) {
|
|
11413
11417
|
debug6("Using proxy %s for %s", proxy, url);
|
|
11414
11418
|
return new HttpsProxyAgent(proxy);
|
|
@@ -11418,6 +11422,39 @@ function getProxyAgent(url) {
|
|
|
11418
11422
|
}
|
|
11419
11423
|
return void 0;
|
|
11420
11424
|
}
|
|
11425
|
+
function getUploadAgent(url) {
|
|
11426
|
+
try {
|
|
11427
|
+
const { proxy, isHttp, isHttps } = resolveProxyForUrl(url);
|
|
11428
|
+
if (proxy) {
|
|
11429
|
+
let agent = uploadProxyAgents.get(proxy);
|
|
11430
|
+
if (!agent) {
|
|
11431
|
+
agent = new HttpsProxyAgent(proxy, {
|
|
11432
|
+
keepAlive: true,
|
|
11433
|
+
maxSockets: UPLOAD_MAX_SOCKETS
|
|
11434
|
+
});
|
|
11435
|
+
uploadProxyAgents.set(proxy, agent);
|
|
11436
|
+
}
|
|
11437
|
+
return agent;
|
|
11438
|
+
}
|
|
11439
|
+
if (isHttps) {
|
|
11440
|
+
uploadHttpsAgent ?? (uploadHttpsAgent = new HttpsAgent({
|
|
11441
|
+
keepAlive: true,
|
|
11442
|
+
maxSockets: UPLOAD_MAX_SOCKETS
|
|
11443
|
+
}));
|
|
11444
|
+
return uploadHttpsAgent;
|
|
11445
|
+
}
|
|
11446
|
+
if (isHttp) {
|
|
11447
|
+
uploadHttpAgent ?? (uploadHttpAgent = new HttpAgent({
|
|
11448
|
+
keepAlive: true,
|
|
11449
|
+
maxSockets: UPLOAD_MAX_SOCKETS
|
|
11450
|
+
}));
|
|
11451
|
+
return uploadHttpAgent;
|
|
11452
|
+
}
|
|
11453
|
+
} catch (err) {
|
|
11454
|
+
debug6(`No upload agent for ${url}. Reason: ${err.message}`);
|
|
11455
|
+
}
|
|
11456
|
+
return void 0;
|
|
11457
|
+
}
|
|
11421
11458
|
var fetchWithProxy = (url, options = {}) => {
|
|
11422
11459
|
try {
|
|
11423
11460
|
const agent = getProxyAgent(url.toString());
|
|
@@ -12301,6 +12338,16 @@ var S3UploadError = class extends Error {
|
|
|
12301
12338
|
this.name = "S3UploadError";
|
|
12302
12339
|
}
|
|
12303
12340
|
};
|
|
12341
|
+
var ACTIONABLE_S3_CODES = /* @__PURE__ */ new Set([
|
|
12342
|
+
"AccessDenied",
|
|
12343
|
+
"ExpiredToken",
|
|
12344
|
+
"InvalidAccessKeyId",
|
|
12345
|
+
"SignatureDoesNotMatch",
|
|
12346
|
+
"TokenRefreshRequired"
|
|
12347
|
+
]);
|
|
12348
|
+
function isActionableS3Error(err) {
|
|
12349
|
+
return err instanceof S3UploadError && err.s3Code !== void 0 && ACTIONABLE_S3_CODES.has(err.s3Code);
|
|
12350
|
+
}
|
|
12304
12351
|
function parseS3ErrorBody(body) {
|
|
12305
12352
|
return {
|
|
12306
12353
|
code: body.match(/<Code>([^<]+)<\/Code>/)?.[1],
|
|
@@ -12349,7 +12396,7 @@ async function uploadFile({
|
|
|
12349
12396
|
)
|
|
12350
12397
|
);
|
|
12351
12398
|
}
|
|
12352
|
-
const agent =
|
|
12399
|
+
const agent = getUploadAgent(url);
|
|
12353
12400
|
const effectiveSignal = signal ?? AbortSignal.timeout(computeUploadTimeoutMs(file));
|
|
12354
12401
|
const response = await fetch3(url, {
|
|
12355
12402
|
method: "POST",
|
|
@@ -15950,7 +15997,179 @@ import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
|
15950
15997
|
import * as os8 from "os";
|
|
15951
15998
|
import path24 from "path";
|
|
15952
15999
|
import { setTimeout as sleep4 } from "timers/promises";
|
|
15953
|
-
import
|
|
16000
|
+
import Configstore4 from "configstore";
|
|
16001
|
+
|
|
16002
|
+
// src/features/analysis/context_upload_ledger.ts
|
|
16003
|
+
import Configstore2 from "configstore";
|
|
16004
|
+
var UPLOADED_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
16005
|
+
var FAILURE_BACKOFF_BASE_MS = 60 * 1e3;
|
|
16006
|
+
var FAILURE_BACKOFF_MAX_MS = 60 * 60 * 1e3;
|
|
16007
|
+
var FAILURE_LOG_WINDOW_MS = 30 * 60 * 1e3;
|
|
16008
|
+
var BREAKER_FAILURE_THRESHOLD = 8;
|
|
16009
|
+
var BREAKER_FAILURE_WINDOW_MS = 5 * 60 * 1e3;
|
|
16010
|
+
var BREAKER_BASE_COOLDOWN_MS = 30 * 1e3;
|
|
16011
|
+
var BREAKER_MAX_COOLDOWN_MS = 10 * 60 * 1e3;
|
|
16012
|
+
var MAX_LEDGER_ENTRIES = 5e3;
|
|
16013
|
+
var LEDGER_NAMESPACE = "mobbdev-context-upload-ledger";
|
|
16014
|
+
var ENTRIES_KEY = "entries";
|
|
16015
|
+
var BREAKER_KEY = "breaker";
|
|
16016
|
+
var NOOP_UPLOAD_LEDGER = {
|
|
16017
|
+
isUploaded: () => false,
|
|
16018
|
+
isBackedOff: () => false,
|
|
16019
|
+
isBreakerOpen: () => false,
|
|
16020
|
+
onSuccess: () => {
|
|
16021
|
+
},
|
|
16022
|
+
onFailure: () => true,
|
|
16023
|
+
flush: () => {
|
|
16024
|
+
}
|
|
16025
|
+
};
|
|
16026
|
+
var ConfigstoreUploadLedger = class {
|
|
16027
|
+
constructor(store) {
|
|
16028
|
+
__publicField(this, "store");
|
|
16029
|
+
__publicField(this, "entries");
|
|
16030
|
+
__publicField(this, "breaker");
|
|
16031
|
+
__publicField(this, "dirty", false);
|
|
16032
|
+
this.store = store ?? new Configstore2(LEDGER_NAMESPACE);
|
|
16033
|
+
this.entries = loadEntries(this.store);
|
|
16034
|
+
this.breaker = loadBreaker(this.store);
|
|
16035
|
+
}
|
|
16036
|
+
isUploaded(md5, now) {
|
|
16037
|
+
const entry = this.entries.get(md5);
|
|
16038
|
+
if (!entry?.uploadedAt) {
|
|
16039
|
+
return false;
|
|
16040
|
+
}
|
|
16041
|
+
if (now - entry.uploadedAt > UPLOADED_TTL_MS) {
|
|
16042
|
+
return false;
|
|
16043
|
+
}
|
|
16044
|
+
entry.touchedAt = now;
|
|
16045
|
+
this.dirty = true;
|
|
16046
|
+
return true;
|
|
16047
|
+
}
|
|
16048
|
+
isBackedOff(md5, now) {
|
|
16049
|
+
const entry = this.entries.get(md5);
|
|
16050
|
+
return entry?.nextRetryAt !== void 0 && entry.nextRetryAt > now;
|
|
16051
|
+
}
|
|
16052
|
+
isBreakerOpen(now) {
|
|
16053
|
+
return this.breaker.openUntil > now;
|
|
16054
|
+
}
|
|
16055
|
+
onSuccess(md5, now) {
|
|
16056
|
+
const entry = this.getOrCreate(md5, now);
|
|
16057
|
+
entry.uploadedAt = now;
|
|
16058
|
+
entry.touchedAt = now;
|
|
16059
|
+
delete entry.attempts;
|
|
16060
|
+
delete entry.nextRetryAt;
|
|
16061
|
+
delete entry.lastLoggedAt;
|
|
16062
|
+
this.breaker.consecutiveFailures = 0;
|
|
16063
|
+
this.breaker.lastFailureAt = 0;
|
|
16064
|
+
this.breaker.trips = 0;
|
|
16065
|
+
this.breaker.openUntil = 0;
|
|
16066
|
+
this.dirty = true;
|
|
16067
|
+
}
|
|
16068
|
+
onFailure(md5, now) {
|
|
16069
|
+
const entry = this.getOrCreate(md5, now);
|
|
16070
|
+
const attempts = (entry.attempts ?? 0) + 1;
|
|
16071
|
+
const delay3 = Math.min(
|
|
16072
|
+
FAILURE_BACKOFF_BASE_MS * 2 ** (attempts - 1),
|
|
16073
|
+
FAILURE_BACKOFF_MAX_MS
|
|
16074
|
+
);
|
|
16075
|
+
entry.attempts = attempts;
|
|
16076
|
+
entry.nextRetryAt = now + delay3;
|
|
16077
|
+
entry.touchedAt = now;
|
|
16078
|
+
if (now - this.breaker.lastFailureAt > BREAKER_FAILURE_WINDOW_MS) {
|
|
16079
|
+
this.breaker.consecutiveFailures = 0;
|
|
16080
|
+
}
|
|
16081
|
+
this.breaker.lastFailureAt = now;
|
|
16082
|
+
this.breaker.consecutiveFailures += 1;
|
|
16083
|
+
if (this.breaker.consecutiveFailures >= BREAKER_FAILURE_THRESHOLD && this.breaker.openUntil <= now) {
|
|
16084
|
+
const cooldown = Math.min(
|
|
16085
|
+
BREAKER_BASE_COOLDOWN_MS * 2 ** this.breaker.trips,
|
|
16086
|
+
BREAKER_MAX_COOLDOWN_MS
|
|
16087
|
+
);
|
|
16088
|
+
this.breaker.trips += 1;
|
|
16089
|
+
this.breaker.openUntil = now + cooldown;
|
|
16090
|
+
}
|
|
16091
|
+
const shouldLog = entry.lastLoggedAt === void 0 || now - entry.lastLoggedAt >= FAILURE_LOG_WINDOW_MS;
|
|
16092
|
+
if (shouldLog) {
|
|
16093
|
+
entry.lastLoggedAt = now;
|
|
16094
|
+
}
|
|
16095
|
+
this.dirty = true;
|
|
16096
|
+
return shouldLog;
|
|
16097
|
+
}
|
|
16098
|
+
flush() {
|
|
16099
|
+
if (!this.dirty) {
|
|
16100
|
+
return;
|
|
16101
|
+
}
|
|
16102
|
+
this.evictIfNeeded();
|
|
16103
|
+
try {
|
|
16104
|
+
this.store.set(ENTRIES_KEY, Object.fromEntries(this.entries));
|
|
16105
|
+
this.store.set(BREAKER_KEY, this.breaker);
|
|
16106
|
+
this.dirty = false;
|
|
16107
|
+
} catch {
|
|
16108
|
+
}
|
|
16109
|
+
}
|
|
16110
|
+
getOrCreate(md5, now) {
|
|
16111
|
+
let entry = this.entries.get(md5);
|
|
16112
|
+
if (!entry) {
|
|
16113
|
+
entry = { touchedAt: now };
|
|
16114
|
+
this.entries.set(md5, entry);
|
|
16115
|
+
}
|
|
16116
|
+
return entry;
|
|
16117
|
+
}
|
|
16118
|
+
/** Evict least-recently-touched entries when over the cap. */
|
|
16119
|
+
evictIfNeeded() {
|
|
16120
|
+
if (this.entries.size <= MAX_LEDGER_ENTRIES) {
|
|
16121
|
+
return;
|
|
16122
|
+
}
|
|
16123
|
+
const sorted = [...this.entries.entries()].sort(
|
|
16124
|
+
(a, b) => a[1].touchedAt - b[1].touchedAt
|
|
16125
|
+
);
|
|
16126
|
+
const removeCount = this.entries.size - MAX_LEDGER_ENTRIES;
|
|
16127
|
+
for (let i = 0; i < removeCount; i++) {
|
|
16128
|
+
this.entries.delete(sorted[i][0]);
|
|
16129
|
+
}
|
|
16130
|
+
}
|
|
16131
|
+
};
|
|
16132
|
+
function finiteOrUndefined(value) {
|
|
16133
|
+
const n = Number(value);
|
|
16134
|
+
return Number.isFinite(n) && n >= 0 ? n : void 0;
|
|
16135
|
+
}
|
|
16136
|
+
function loadEntries(store) {
|
|
16137
|
+
const map = /* @__PURE__ */ new Map();
|
|
16138
|
+
try {
|
|
16139
|
+
const raw = store.get(ENTRIES_KEY);
|
|
16140
|
+
if (raw && typeof raw === "object") {
|
|
16141
|
+
for (const [md5, entry] of Object.entries(raw)) {
|
|
16142
|
+
if (!entry || typeof entry !== "object") {
|
|
16143
|
+
continue;
|
|
16144
|
+
}
|
|
16145
|
+
map.set(md5, {
|
|
16146
|
+
uploadedAt: finiteOrUndefined(entry.uploadedAt),
|
|
16147
|
+
attempts: finiteOrUndefined(entry.attempts),
|
|
16148
|
+
nextRetryAt: finiteOrUndefined(entry.nextRetryAt),
|
|
16149
|
+
lastLoggedAt: finiteOrUndefined(entry.lastLoggedAt),
|
|
16150
|
+
touchedAt: finiteOrUndefined(entry.touchedAt) ?? 0
|
|
16151
|
+
});
|
|
16152
|
+
}
|
|
16153
|
+
}
|
|
16154
|
+
} catch {
|
|
16155
|
+
}
|
|
16156
|
+
return map;
|
|
16157
|
+
}
|
|
16158
|
+
function loadBreaker(store) {
|
|
16159
|
+
try {
|
|
16160
|
+
const raw = store.get(BREAKER_KEY);
|
|
16161
|
+
if (raw && typeof raw === "object") {
|
|
16162
|
+
return {
|
|
16163
|
+
consecutiveFailures: finiteOrUndefined(raw.consecutiveFailures) ?? 0,
|
|
16164
|
+
lastFailureAt: finiteOrUndefined(raw.lastFailureAt) ?? 0,
|
|
16165
|
+
trips: finiteOrUndefined(raw.trips) ?? 0,
|
|
16166
|
+
openUntil: finiteOrUndefined(raw.openUntil) ?? 0
|
|
16167
|
+
};
|
|
16168
|
+
}
|
|
16169
|
+
} catch {
|
|
16170
|
+
}
|
|
16171
|
+
return { consecutiveFailures: 0, lastFailureAt: 0, trips: 0, openUntil: 0 };
|
|
16172
|
+
}
|
|
15954
16173
|
|
|
15955
16174
|
// src/features/analysis/skill_quarantine/runQuarantineCheck.ts
|
|
15956
16175
|
import { stat as stat3 } from "fs/promises";
|
|
@@ -16228,8 +16447,8 @@ var SCAN_PATHS = {
|
|
|
16228
16447
|
// src/features/analysis/context_file_scanner.ts
|
|
16229
16448
|
var MAX_CONTEXT_FILE_SIZE = 20 * 1024 * 1024;
|
|
16230
16449
|
var SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
16231
|
-
var
|
|
16232
|
-
var
|
|
16450
|
+
var FAILURE_BACKOFF_BASE_MS2 = 60 * 1e3;
|
|
16451
|
+
var FAILURE_BACKOFF_MAX_MS2 = 60 * 60 * 1e3;
|
|
16233
16452
|
var sessionMtimes = /* @__PURE__ */ new Map();
|
|
16234
16453
|
function markContextFilesUploaded(sessionId, files, skills) {
|
|
16235
16454
|
const entry = getOrCreateSessionEntry(sessionId);
|
|
@@ -16251,8 +16470,8 @@ function markContextUploadFailed(sessionId, files, skills) {
|
|
|
16251
16470
|
const recordFailure = (key) => {
|
|
16252
16471
|
const attempts = (entry.failures.get(key)?.attempts ?? 0) + 1;
|
|
16253
16472
|
const delay3 = Math.min(
|
|
16254
|
-
|
|
16255
|
-
|
|
16473
|
+
FAILURE_BACKOFF_BASE_MS2 * 2 ** (attempts - 1),
|
|
16474
|
+
FAILURE_BACKOFF_MAX_MS2
|
|
16256
16475
|
);
|
|
16257
16476
|
entry.failures.set(key, { attempts, nextRetryAt: now + delay3 });
|
|
16258
16477
|
};
|
|
@@ -17367,7 +17586,12 @@ var DAEMON_POLL_INTERVAL_MS = (() => {
|
|
|
17367
17586
|
var HEARTBEAT_STALE_MS = 3e4;
|
|
17368
17587
|
var TRANSCRIPT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
17369
17588
|
var DAEMON_CHUNK_SIZE = 50;
|
|
17370
|
-
var CONTEXT_SCAN_INTERVAL_MS =
|
|
17589
|
+
var CONTEXT_SCAN_INTERVAL_MS = (() => {
|
|
17590
|
+
const raw = Number(process.env["MOBB_CONTEXT_SCAN_INTERVAL_MS"]);
|
|
17591
|
+
if (!Number.isFinite(raw) || raw <= 0) return 15 * 6e4;
|
|
17592
|
+
return Math.min(Math.max(raw, 1e4), 60 * 6e4);
|
|
17593
|
+
})();
|
|
17594
|
+
var DAEMON_SHUTDOWN_DRAIN_MS = 6e3;
|
|
17371
17595
|
|
|
17372
17596
|
// src/features/claude_code/daemon_pid_file.ts
|
|
17373
17597
|
function getMobbdevDir() {
|
|
@@ -17451,7 +17675,7 @@ import { promisify } from "util";
|
|
|
17451
17675
|
import { setTimeout as sleep3 } from "timers/promises";
|
|
17452
17676
|
import pLimit6 from "p-limit";
|
|
17453
17677
|
init_client_generates();
|
|
17454
|
-
var UPLOAD_CONCURRENCY =
|
|
17678
|
+
var UPLOAD_CONCURRENCY = 1;
|
|
17455
17679
|
var UPLOAD_MAX_ATTEMPTS = 2;
|
|
17456
17680
|
var UPLOAD_RETRY_BASE_MS = 250;
|
|
17457
17681
|
async function uploadWithRetry(args) {
|
|
@@ -17469,6 +17693,29 @@ async function uploadWithRetry(args) {
|
|
|
17469
17693
|
}
|
|
17470
17694
|
}
|
|
17471
17695
|
}
|
|
17696
|
+
var uploadLimit = pLimit6(UPLOAD_CONCURRENCY);
|
|
17697
|
+
var inFlightUploads = /* @__PURE__ */ new Map();
|
|
17698
|
+
async function coalescedUpload(s3Key, run2) {
|
|
17699
|
+
const existing = inFlightUploads.get(s3Key);
|
|
17700
|
+
if (existing) {
|
|
17701
|
+
try {
|
|
17702
|
+
await existing;
|
|
17703
|
+
return { leader: false, ok: true };
|
|
17704
|
+
} catch (err) {
|
|
17705
|
+
return { leader: false, ok: false, err };
|
|
17706
|
+
}
|
|
17707
|
+
}
|
|
17708
|
+
const p = uploadLimit(run2);
|
|
17709
|
+
inFlightUploads.set(s3Key, p);
|
|
17710
|
+
try {
|
|
17711
|
+
await p;
|
|
17712
|
+
return { leader: true, ok: true };
|
|
17713
|
+
} catch (err) {
|
|
17714
|
+
return { leader: true, ok: false, err };
|
|
17715
|
+
} finally {
|
|
17716
|
+
inFlightUploads.delete(s3Key);
|
|
17717
|
+
}
|
|
17718
|
+
}
|
|
17472
17719
|
async function uploadContextRecords(opts) {
|
|
17473
17720
|
const {
|
|
17474
17721
|
processedFiles,
|
|
@@ -17483,6 +17730,7 @@ async function uploadContextRecords(opts) {
|
|
|
17483
17730
|
branch,
|
|
17484
17731
|
commitSha,
|
|
17485
17732
|
clientVersion,
|
|
17733
|
+
ledger = NOOP_UPLOAD_LEDGER,
|
|
17486
17734
|
onFileError,
|
|
17487
17735
|
onSkillError
|
|
17488
17736
|
} = opts;
|
|
@@ -17491,82 +17739,97 @@ async function uploadContextRecords(opts) {
|
|
|
17491
17739
|
const uploadedSkillGroups = [];
|
|
17492
17740
|
const failedFiles = [];
|
|
17493
17741
|
const failedSkillGroups = [];
|
|
17494
|
-
|
|
17742
|
+
let dedupedCount = 0;
|
|
17743
|
+
let suppressedCount = 0;
|
|
17495
17744
|
const extraFields = {
|
|
17496
17745
|
...repositoryUrl !== void 0 && { repositoryUrl },
|
|
17497
17746
|
...branch !== void 0 && { branch },
|
|
17498
17747
|
...commitSha !== void 0 && { commitSha },
|
|
17499
17748
|
...clientVersion !== void 0 && { clientVersion }
|
|
17500
17749
|
};
|
|
17750
|
+
const processItem = async (item) => {
|
|
17751
|
+
const nowMs = Date.now();
|
|
17752
|
+
if (ledger.isUploaded(item.md5, nowMs)) {
|
|
17753
|
+
dedupedCount++;
|
|
17754
|
+
} else if (ledger.isBreakerOpen(nowMs) || ledger.isBackedOff(item.md5, nowMs)) {
|
|
17755
|
+
suppressedCount++;
|
|
17756
|
+
return;
|
|
17757
|
+
} else {
|
|
17758
|
+
const { leader, ok, err } = await coalescedUpload(
|
|
17759
|
+
item.s3Key,
|
|
17760
|
+
() => uploadWithRetry({
|
|
17761
|
+
file: item.getPayload(),
|
|
17762
|
+
url,
|
|
17763
|
+
uploadKey: item.s3Key,
|
|
17764
|
+
uploadFields
|
|
17765
|
+
})
|
|
17766
|
+
);
|
|
17767
|
+
if (!ok) {
|
|
17768
|
+
item.markFailed();
|
|
17769
|
+
if (leader) {
|
|
17770
|
+
const logByWindow = ledger.onFailure(item.md5, Date.now());
|
|
17771
|
+
if (logByWindow || isActionableS3Error(err)) {
|
|
17772
|
+
item.onError?.(item.errorName, err);
|
|
17773
|
+
}
|
|
17774
|
+
}
|
|
17775
|
+
return;
|
|
17776
|
+
}
|
|
17777
|
+
if (leader) {
|
|
17778
|
+
ledger.onSuccess(item.md5, Date.now());
|
|
17779
|
+
}
|
|
17780
|
+
}
|
|
17781
|
+
records.push({
|
|
17782
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17783
|
+
platform: platform2,
|
|
17784
|
+
recordId: `ctx:${sessionId}:${item.md5}`,
|
|
17785
|
+
recordTimestamp: now,
|
|
17786
|
+
blameType: "CHAT" /* Chat */,
|
|
17787
|
+
rawDataS3Key: item.s3Key,
|
|
17788
|
+
...extraFields,
|
|
17789
|
+
context: {
|
|
17790
|
+
md5: item.md5,
|
|
17791
|
+
category: item.context.category,
|
|
17792
|
+
name: item.context.name,
|
|
17793
|
+
sizeBytes: item.context.sizeBytes,
|
|
17794
|
+
filePath: item.context.filePath,
|
|
17795
|
+
sessionId
|
|
17796
|
+
}
|
|
17797
|
+
});
|
|
17798
|
+
item.markUploaded();
|
|
17799
|
+
};
|
|
17501
17800
|
const tasks = [
|
|
17502
17801
|
...processedFiles.map(
|
|
17503
|
-
(pf) =>
|
|
17504
|
-
|
|
17505
|
-
|
|
17506
|
-
|
|
17507
|
-
|
|
17508
|
-
|
|
17509
|
-
|
|
17510
|
-
|
|
17511
|
-
|
|
17512
|
-
}
|
|
17513
|
-
|
|
17514
|
-
|
|
17515
|
-
|
|
17516
|
-
|
|
17517
|
-
records.push({
|
|
17518
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17519
|
-
platform: platform2,
|
|
17520
|
-
recordId: `ctx:${sessionId}:${pf.md5}`,
|
|
17521
|
-
recordTimestamp: now,
|
|
17522
|
-
blameType: "CHAT" /* Chat */,
|
|
17523
|
-
rawDataS3Key: s3Key,
|
|
17524
|
-
...extraFields,
|
|
17525
|
-
context: {
|
|
17526
|
-
md5: pf.md5,
|
|
17527
|
-
category: pf.entry.category,
|
|
17528
|
-
name: pf.entry.name,
|
|
17529
|
-
sizeBytes: pf.sizeBytes,
|
|
17530
|
-
filePath: pf.entry.path,
|
|
17531
|
-
sessionId
|
|
17532
|
-
}
|
|
17533
|
-
});
|
|
17534
|
-
uploadedFiles.push(pf.entry);
|
|
17802
|
+
(pf) => processItem({
|
|
17803
|
+
md5: pf.md5,
|
|
17804
|
+
s3Key: `${keyPrefix}ctx-${pf.md5}.bin`,
|
|
17805
|
+
getPayload: () => Buffer.from(pf.sanitizedContent, "utf-8"),
|
|
17806
|
+
context: {
|
|
17807
|
+
category: pf.entry.category,
|
|
17808
|
+
name: pf.entry.name,
|
|
17809
|
+
sizeBytes: pf.sizeBytes,
|
|
17810
|
+
filePath: pf.entry.path
|
|
17811
|
+
},
|
|
17812
|
+
errorName: pf.entry.name,
|
|
17813
|
+
onError: onFileError,
|
|
17814
|
+
markUploaded: () => uploadedFiles.push(pf.entry),
|
|
17815
|
+
markFailed: () => failedFiles.push(pf.entry)
|
|
17535
17816
|
})
|
|
17536
17817
|
),
|
|
17537
17818
|
...processedSkills.map(
|
|
17538
|
-
(ps) =>
|
|
17539
|
-
|
|
17540
|
-
|
|
17541
|
-
|
|
17542
|
-
|
|
17543
|
-
|
|
17544
|
-
|
|
17545
|
-
|
|
17546
|
-
|
|
17547
|
-
}
|
|
17548
|
-
|
|
17549
|
-
|
|
17550
|
-
|
|
17551
|
-
|
|
17552
|
-
records.push({
|
|
17553
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17554
|
-
platform: platform2,
|
|
17555
|
-
recordId: `ctx:${sessionId}:${ps.md5}`,
|
|
17556
|
-
recordTimestamp: now,
|
|
17557
|
-
blameType: "CHAT" /* Chat */,
|
|
17558
|
-
rawDataS3Key: s3Key,
|
|
17559
|
-
...extraFields,
|
|
17560
|
-
context: {
|
|
17561
|
-
md5: ps.md5,
|
|
17562
|
-
category: SKILL_CATEGORY,
|
|
17563
|
-
name: ps.group.name,
|
|
17564
|
-
sizeBytes: ps.sizeBytes,
|
|
17565
|
-
filePath: ps.group.skillPath,
|
|
17566
|
-
sessionId
|
|
17567
|
-
}
|
|
17568
|
-
});
|
|
17569
|
-
uploadedSkillGroups.push(ps.group);
|
|
17819
|
+
(ps) => processItem({
|
|
17820
|
+
md5: ps.md5,
|
|
17821
|
+
s3Key: `${keyPrefix}skill-${ps.md5}.zip`,
|
|
17822
|
+
getPayload: () => ps.zipBuffer,
|
|
17823
|
+
context: {
|
|
17824
|
+
category: SKILL_CATEGORY,
|
|
17825
|
+
name: ps.group.name,
|
|
17826
|
+
sizeBytes: ps.sizeBytes,
|
|
17827
|
+
filePath: ps.group.skillPath
|
|
17828
|
+
},
|
|
17829
|
+
errorName: ps.group.name,
|
|
17830
|
+
onError: onSkillError,
|
|
17831
|
+
markUploaded: () => uploadedSkillGroups.push(ps.group),
|
|
17832
|
+
markFailed: () => failedSkillGroups.push(ps.group)
|
|
17570
17833
|
})
|
|
17571
17834
|
)
|
|
17572
17835
|
];
|
|
@@ -17576,7 +17839,10 @@ async function uploadContextRecords(opts) {
|
|
|
17576
17839
|
uploadedFiles,
|
|
17577
17840
|
uploadedSkillGroups,
|
|
17578
17841
|
failedFiles,
|
|
17579
|
-
failedSkillGroups
|
|
17842
|
+
failedSkillGroups,
|
|
17843
|
+
dedupedCount,
|
|
17844
|
+
suppressedCount,
|
|
17845
|
+
breakerOpen: ledger.isBreakerOpen(Date.now())
|
|
17580
17846
|
};
|
|
17581
17847
|
}
|
|
17582
17848
|
async function runContextFileUploadPipeline(opts) {
|
|
@@ -17592,6 +17858,7 @@ async function runContextFileUploadPipeline(opts) {
|
|
|
17592
17858
|
branch,
|
|
17593
17859
|
commitSha,
|
|
17594
17860
|
clientVersion,
|
|
17861
|
+
ledger,
|
|
17595
17862
|
submitRecords,
|
|
17596
17863
|
onFileError,
|
|
17597
17864
|
onSkillError
|
|
@@ -17608,7 +17875,10 @@ async function runContextFileUploadPipeline(opts) {
|
|
|
17608
17875
|
uploadedFiles,
|
|
17609
17876
|
uploadedSkillGroups,
|
|
17610
17877
|
failedFiles,
|
|
17611
|
-
failedSkillGroups
|
|
17878
|
+
failedSkillGroups,
|
|
17879
|
+
dedupedCount,
|
|
17880
|
+
suppressedCount,
|
|
17881
|
+
breakerOpen
|
|
17612
17882
|
} = await uploadContextRecords({
|
|
17613
17883
|
processedFiles,
|
|
17614
17884
|
processedSkills,
|
|
@@ -17622,20 +17892,28 @@ async function runContextFileUploadPipeline(opts) {
|
|
|
17622
17892
|
branch,
|
|
17623
17893
|
commitSha,
|
|
17624
17894
|
clientVersion,
|
|
17895
|
+
ledger,
|
|
17625
17896
|
onFileError,
|
|
17626
17897
|
onSkillError
|
|
17627
17898
|
});
|
|
17628
17899
|
if (failedFiles.length > 0 || failedSkillGroups.length > 0) {
|
|
17629
17900
|
markContextUploadFailed(sessionId, failedFiles, failedSkillGroups);
|
|
17630
17901
|
}
|
|
17902
|
+
const summary = {
|
|
17903
|
+
dedupedCount,
|
|
17904
|
+
suppressedCount,
|
|
17905
|
+
failedCount: failedFiles.length + failedSkillGroups.length,
|
|
17906
|
+
breakerOpen
|
|
17907
|
+
};
|
|
17631
17908
|
if (records.length === 0) {
|
|
17632
|
-
return { fileCount: 0, skillCount: 0 };
|
|
17909
|
+
return { fileCount: 0, skillCount: 0, ...summary };
|
|
17633
17910
|
}
|
|
17634
17911
|
await submitRecords(records);
|
|
17635
17912
|
markContextFilesUploaded(sessionId, uploadedFiles, uploadedSkillGroups);
|
|
17636
17913
|
return {
|
|
17637
17914
|
fileCount: uploadedFiles.length,
|
|
17638
|
-
skillCount: uploadedSkillGroups.length
|
|
17915
|
+
skillCount: uploadedSkillGroups.length,
|
|
17916
|
+
...summary
|
|
17639
17917
|
};
|
|
17640
17918
|
}
|
|
17641
17919
|
|
|
@@ -17643,7 +17921,7 @@ async function runContextFileUploadPipeline(opts) {
|
|
|
17643
17921
|
init_client_generates();
|
|
17644
17922
|
|
|
17645
17923
|
// src/utils/shared-logger/create-logger.ts
|
|
17646
|
-
import
|
|
17924
|
+
import Configstore3 from "configstore";
|
|
17647
17925
|
import pino from "pino";
|
|
17648
17926
|
|
|
17649
17927
|
// src/utils/shared-logger/configstore-stream.ts
|
|
@@ -17870,7 +18148,7 @@ function createLogger(config2) {
|
|
|
17870
18148
|
} = config2;
|
|
17871
18149
|
let csStream = null;
|
|
17872
18150
|
if (enableConfigstore) {
|
|
17873
|
-
const store = new
|
|
18151
|
+
const store = new Configstore3(namespace, {});
|
|
17874
18152
|
csStream = createConfigstoreStream(store, {
|
|
17875
18153
|
buffered,
|
|
17876
18154
|
scopePath,
|
|
@@ -17979,7 +18257,7 @@ function createLogger(config2) {
|
|
|
17979
18257
|
|
|
17980
18258
|
// src/features/claude_code/hook_logger.ts
|
|
17981
18259
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
17982
|
-
var CLI_VERSION = true ? "1.4.
|
|
18260
|
+
var CLI_VERSION = true ? "1.4.46" : "unknown";
|
|
17983
18261
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
17984
18262
|
var claudeCodeVersion;
|
|
17985
18263
|
function buildDdTags() {
|
|
@@ -18550,7 +18828,7 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
|
|
|
18550
18828
|
errors: entries.length
|
|
18551
18829
|
};
|
|
18552
18830
|
}
|
|
18553
|
-
async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
|
|
18831
|
+
async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2, ledger) {
|
|
18554
18832
|
const { regularFiles, skillGroups } = await scanContextFiles(
|
|
18555
18833
|
cwd,
|
|
18556
18834
|
"claude-code",
|
|
@@ -18572,6 +18850,7 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
|
|
|
18572
18850
|
);
|
|
18573
18851
|
return;
|
|
18574
18852
|
}
|
|
18853
|
+
const uploadFailures = [];
|
|
18575
18854
|
const pipelineResult = await runContextFileUploadPipeline({
|
|
18576
18855
|
processedFiles,
|
|
18577
18856
|
processedSkills,
|
|
@@ -18580,20 +18859,15 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
|
|
|
18580
18859
|
url,
|
|
18581
18860
|
uploadFieldsJSON,
|
|
18582
18861
|
keyPrefix,
|
|
18862
|
+
ledger,
|
|
18583
18863
|
submitRecords: async (records) => {
|
|
18584
18864
|
const r = await prepareAndSendTracyRecords(gqlClient, records, cwd);
|
|
18585
18865
|
if (!r.ok) {
|
|
18586
18866
|
throw new Error(r.errors?.join(", ") ?? "batch upload failed");
|
|
18587
18867
|
}
|
|
18588
18868
|
},
|
|
18589
|
-
onFileError: (name, err) =>
|
|
18590
|
-
|
|
18591
|
-
"Failed to upload context file to S3"
|
|
18592
|
-
),
|
|
18593
|
-
onSkillError: (name, err) => log2.error(
|
|
18594
|
-
{ data: { sessionId, name, err } },
|
|
18595
|
-
"Failed to upload skill zip to S3"
|
|
18596
|
-
)
|
|
18869
|
+
onFileError: (name, err) => uploadFailures.push({ name, err }),
|
|
18870
|
+
onSkillError: (name, err) => uploadFailures.push({ name, err })
|
|
18597
18871
|
});
|
|
18598
18872
|
if (pipelineResult === null) {
|
|
18599
18873
|
log2.error(
|
|
@@ -18602,19 +18876,48 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
|
|
|
18602
18876
|
);
|
|
18603
18877
|
return;
|
|
18604
18878
|
}
|
|
18879
|
+
if (uploadFailures.length > 0) {
|
|
18880
|
+
const hasActionableS3Error = uploadFailures.some(
|
|
18881
|
+
(f) => isActionableS3Error(f.err)
|
|
18882
|
+
);
|
|
18883
|
+
const payload = {
|
|
18884
|
+
sessionId,
|
|
18885
|
+
failedCount: pipelineResult.failedCount,
|
|
18886
|
+
suppressedCount: pipelineResult.suppressedCount,
|
|
18887
|
+
dedupedCount: pipelineResult.dedupedCount,
|
|
18888
|
+
breakerOpen: pipelineResult.breakerOpen,
|
|
18889
|
+
errTypes: summarizeUploadErrTypes(uploadFailures.map((f) => f.err)),
|
|
18890
|
+
sample: uploadFailures.slice(0, 3).map((f) => f.name)
|
|
18891
|
+
};
|
|
18892
|
+
if (hasActionableS3Error) {
|
|
18893
|
+
log2.error({ data: payload }, "Context/skill S3 upload failures");
|
|
18894
|
+
} else {
|
|
18895
|
+
log2.warn({ data: payload }, "Context/skill upload failures (transient)");
|
|
18896
|
+
}
|
|
18897
|
+
}
|
|
18605
18898
|
if (pipelineResult.fileCount > 0 || pipelineResult.skillCount > 0) {
|
|
18606
18899
|
log2.info(
|
|
18607
18900
|
{
|
|
18608
18901
|
data: {
|
|
18609
18902
|
sessionId,
|
|
18610
18903
|
fileCount: pipelineResult.fileCount,
|
|
18611
|
-
skillCount: pipelineResult.skillCount
|
|
18904
|
+
skillCount: pipelineResult.skillCount,
|
|
18905
|
+
dedupedCount: pipelineResult.dedupedCount
|
|
18612
18906
|
}
|
|
18613
18907
|
},
|
|
18614
18908
|
"Uploaded context files and skills for session"
|
|
18615
18909
|
);
|
|
18616
18910
|
}
|
|
18617
18911
|
}
|
|
18912
|
+
function summarizeUploadErrTypes(errs) {
|
|
18913
|
+
const counts = {};
|
|
18914
|
+
for (const err of errs) {
|
|
18915
|
+
const e = err;
|
|
18916
|
+
const key = e?.type ?? e?.code ?? e?.name ?? "unknown";
|
|
18917
|
+
counts[key] = (counts[key] ?? 0) + 1;
|
|
18918
|
+
}
|
|
18919
|
+
return counts;
|
|
18920
|
+
}
|
|
18618
18921
|
|
|
18619
18922
|
// src/features/claude_code/install_hook.ts
|
|
18620
18923
|
import fs14 from "fs";
|
|
@@ -18953,9 +19256,18 @@ async function startDaemon() {
|
|
|
18953
19256
|
hookLog.info("Daemon starting");
|
|
18954
19257
|
pruneHookLogFile();
|
|
18955
19258
|
const pidFile = await acquirePidFile();
|
|
19259
|
+
const uploadLedger = new ConfigstoreUploadLedger();
|
|
19260
|
+
const inFlightContextUploads = /* @__PURE__ */ new Set();
|
|
18956
19261
|
async function gracefulExit(code, reason) {
|
|
18957
19262
|
hookLog.info({ data: { code } }, `Daemon exiting: ${reason}`);
|
|
18958
19263
|
pidFile.remove();
|
|
19264
|
+
if (inFlightContextUploads.size > 0) {
|
|
19265
|
+
await Promise.race([
|
|
19266
|
+
Promise.allSettled([...inFlightContextUploads]),
|
|
19267
|
+
sleep4(DAEMON_SHUTDOWN_DRAIN_MS)
|
|
19268
|
+
]);
|
|
19269
|
+
}
|
|
19270
|
+
uploadLedger.flush();
|
|
18959
19271
|
await flushDdLogs();
|
|
18960
19272
|
process.exit(code);
|
|
18961
19273
|
}
|
|
@@ -19045,14 +19357,20 @@ async function startDaemon() {
|
|
|
19045
19357
|
}
|
|
19046
19358
|
}
|
|
19047
19359
|
const now = Date.now();
|
|
19048
|
-
if (now - lastContextScanMs >= CONTEXT_SCAN_INTERVAL_MS) {
|
|
19360
|
+
if (!shuttingDown && now - lastContextScanMs >= CONTEXT_SCAN_INTERVAL_MS) {
|
|
19049
19361
|
lastContextScanMs = now;
|
|
19050
19362
|
for (const { sessionId, cwd } of sessionCwdCache.values()) {
|
|
19051
19363
|
const log2 = createScopedHookLog(cwd, { daemonMode: true });
|
|
19052
|
-
|
|
19053
|
-
|
|
19054
|
-
|
|
19364
|
+
const p = uploadContextFilesIfNeeded(
|
|
19365
|
+
sessionId,
|
|
19366
|
+
cwd,
|
|
19367
|
+
gqlClient,
|
|
19368
|
+
log2,
|
|
19369
|
+
uploadLedger
|
|
19370
|
+
).catch((err) => log2.warn({ err }, "Context file scan failed")).finally(() => inFlightContextUploads.delete(p));
|
|
19371
|
+
inFlightContextUploads.add(p);
|
|
19055
19372
|
}
|
|
19373
|
+
uploadLedger.flush();
|
|
19056
19374
|
}
|
|
19057
19375
|
if (cleanupConfigDir) {
|
|
19058
19376
|
await cleanupStaleSessions(cleanupConfigDir);
|
|
@@ -19222,7 +19540,7 @@ async function tryAutoUpgradeHooks() {
|
|
|
19222
19540
|
var HOOK_LOG_MAX_SCOPE_KEYS = 20;
|
|
19223
19541
|
var HOOK_LOG_MAX_ENTRIES_PER_KEY = 200;
|
|
19224
19542
|
function pruneHookLogFile() {
|
|
19225
|
-
const logFilePath = new
|
|
19543
|
+
const logFilePath = new Configstore4("mobbdev-claude-code-hook-logs").path;
|
|
19226
19544
|
try {
|
|
19227
19545
|
const raw = readFileSync(logFilePath, "utf-8");
|
|
19228
19546
|
const data = JSON.parse(raw);
|
|
@@ -19354,7 +19672,7 @@ import {
|
|
|
19354
19672
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
19355
19673
|
|
|
19356
19674
|
// src/mcp/Logger.ts
|
|
19357
|
-
import
|
|
19675
|
+
import Configstore5 from "configstore";
|
|
19358
19676
|
|
|
19359
19677
|
// src/mcp/services/WorkspaceService.ts
|
|
19360
19678
|
var WorkspaceService = class {
|
|
@@ -19440,7 +19758,7 @@ var Logger = class {
|
|
|
19440
19758
|
__publicField(this, "lastKnownPath", null);
|
|
19441
19759
|
this.host = WorkspaceService.getHost();
|
|
19442
19760
|
this.unknownPathSuffix = Math.floor(1e3 + Math.random() * 9e3).toString();
|
|
19443
|
-
this.mobbConfigStore = new
|
|
19761
|
+
this.mobbConfigStore = new Configstore5("mobb-logs", {});
|
|
19444
19762
|
this.mobbConfigStore.set("version", packageJson.version);
|
|
19445
19763
|
}
|
|
19446
19764
|
/**
|