mobbdev 1.4.26 → 1.4.28
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.
|
@@ -221,13 +221,18 @@ type RepoState = {
|
|
|
221
221
|
* `GitService.getCurrentRepoState()` so detached-HEAD handling and SHA
|
|
222
222
|
* normalization stay in lockstep across the two clients.
|
|
223
223
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
224
|
+
* The repository URL is the canonical form (parseScmURL().canonicalUrl) for any
|
|
225
|
+
* SCM host, including self-hosted / enterprise remotes (scmType "Unknown"); only
|
|
226
|
+
* an absent or genuinely unparseable remote resolves to `repositoryUrl: null`.
|
|
227
|
+
*
|
|
228
|
+
* Never throws — a non-existent dir, missing git binary, or absent/unparseable
|
|
229
|
+
* remote resolves to null so the daemon hot path can rely on a value.
|
|
226
230
|
*/
|
|
227
231
|
declare function readRepoState(workingDir?: string): Promise<RepoState>;
|
|
228
232
|
/**
|
|
229
|
-
* Gets the
|
|
230
|
-
*
|
|
233
|
+
* Gets the canonical remote repository URL for the working directory's repo, for
|
|
234
|
+
* any SCM host (GitHub, GitLab, Azure DevOps, Bitbucket, and self-hosted /
|
|
235
|
+
* enterprise). Returns null when there is no remote or it can't be parsed.
|
|
231
236
|
*/
|
|
232
237
|
declare function getRepositoryUrl(workingDir?: string): Promise<string | null>;
|
|
233
238
|
/**
|
|
@@ -357,6 +357,7 @@ var init_client_generates = __esm({
|
|
|
357
357
|
IssueType_Enum2["TrustBoundaryViolation"] = "TRUST_BOUNDARY_VIOLATION";
|
|
358
358
|
IssueType_Enum2["TypeConfusion"] = "TYPE_CONFUSION";
|
|
359
359
|
IssueType_Enum2["UncheckedLoopCondition"] = "UNCHECKED_LOOP_CONDITION";
|
|
360
|
+
IssueType_Enum2["UncheckedReturnValue"] = "UNCHECKED_RETURN_VALUE";
|
|
360
361
|
IssueType_Enum2["UnencryptedAwsSqsQueue"] = "UNENCRYPTED_AWS_SQS_QUEUE";
|
|
361
362
|
IssueType_Enum2["UnnecessaryImports"] = "UNNECESSARY_IMPORTS";
|
|
362
363
|
IssueType_Enum2["UnsafeDeserialization"] = "UNSAFE_DESERIALIZATION";
|
|
@@ -1930,7 +1931,8 @@ var init_getIssueType = __esm({
|
|
|
1930
1931
|
["UNENCRYPTED_AWS_SQS_QUEUE" /* UnencryptedAwsSqsQueue */]: "AWS SQS Queue Unencrypted",
|
|
1931
1932
|
["INSECURE_DESERIALIZATION" /* InsecureDeserialization */]: "Insecure Deserialization",
|
|
1932
1933
|
["AWS_DYNAMODB_POINT_IN_TIME_RECOVERY_DISABLED" /* AwsDynamodbPointInTimeRecoveryDisabled */]: "AWS DynamoDB Point-in-Time Recovery Disabled",
|
|
1933
|
-
["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: "JWT Decoded Without Signature Verification"
|
|
1934
|
+
["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: "JWT Decoded Without Signature Verification",
|
|
1935
|
+
["UNCHECKED_RETURN_VALUE" /* UncheckedReturnValue */]: "Unchecked Return Value"
|
|
1934
1936
|
};
|
|
1935
1937
|
issueTypeZ = z5.nativeEnum(IssueType_Enum);
|
|
1936
1938
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -5188,7 +5190,8 @@ var fixDetailsData = {
|
|
|
5188
5190
|
["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: {
|
|
5189
5191
|
issueDescription: "Decoding a JWT with `JWT.decode()` only base64-decodes the token without checking its signature, so an attacker can forge a token with arbitrary claims (identity, roles, expiration) and have it trusted. CWE-345, OWASP A08:2021 Software and Data Integrity Failures.",
|
|
5190
5192
|
fixInstructions: "Verify the signature before trusting any claims: build a verifier with the expected algorithm and secret/key (e.g. `JWT.require(Algorithm.HMAC256(secret)).build().verify(token)`) instead of calling `JWT.decode(token)`. After merging, confirm the verifier is configured with the same algorithm and secret/key used to sign your tokens \u2014 an incorrect or placeholder secret will make verification throw `JWTVerificationException` at runtime and reject legitimate tokens."
|
|
5191
|
-
}
|
|
5193
|
+
},
|
|
5194
|
+
["UNCHECKED_RETURN_VALUE" /* UncheckedReturnValue */]: void 0
|
|
5192
5195
|
};
|
|
5193
5196
|
|
|
5194
5197
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -7826,6 +7829,19 @@ import Debug8 from "debug";
|
|
|
7826
7829
|
import Debug7 from "debug";
|
|
7827
7830
|
import fetch3, { File, fileFrom, FormData } from "node-fetch";
|
|
7828
7831
|
var debug8 = Debug7("mobbdev:upload-file");
|
|
7832
|
+
var MIN_UPLOAD_TIMEOUT_MS = 1e4;
|
|
7833
|
+
var MAX_UPLOAD_TIMEOUT_MS = 6e4;
|
|
7834
|
+
var SLOW_LINK_BYTES_PER_MS = 512;
|
|
7835
|
+
function computeUploadTimeoutMs(file) {
|
|
7836
|
+
if (typeof file === "string") {
|
|
7837
|
+
return MAX_UPLOAD_TIMEOUT_MS;
|
|
7838
|
+
}
|
|
7839
|
+
const sizeAllowanceMs = Math.ceil(file.byteLength / SLOW_LINK_BYTES_PER_MS);
|
|
7840
|
+
return Math.min(
|
|
7841
|
+
MAX_UPLOAD_TIMEOUT_MS,
|
|
7842
|
+
MIN_UPLOAD_TIMEOUT_MS + sizeAllowanceMs
|
|
7843
|
+
);
|
|
7844
|
+
}
|
|
7829
7845
|
var S3UploadError = class extends Error {
|
|
7830
7846
|
constructor(status, s3Code, s3Message) {
|
|
7831
7847
|
super(`Failed to upload the file: ${status}`);
|
|
@@ -7884,11 +7900,12 @@ async function uploadFile({
|
|
|
7884
7900
|
);
|
|
7885
7901
|
}
|
|
7886
7902
|
const agent = getProxyAgent(url);
|
|
7903
|
+
const effectiveSignal = signal ?? AbortSignal.timeout(computeUploadTimeoutMs(file));
|
|
7887
7904
|
const response = await fetch3(url, {
|
|
7888
7905
|
method: "POST",
|
|
7889
7906
|
body: form,
|
|
7890
7907
|
agent,
|
|
7891
|
-
signal
|
|
7908
|
+
signal: effectiveSignal
|
|
7892
7909
|
});
|
|
7893
7910
|
if (!response.ok) {
|
|
7894
7911
|
let bodyText = "";
|
|
@@ -8465,8 +8482,7 @@ async function readRepoState(workingDir) {
|
|
|
8465
8482
|
const repoStatePromise = gitService.getCurrentRepoState().catch(() => ({ branch: null, commitSha: null }));
|
|
8466
8483
|
const repositoryUrlPromise = gitService.getRemoteUrl().then((url) => {
|
|
8467
8484
|
if (!url) return null;
|
|
8468
|
-
|
|
8469
|
-
return parsed?.scmType && parsed.scmType !== "Unknown" ? url : null;
|
|
8485
|
+
return parseScmURL(url)?.canonicalUrl ?? null;
|
|
8470
8486
|
}).catch(() => null);
|
|
8471
8487
|
const [{ branch, commitSha }, repositoryUrl] = await Promise.all([
|
|
8472
8488
|
repoStatePromise,
|
package/dist/index.mjs
CHANGED
|
@@ -357,6 +357,7 @@ var init_client_generates = __esm({
|
|
|
357
357
|
IssueType_Enum2["TrustBoundaryViolation"] = "TRUST_BOUNDARY_VIOLATION";
|
|
358
358
|
IssueType_Enum2["TypeConfusion"] = "TYPE_CONFUSION";
|
|
359
359
|
IssueType_Enum2["UncheckedLoopCondition"] = "UNCHECKED_LOOP_CONDITION";
|
|
360
|
+
IssueType_Enum2["UncheckedReturnValue"] = "UNCHECKED_RETURN_VALUE";
|
|
360
361
|
IssueType_Enum2["UnencryptedAwsSqsQueue"] = "UNENCRYPTED_AWS_SQS_QUEUE";
|
|
361
362
|
IssueType_Enum2["UnnecessaryImports"] = "UNNECESSARY_IMPORTS";
|
|
362
363
|
IssueType_Enum2["UnsafeDeserialization"] = "UNSAFE_DESERIALIZATION";
|
|
@@ -1595,7 +1596,8 @@ var init_getIssueType = __esm({
|
|
|
1595
1596
|
["UNENCRYPTED_AWS_SQS_QUEUE" /* UnencryptedAwsSqsQueue */]: "AWS SQS Queue Unencrypted",
|
|
1596
1597
|
["INSECURE_DESERIALIZATION" /* InsecureDeserialization */]: "Insecure Deserialization",
|
|
1597
1598
|
["AWS_DYNAMODB_POINT_IN_TIME_RECOVERY_DISABLED" /* AwsDynamodbPointInTimeRecoveryDisabled */]: "AWS DynamoDB Point-in-Time Recovery Disabled",
|
|
1598
|
-
["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: "JWT Decoded Without Signature Verification"
|
|
1599
|
+
["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: "JWT Decoded Without Signature Verification",
|
|
1600
|
+
["UNCHECKED_RETURN_VALUE" /* UncheckedReturnValue */]: "Unchecked Return Value"
|
|
1599
1601
|
};
|
|
1600
1602
|
issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
1601
1603
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -4893,7 +4895,8 @@ var fixDetailsData = {
|
|
|
4893
4895
|
["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: {
|
|
4894
4896
|
issueDescription: "Decoding a JWT with `JWT.decode()` only base64-decodes the token without checking its signature, so an attacker can forge a token with arbitrary claims (identity, roles, expiration) and have it trusted. CWE-345, OWASP A08:2021 Software and Data Integrity Failures.",
|
|
4895
4897
|
fixInstructions: "Verify the signature before trusting any claims: build a verifier with the expected algorithm and secret/key (e.g. `JWT.require(Algorithm.HMAC256(secret)).build().verify(token)`) instead of calling `JWT.decode(token)`. After merging, confirm the verifier is configured with the same algorithm and secret/key used to sign your tokens \u2014 an incorrect or placeholder secret will make verification throw `JWTVerificationException` at runtime and reject legitimate tokens."
|
|
4896
|
-
}
|
|
4898
|
+
},
|
|
4899
|
+
["UNCHECKED_RETURN_VALUE" /* UncheckedReturnValue */]: void 0
|
|
4897
4900
|
};
|
|
4898
4901
|
|
|
4899
4902
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -14206,6 +14209,41 @@ init_urlParser2();
|
|
|
14206
14209
|
import Debug8 from "debug";
|
|
14207
14210
|
import fetch3, { File, fileFrom, FormData } from "node-fetch";
|
|
14208
14211
|
var debug9 = Debug8("mobbdev:upload-file");
|
|
14212
|
+
var MIN_UPLOAD_TIMEOUT_MS = 1e4;
|
|
14213
|
+
var MAX_UPLOAD_TIMEOUT_MS = 6e4;
|
|
14214
|
+
var SLOW_LINK_BYTES_PER_MS = 512;
|
|
14215
|
+
function computeUploadTimeoutMs(file) {
|
|
14216
|
+
if (typeof file === "string") {
|
|
14217
|
+
return MAX_UPLOAD_TIMEOUT_MS;
|
|
14218
|
+
}
|
|
14219
|
+
const sizeAllowanceMs = Math.ceil(file.byteLength / SLOW_LINK_BYTES_PER_MS);
|
|
14220
|
+
return Math.min(
|
|
14221
|
+
MAX_UPLOAD_TIMEOUT_MS,
|
|
14222
|
+
MIN_UPLOAD_TIMEOUT_MS + sizeAllowanceMs
|
|
14223
|
+
);
|
|
14224
|
+
}
|
|
14225
|
+
var TRANSIENT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
14226
|
+
"ECONNRESET",
|
|
14227
|
+
"ETIMEDOUT",
|
|
14228
|
+
"ECONNREFUSED",
|
|
14229
|
+
"EPIPE",
|
|
14230
|
+
"EAI_AGAIN",
|
|
14231
|
+
"ENETUNREACH",
|
|
14232
|
+
"ENOTFOUND"
|
|
14233
|
+
]);
|
|
14234
|
+
function isTransientUploadError(err) {
|
|
14235
|
+
if (err instanceof S3UploadError) {
|
|
14236
|
+
return false;
|
|
14237
|
+
}
|
|
14238
|
+
if (typeof err !== "object" || err === null) {
|
|
14239
|
+
return false;
|
|
14240
|
+
}
|
|
14241
|
+
const e = err;
|
|
14242
|
+
if (e.code !== void 0 && TRANSIENT_ERROR_CODES.has(e.code)) {
|
|
14243
|
+
return true;
|
|
14244
|
+
}
|
|
14245
|
+
return e.type === "system" || e.name === "AbortError" || e.name === "TimeoutError";
|
|
14246
|
+
}
|
|
14209
14247
|
var S3UploadError = class extends Error {
|
|
14210
14248
|
constructor(status, s3Code, s3Message) {
|
|
14211
14249
|
super(`Failed to upload the file: ${status}`);
|
|
@@ -14264,11 +14302,12 @@ async function uploadFile({
|
|
|
14264
14302
|
);
|
|
14265
14303
|
}
|
|
14266
14304
|
const agent = getProxyAgent(url);
|
|
14305
|
+
const effectiveSignal = signal ?? AbortSignal.timeout(computeUploadTimeoutMs(file));
|
|
14267
14306
|
const response = await fetch3(url, {
|
|
14268
14307
|
method: "POST",
|
|
14269
14308
|
body: form,
|
|
14270
14309
|
agent,
|
|
14271
|
-
signal
|
|
14310
|
+
signal: effectiveSignal
|
|
14272
14311
|
});
|
|
14273
14312
|
if (!response.ok) {
|
|
14274
14313
|
let bodyText = "";
|
|
@@ -14650,8 +14689,7 @@ async function readRepoState(workingDir) {
|
|
|
14650
14689
|
const repoStatePromise = gitService.getCurrentRepoState().catch(() => ({ branch: null, commitSha: null }));
|
|
14651
14690
|
const repositoryUrlPromise = gitService.getRemoteUrl().then((url) => {
|
|
14652
14691
|
if (!url) return null;
|
|
14653
|
-
|
|
14654
|
-
return parsed?.scmType && parsed.scmType !== "Unknown" ? url : null;
|
|
14692
|
+
return parseScmURL(url)?.canonicalUrl ?? null;
|
|
14655
14693
|
}).catch(() => null);
|
|
14656
14694
|
const [{ branch, commitSha }, repositoryUrl] = await Promise.all([
|
|
14657
14695
|
repoStatePromise,
|
|
@@ -17862,7 +17900,7 @@ import { spawn } from "child_process";
|
|
|
17862
17900
|
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
17863
17901
|
import * as os8 from "os";
|
|
17864
17902
|
import path24 from "path";
|
|
17865
|
-
import { setTimeout as
|
|
17903
|
+
import { setTimeout as sleep4 } from "timers/promises";
|
|
17866
17904
|
import Configstore3 from "configstore";
|
|
17867
17905
|
|
|
17868
17906
|
// src/features/analysis/skill_quarantine/runQuarantineCheck.ts
|
|
@@ -18141,23 +18179,61 @@ var SCAN_PATHS = {
|
|
|
18141
18179
|
// src/features/analysis/context_file_scanner.ts
|
|
18142
18180
|
var MAX_CONTEXT_FILE_SIZE = 20 * 1024 * 1024;
|
|
18143
18181
|
var SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
18182
|
+
var FAILURE_BACKOFF_BASE_MS = 60 * 1e3;
|
|
18183
|
+
var FAILURE_BACKOFF_MAX_MS = 60 * 60 * 1e3;
|
|
18144
18184
|
var sessionMtimes = /* @__PURE__ */ new Map();
|
|
18145
18185
|
function markContextFilesUploaded(sessionId, files, skills) {
|
|
18146
|
-
|
|
18147
|
-
if (!entry) {
|
|
18148
|
-
entry = { files: /* @__PURE__ */ new Map(), skills: /* @__PURE__ */ new Map(), lastUpdatedAt: Date.now() };
|
|
18149
|
-
sessionMtimes.set(sessionId, entry);
|
|
18150
|
-
}
|
|
18186
|
+
const entry = getOrCreateSessionEntry(sessionId);
|
|
18151
18187
|
for (const f of files) {
|
|
18152
18188
|
entry.files.set(f.path, f.mtimeMs);
|
|
18189
|
+
entry.failures.delete(f.path);
|
|
18153
18190
|
}
|
|
18154
18191
|
if (skills) {
|
|
18155
18192
|
for (const sg of skills) {
|
|
18156
18193
|
entry.skills.set(sg.sessionKey, sg.maxMtimeMs);
|
|
18194
|
+
entry.failures.delete(sg.sessionKey);
|
|
18157
18195
|
}
|
|
18158
18196
|
}
|
|
18159
18197
|
entry.lastUpdatedAt = Date.now();
|
|
18160
18198
|
}
|
|
18199
|
+
function markContextUploadFailed(sessionId, files, skills) {
|
|
18200
|
+
const entry = getOrCreateSessionEntry(sessionId);
|
|
18201
|
+
const now = Date.now();
|
|
18202
|
+
const recordFailure = (key) => {
|
|
18203
|
+
const attempts = (entry.failures.get(key)?.attempts ?? 0) + 1;
|
|
18204
|
+
const delay3 = Math.min(
|
|
18205
|
+
FAILURE_BACKOFF_BASE_MS * 2 ** (attempts - 1),
|
|
18206
|
+
FAILURE_BACKOFF_MAX_MS
|
|
18207
|
+
);
|
|
18208
|
+
entry.failures.set(key, { attempts, nextRetryAt: now + delay3 });
|
|
18209
|
+
};
|
|
18210
|
+
for (const f of files) {
|
|
18211
|
+
recordFailure(f.path);
|
|
18212
|
+
}
|
|
18213
|
+
if (skills) {
|
|
18214
|
+
for (const sg of skills) {
|
|
18215
|
+
recordFailure(sg.sessionKey);
|
|
18216
|
+
}
|
|
18217
|
+
}
|
|
18218
|
+
entry.lastUpdatedAt = now;
|
|
18219
|
+
}
|
|
18220
|
+
function getOrCreateSessionEntry(sessionId) {
|
|
18221
|
+
let entry = sessionMtimes.get(sessionId);
|
|
18222
|
+
if (!entry) {
|
|
18223
|
+
entry = {
|
|
18224
|
+
files: /* @__PURE__ */ new Map(),
|
|
18225
|
+
skills: /* @__PURE__ */ new Map(),
|
|
18226
|
+
failures: /* @__PURE__ */ new Map(),
|
|
18227
|
+
lastUpdatedAt: Date.now()
|
|
18228
|
+
};
|
|
18229
|
+
sessionMtimes.set(sessionId, entry);
|
|
18230
|
+
}
|
|
18231
|
+
return entry;
|
|
18232
|
+
}
|
|
18233
|
+
function isInUploadBackoff(entry, key, now) {
|
|
18234
|
+
const failure = entry.failures.get(key);
|
|
18235
|
+
return failure !== void 0 && failure.nextRetryAt > now;
|
|
18236
|
+
}
|
|
18161
18237
|
var COPILOT_CUSTOM_LOCATION_SETTINGS = [
|
|
18162
18238
|
{
|
|
18163
18239
|
key: "chat.agentSkillsLocations",
|
|
@@ -18517,6 +18593,9 @@ async function scanContextFiles(workspaceRoot, platform2, sessionId) {
|
|
|
18517
18593
|
if (prevMtime !== void 0 && fileStat.mtimeMs <= prevMtime) {
|
|
18518
18594
|
continue;
|
|
18519
18595
|
}
|
|
18596
|
+
if (sessionEntry && isInUploadBackoff(sessionEntry, filePath, now)) {
|
|
18597
|
+
continue;
|
|
18598
|
+
}
|
|
18520
18599
|
allFiles.push(fileEntry);
|
|
18521
18600
|
}
|
|
18522
18601
|
} catch {
|
|
@@ -18532,6 +18611,9 @@ async function scanContextFiles(workspaceRoot, platform2, sessionId) {
|
|
|
18532
18611
|
if (prevMtime !== void 0 && group.maxMtimeMs <= prevMtime) {
|
|
18533
18612
|
continue;
|
|
18534
18613
|
}
|
|
18614
|
+
if (isInUploadBackoff(sessionEntry, group.sessionKey, now)) {
|
|
18615
|
+
continue;
|
|
18616
|
+
}
|
|
18535
18617
|
}
|
|
18536
18618
|
allSkillGroups.push(group);
|
|
18537
18619
|
}
|
|
@@ -19120,6 +19202,7 @@ var lastRunAt = /* @__PURE__ */ new Map();
|
|
|
19120
19202
|
var lastDirsMtimeMs = /* @__PURE__ */ new Map();
|
|
19121
19203
|
var seenSkillMd5s = /* @__PURE__ */ new Set();
|
|
19122
19204
|
var killSwitchLogged = false;
|
|
19205
|
+
var optInDisabledLogged = false;
|
|
19123
19206
|
async function runQuarantineCheckIfNeeded(opts) {
|
|
19124
19207
|
const { sessionId, cwd, gqlClient, log: log2 } = opts;
|
|
19125
19208
|
if (process.env[KILL_SWITCH_ENV] === "1") {
|
|
@@ -19146,14 +19229,14 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
19146
19229
|
return;
|
|
19147
19230
|
}
|
|
19148
19231
|
lastDirsMtimeMs.set(sessionId, dirsMtime);
|
|
19149
|
-
log2.
|
|
19232
|
+
log2.debug(
|
|
19150
19233
|
{ sessionId, metric: Metric.CHECK_TRIGGERED, hasNewSkills },
|
|
19151
19234
|
"skill_quarantine: check start"
|
|
19152
19235
|
);
|
|
19153
19236
|
const t0 = Date.now();
|
|
19154
19237
|
try {
|
|
19155
19238
|
await reconcileAndSweep(log2);
|
|
19156
|
-
log2.
|
|
19239
|
+
log2.debug(
|
|
19157
19240
|
{ sessionId, count: installed.length, metric: Metric.SKILLS_CHECKED },
|
|
19158
19241
|
"skill_quarantine: skills enumerated"
|
|
19159
19242
|
);
|
|
@@ -19173,10 +19256,13 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
19173
19256
|
log2
|
|
19174
19257
|
);
|
|
19175
19258
|
if (!quarantineEnabled) {
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19259
|
+
if (!optInDisabledLogged) {
|
|
19260
|
+
log2.info(
|
|
19261
|
+
{ sessionId, metric: Metric.CHECK_DISABLED_ORG },
|
|
19262
|
+
"skill_quarantine: opt-in not enabled for any org of caller; skipping enforcement"
|
|
19263
|
+
);
|
|
19264
|
+
optInDisabledLogged = true;
|
|
19265
|
+
}
|
|
19180
19266
|
return;
|
|
19181
19267
|
}
|
|
19182
19268
|
for (const skill of installed) {
|
|
@@ -19201,7 +19287,7 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
19201
19287
|
}
|
|
19202
19288
|
}
|
|
19203
19289
|
} finally {
|
|
19204
|
-
log2.
|
|
19290
|
+
log2.debug(
|
|
19205
19291
|
{
|
|
19206
19292
|
sessionId,
|
|
19207
19293
|
duration_ms: Date.now() - t0,
|
|
@@ -19313,9 +19399,27 @@ import path21 from "path";
|
|
|
19313
19399
|
import { promisify } from "util";
|
|
19314
19400
|
|
|
19315
19401
|
// src/features/analysis/context_file_uploader.ts
|
|
19402
|
+
import { setTimeout as sleep3 } from "timers/promises";
|
|
19316
19403
|
import pLimit7 from "p-limit";
|
|
19317
19404
|
init_client_generates();
|
|
19318
19405
|
var UPLOAD_CONCURRENCY = 5;
|
|
19406
|
+
var UPLOAD_MAX_ATTEMPTS = 2;
|
|
19407
|
+
var UPLOAD_RETRY_BASE_MS = 250;
|
|
19408
|
+
async function uploadWithRetry(args) {
|
|
19409
|
+
for (let attempt = 1; attempt <= UPLOAD_MAX_ATTEMPTS; attempt++) {
|
|
19410
|
+
try {
|
|
19411
|
+
await uploadFile(args);
|
|
19412
|
+
return;
|
|
19413
|
+
} catch (err) {
|
|
19414
|
+
const isLastAttempt = attempt === UPLOAD_MAX_ATTEMPTS;
|
|
19415
|
+
if (isLastAttempt || !isTransientUploadError(err)) {
|
|
19416
|
+
throw err;
|
|
19417
|
+
}
|
|
19418
|
+
const backoff = UPLOAD_RETRY_BASE_MS * 2 ** (attempt - 1) * (1 + Math.random());
|
|
19419
|
+
await sleep3(backoff);
|
|
19420
|
+
}
|
|
19421
|
+
}
|
|
19422
|
+
}
|
|
19319
19423
|
async function uploadContextRecords(opts) {
|
|
19320
19424
|
const {
|
|
19321
19425
|
processedFiles,
|
|
@@ -19336,6 +19440,8 @@ async function uploadContextRecords(opts) {
|
|
|
19336
19440
|
const records = [];
|
|
19337
19441
|
const uploadedFiles = [];
|
|
19338
19442
|
const uploadedSkillGroups = [];
|
|
19443
|
+
const failedFiles = [];
|
|
19444
|
+
const failedSkillGroups = [];
|
|
19339
19445
|
const limit = pLimit7(UPLOAD_CONCURRENCY);
|
|
19340
19446
|
const extraFields = {
|
|
19341
19447
|
...repositoryUrl !== void 0 && { repositoryUrl },
|
|
@@ -19348,13 +19454,14 @@ async function uploadContextRecords(opts) {
|
|
|
19348
19454
|
(pf) => limit(async () => {
|
|
19349
19455
|
const s3Key = `${keyPrefix}ctx-${pf.md5}.bin`;
|
|
19350
19456
|
try {
|
|
19351
|
-
await
|
|
19457
|
+
await uploadWithRetry({
|
|
19352
19458
|
file: Buffer.from(pf.sanitizedContent, "utf-8"),
|
|
19353
19459
|
url,
|
|
19354
19460
|
uploadKey: s3Key,
|
|
19355
19461
|
uploadFields
|
|
19356
19462
|
});
|
|
19357
19463
|
} catch (err) {
|
|
19464
|
+
failedFiles.push(pf.entry);
|
|
19358
19465
|
onFileError?.(pf.entry.name, err);
|
|
19359
19466
|
return;
|
|
19360
19467
|
}
|
|
@@ -19382,13 +19489,14 @@ async function uploadContextRecords(opts) {
|
|
|
19382
19489
|
(ps) => limit(async () => {
|
|
19383
19490
|
const s3Key = `${keyPrefix}skill-${ps.md5}.zip`;
|
|
19384
19491
|
try {
|
|
19385
|
-
await
|
|
19492
|
+
await uploadWithRetry({
|
|
19386
19493
|
file: ps.zipBuffer,
|
|
19387
19494
|
url,
|
|
19388
19495
|
uploadKey: s3Key,
|
|
19389
19496
|
uploadFields
|
|
19390
19497
|
});
|
|
19391
19498
|
} catch (err) {
|
|
19499
|
+
failedSkillGroups.push(ps.group);
|
|
19392
19500
|
onSkillError?.(ps.group.name, err);
|
|
19393
19501
|
return;
|
|
19394
19502
|
}
|
|
@@ -19414,7 +19522,13 @@ async function uploadContextRecords(opts) {
|
|
|
19414
19522
|
)
|
|
19415
19523
|
];
|
|
19416
19524
|
await Promise.allSettled(tasks);
|
|
19417
|
-
return {
|
|
19525
|
+
return {
|
|
19526
|
+
records,
|
|
19527
|
+
uploadedFiles,
|
|
19528
|
+
uploadedSkillGroups,
|
|
19529
|
+
failedFiles,
|
|
19530
|
+
failedSkillGroups
|
|
19531
|
+
};
|
|
19418
19532
|
}
|
|
19419
19533
|
async function runContextFileUploadPipeline(opts) {
|
|
19420
19534
|
const {
|
|
@@ -19440,7 +19554,13 @@ async function runContextFileUploadPipeline(opts) {
|
|
|
19440
19554
|
return null;
|
|
19441
19555
|
}
|
|
19442
19556
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19443
|
-
const {
|
|
19557
|
+
const {
|
|
19558
|
+
records,
|
|
19559
|
+
uploadedFiles,
|
|
19560
|
+
uploadedSkillGroups,
|
|
19561
|
+
failedFiles,
|
|
19562
|
+
failedSkillGroups
|
|
19563
|
+
} = await uploadContextRecords({
|
|
19444
19564
|
processedFiles,
|
|
19445
19565
|
processedSkills,
|
|
19446
19566
|
keyPrefix,
|
|
@@ -19456,6 +19576,9 @@ async function runContextFileUploadPipeline(opts) {
|
|
|
19456
19576
|
onFileError,
|
|
19457
19577
|
onSkillError
|
|
19458
19578
|
});
|
|
19579
|
+
if (failedFiles.length > 0 || failedSkillGroups.length > 0) {
|
|
19580
|
+
markContextUploadFailed(sessionId, failedFiles, failedSkillGroups);
|
|
19581
|
+
}
|
|
19459
19582
|
if (records.length === 0) {
|
|
19460
19583
|
return { fileCount: 0, skillCount: 0 };
|
|
19461
19584
|
}
|
|
@@ -19807,7 +19930,7 @@ function createLogger(config2) {
|
|
|
19807
19930
|
|
|
19808
19931
|
// src/features/claude_code/hook_logger.ts
|
|
19809
19932
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
19810
|
-
var CLI_VERSION = true ? "1.4.
|
|
19933
|
+
var CLI_VERSION = true ? "1.4.28" : "unknown";
|
|
19811
19934
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
19812
19935
|
var claudeCodeVersion;
|
|
19813
19936
|
function buildDdTags() {
|
|
@@ -19986,7 +20109,7 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
19986
20109
|
const stat5 = await fh.stat();
|
|
19987
20110
|
fileSize = stat5.size;
|
|
19988
20111
|
if (cursor.byteOffset >= stat5.size) {
|
|
19989
|
-
hookLog.
|
|
20112
|
+
hookLog.debug({ data: { sessionId } }, "No new data in transcript file");
|
|
19990
20113
|
return {
|
|
19991
20114
|
entries: [],
|
|
19992
20115
|
endByteOffset: fileSize,
|
|
@@ -20223,12 +20346,12 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
|
|
|
20223
20346
|
);
|
|
20224
20347
|
const cursorKey = getCursorKey(resolvedTranscriptPath);
|
|
20225
20348
|
if (rawEntries.length === 0) {
|
|
20226
|
-
log2.
|
|
20349
|
+
log2.debug("No new entries to upload");
|
|
20227
20350
|
return { entriesUploaded: 0, entriesSkipped: 0, errors: 0 };
|
|
20228
20351
|
}
|
|
20229
20352
|
const { filtered: entries, filteredOut } = filterEntries(rawEntries);
|
|
20230
20353
|
if (filteredOut > 0) {
|
|
20231
|
-
log2.
|
|
20354
|
+
log2.debug(
|
|
20232
20355
|
{ data: { filteredOut, remaining: entries.length } },
|
|
20233
20356
|
"Filtered out noise entries"
|
|
20234
20357
|
);
|
|
@@ -20911,7 +21034,7 @@ async function startDaemon() {
|
|
|
20911
21034
|
},
|
|
20912
21035
|
"daemon poll cycle"
|
|
20913
21036
|
);
|
|
20914
|
-
await
|
|
21037
|
+
await sleep4(DAEMON_POLL_INTERVAL_MS);
|
|
20915
21038
|
}
|
|
20916
21039
|
}
|
|
20917
21040
|
async function acquirePidFile() {
|
|
@@ -28770,7 +28893,7 @@ var _FetchAvailableFixesService = class _FetchAvailableFixesService {
|
|
|
28770
28893
|
interactiveFixes: fixReport.interactiveFixes ?? [],
|
|
28771
28894
|
repositoryPath
|
|
28772
28895
|
});
|
|
28773
|
-
this.currentOffset = effectiveOffset + (fixReport.fixes?.length || 0);
|
|
28896
|
+
this.currentOffset = effectiveOffset + (fixReport.fixes?.length || 0) + (fixReport.interactiveFixes?.length || 0);
|
|
28774
28897
|
return prompt;
|
|
28775
28898
|
} catch (error) {
|
|
28776
28899
|
logError("Failed to check for available fixes", {
|
|
@@ -29128,7 +29251,7 @@ var _ScanAndFixVulnerabilitiesService = class _ScanAndFixVulnerabilitiesService
|
|
|
29128
29251
|
interactiveFixes: fixes.interactiveFixes,
|
|
29129
29252
|
repositoryPath
|
|
29130
29253
|
});
|
|
29131
|
-
this.currentOffset = effectiveOffset + (fixes.fixes?.length || 0);
|
|
29254
|
+
this.currentOffset = effectiveOffset + (fixes.fixes?.length || 0) + (fixes.interactiveFixes?.length || 0);
|
|
29132
29255
|
return prompt;
|
|
29133
29256
|
} catch (error) {
|
|
29134
29257
|
const message = error.message;
|