mobbdev 1.2.56 → 1.2.57

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.
@@ -1445,8 +1445,16 @@ var init_analysis = __esm({
1445
1445
  originalUrl: z3.string(),
1446
1446
  reference: z3.string(),
1447
1447
  commitSha: z3.string(),
1448
- isKnownBranch: z3.boolean().nullable()
1449
- }),
1448
+ isKnownBranch: z3.boolean().nullish().default(true)
1449
+ }).nullable().transform(
1450
+ (repo) => repo ?? {
1451
+ name: null,
1452
+ originalUrl: "",
1453
+ reference: "",
1454
+ commitSha: "",
1455
+ isKnownBranch: true
1456
+ }
1457
+ ),
1450
1458
  vulnerabilityReport: z3.object({
1451
1459
  id: z3.string().uuid(),
1452
1460
  vendor: z3.nativeEnum(Vulnerability_Report_Vendor_Enum),
@@ -1937,7 +1945,15 @@ var init_types = __esm({
1937
1945
  reference: z7.string(),
1938
1946
  commitSha: z7.string(),
1939
1947
  isKnownBranch: z7.boolean().nullish().default(true)
1940
- }),
1948
+ }).nullable().transform(
1949
+ (repo) => repo ?? {
1950
+ name: null,
1951
+ originalUrl: "",
1952
+ reference: "",
1953
+ commitSha: "",
1954
+ isKnownBranch: true
1955
+ }
1956
+ ),
1941
1957
  vulnerabilityReportIssuesFixedCount: z7.object({
1942
1958
  vulnerabilityReportIssues_aggregate: z7.object({
1943
1959
  aggregate: z7.object({ count: z7.number() })
@@ -1958,7 +1974,7 @@ var init_types = __esm({
1958
1974
  file: z7.object({
1959
1975
  id: z7.string().uuid(),
1960
1976
  path: z7.string()
1961
- }),
1977
+ }).nullable(),
1962
1978
  pending: z7.object({
1963
1979
  aggregate: z7.object({
1964
1980
  count: z7.number()
@@ -2159,7 +2175,13 @@ var init_types = __esm({
2159
2175
  originalUrl: z7.string(),
2160
2176
  reference: z7.string(),
2161
2177
  name: z7.string()
2162
- }),
2178
+ }).nullable().transform(
2179
+ (repo) => repo ?? {
2180
+ originalUrl: "",
2181
+ reference: "",
2182
+ name: ""
2183
+ }
2184
+ ),
2163
2185
  createdByUser: z7.object({
2164
2186
  email: z7.string()
2165
2187
  }).nullable(),
@@ -7323,6 +7345,9 @@ async function sanitizeDataWithCounts(obj) {
7323
7345
  return { sanitizedData, counts };
7324
7346
  }
7325
7347
 
7348
+ // src/utils/with-timeout.ts
7349
+ import { setTimeout as delay } from "timers/promises";
7350
+
7326
7351
  // src/features/analysis/upload-file.ts
7327
7352
  import Debug7 from "debug";
7328
7353
  import fetch3, { File, fileFrom, FormData } from "node-fetch";
package/dist/index.mjs CHANGED
@@ -1660,8 +1660,16 @@ var init_analysis = __esm({
1660
1660
  originalUrl: z8.string(),
1661
1661
  reference: z8.string(),
1662
1662
  commitSha: z8.string(),
1663
- isKnownBranch: z8.boolean().nullable()
1664
- }),
1663
+ isKnownBranch: z8.boolean().nullish().default(true)
1664
+ }).nullable().transform(
1665
+ (repo) => repo ?? {
1666
+ name: null,
1667
+ originalUrl: "",
1668
+ reference: "",
1669
+ commitSha: "",
1670
+ isKnownBranch: true
1671
+ }
1672
+ ),
1665
1673
  vulnerabilityReport: z8.object({
1666
1674
  id: z8.string().uuid(),
1667
1675
  vendor: z8.nativeEnum(Vulnerability_Report_Vendor_Enum),
@@ -1982,7 +1990,15 @@ var init_types = __esm({
1982
1990
  reference: z11.string(),
1983
1991
  commitSha: z11.string(),
1984
1992
  isKnownBranch: z11.boolean().nullish().default(true)
1985
- }),
1993
+ }).nullable().transform(
1994
+ (repo) => repo ?? {
1995
+ name: null,
1996
+ originalUrl: "",
1997
+ reference: "",
1998
+ commitSha: "",
1999
+ isKnownBranch: true
2000
+ }
2001
+ ),
1986
2002
  vulnerabilityReportIssuesFixedCount: z11.object({
1987
2003
  vulnerabilityReportIssues_aggregate: z11.object({
1988
2004
  aggregate: z11.object({ count: z11.number() })
@@ -2003,7 +2019,7 @@ var init_types = __esm({
2003
2019
  file: z11.object({
2004
2020
  id: z11.string().uuid(),
2005
2021
  path: z11.string()
2006
- }),
2022
+ }).nullable(),
2007
2023
  pending: z11.object({
2008
2024
  aggregate: z11.object({
2009
2025
  count: z11.number()
@@ -2204,7 +2220,13 @@ var init_types = __esm({
2204
2220
  originalUrl: z11.string(),
2205
2221
  reference: z11.string(),
2206
2222
  name: z11.string()
2207
- }),
2223
+ }).nullable().transform(
2224
+ (repo) => repo ?? {
2225
+ originalUrl: "",
2226
+ reference: "",
2227
+ name: ""
2228
+ }
2229
+ ),
2208
2230
  createdByUser: z11.object({
2209
2231
  email: z11.string()
2210
2232
  }).nullable(),
@@ -6695,9 +6717,9 @@ function transformVisualStudioUrl(url) {
6695
6717
  }
6696
6718
  function _getPublicAdoClient({
6697
6719
  orgName,
6698
- origin: origin2
6720
+ origin
6699
6721
  }) {
6700
- const orgUrl = `${origin2}/${orgName}`;
6722
+ const orgUrl = `${origin}/${orgName}`;
6701
6723
  const authHandler = api.getPersonalAccessTokenHandler("");
6702
6724
  authHandler.canHandleAuthentication = () => false;
6703
6725
  authHandler.prepareRequest = (_options) => {
@@ -6759,10 +6781,10 @@ async function getAdoConnectData({
6759
6781
  org: tokenOrg
6760
6782
  };
6761
6783
  }
6762
- const { owner, origin: origin2, prefixPath } = parseAdoOwnerAndRepo(url);
6784
+ const { owner, origin, prefixPath } = parseAdoOwnerAndRepo(url);
6763
6785
  return {
6764
6786
  org: owner,
6765
- origin: prefixPath ? `${origin2}/${prefixPath}` : origin2
6787
+ origin: prefixPath ? `${origin}/${prefixPath}` : origin
6766
6788
  };
6767
6789
  }
6768
6790
  if (!tokenOrg) {
@@ -6787,17 +6809,17 @@ function isAdoOnCloud(url) {
6787
6809
  return urlObj.origin.toLowerCase() === DEFUALT_ADO_ORIGIN || urlObj.hostname.toLowerCase().endsWith(".visualstudio.com");
6788
6810
  }
6789
6811
  async function getAdoApiClient(params) {
6790
- const { origin: origin2 = DEFUALT_ADO_ORIGIN, orgName } = params;
6812
+ const { origin = DEFUALT_ADO_ORIGIN, orgName } = params;
6791
6813
  if (params.tokenType === "NONE" /* NONE */ || // note: move to public client if the token is not associated with the PAT org
6792
6814
  // we're only doing it the ado on the cloud
6793
- params.tokenType === "PAT" /* PAT */ && params.patTokenOrg !== orgName && isAdoOnCloud(origin2)) {
6794
- return _getPublicAdoClient({ orgName, origin: origin2 });
6815
+ params.tokenType === "PAT" /* PAT */ && params.patTokenOrg !== orgName && isAdoOnCloud(origin)) {
6816
+ return _getPublicAdoClient({ orgName, origin });
6795
6817
  }
6796
- const orgUrl = `${origin2}/${orgName}`;
6818
+ const orgUrl = `${origin}/${orgName}`;
6797
6819
  if (params.tokenType === "OAUTH" /* OAUTH */) {
6798
- if (!isAdoOnCloud(origin2)) {
6820
+ if (!isAdoOnCloud(origin)) {
6799
6821
  throw new Error(
6800
- `Oauth token is not supported for ADO on prem - ${origin2} `
6822
+ `Oauth token is not supported for ADO on prem - ${origin} `
6801
6823
  );
6802
6824
  }
6803
6825
  const connection2 = new api.WebApi(
@@ -6833,7 +6855,7 @@ function getAdoTokenInfo(token) {
6833
6855
  async function getAdoClientParams(params) {
6834
6856
  const { url, accessToken, tokenOrg } = params;
6835
6857
  const adoTokenInfo = getAdoTokenInfo(accessToken);
6836
- const { org, origin: origin2 } = await getAdoConnectData({
6858
+ const { org, origin } = await getAdoConnectData({
6837
6859
  url,
6838
6860
  tokenOrg,
6839
6861
  adoTokenInfo
@@ -6842,14 +6864,14 @@ async function getAdoClientParams(params) {
6842
6864
  case "NONE" /* NONE */:
6843
6865
  return {
6844
6866
  tokenType: "NONE" /* NONE */,
6845
- origin: origin2,
6867
+ origin,
6846
6868
  orgName: org.toLowerCase()
6847
6869
  };
6848
6870
  case "OAUTH" /* OAUTH */: {
6849
6871
  return {
6850
6872
  tokenType: "OAUTH" /* OAUTH */,
6851
6873
  accessToken: adoTokenInfo.accessToken,
6852
- origin: origin2,
6874
+ origin,
6853
6875
  orgName: org.toLowerCase()
6854
6876
  };
6855
6877
  }
@@ -6858,7 +6880,7 @@ async function getAdoClientParams(params) {
6858
6880
  tokenType: "PAT" /* PAT */,
6859
6881
  accessToken: adoTokenInfo.accessToken,
6860
6882
  patTokenOrg: z17.string().parse(tokenOrg).toLowerCase(),
6861
- origin: origin2,
6883
+ origin,
6862
6884
  orgName: org.toLowerCase()
6863
6885
  };
6864
6886
  }
@@ -7044,7 +7066,7 @@ async function getAdoSdk(params) {
7044
7066
  }) {
7045
7067
  const { owner, repo, projectName, prefixPath } = parseAdoOwnerAndRepo(repoUrl);
7046
7068
  const url = new URL(repoUrl);
7047
- const origin2 = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
7069
+ const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
7048
7070
  const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
7049
7071
  const path27 = [
7050
7072
  prefixPath,
@@ -7057,7 +7079,7 @@ async function getAdoSdk(params) {
7057
7079
  "items",
7058
7080
  "items"
7059
7081
  ].filter(Boolean).join("/");
7060
- return new URL(`${path27}?${params2}`, origin2).toString();
7082
+ return new URL(`${path27}?${params2}`, origin).toString();
7061
7083
  },
7062
7084
  async getAdoBranchList({ repoUrl }) {
7063
7085
  try {
@@ -7338,7 +7360,8 @@ async function getAdoSdk(params) {
7338
7360
  async function getAdoRepoList({
7339
7361
  orgName,
7340
7362
  tokenOrg,
7341
- accessToken
7363
+ accessToken,
7364
+ url
7342
7365
  }) {
7343
7366
  let orgs = [];
7344
7367
  const adoTokenInfo = getAdoTokenInfo(accessToken);
@@ -7348,10 +7371,11 @@ async function getAdoRepoList({
7348
7371
  if (adoTokenInfo.type === "OAUTH" /* OAUTH */) {
7349
7372
  orgs = await getOrgsForOauthToken({ oauthToken: accessToken });
7350
7373
  }
7351
- if (orgs.length === 0 && !orgName) {
7374
+ const effectiveOrgName = orgName || tokenOrg;
7375
+ if (orgs.length === 0 && !effectiveOrgName) {
7352
7376
  throw new Error(`no orgs for ADO`);
7353
- } else if (orgs.length === 0 && orgName) {
7354
- orgs = [orgName];
7377
+ } else if (orgs.length === 0 && effectiveOrgName) {
7378
+ orgs = [effectiveOrgName];
7355
7379
  }
7356
7380
  const repos = (await Promise.allSettled(
7357
7381
  orgs.map(async (org) => {
@@ -7359,7 +7383,7 @@ async function getAdoRepoList({
7359
7383
  ...await getAdoClientParams({
7360
7384
  accessToken,
7361
7385
  tokenOrg: tokenOrg || org,
7362
- url: void 0
7386
+ url
7363
7387
  }),
7364
7388
  orgName: org
7365
7389
  });
@@ -7627,15 +7651,11 @@ var AdoSCMLib = class extends SCMLib {
7627
7651
  }
7628
7652
  async getRepoList(scmOrg) {
7629
7653
  this._validateAccessToken();
7630
- if (this.url && new URL(this.url).origin !== scmCloudUrl.Ado) {
7631
- throw new Error(
7632
- `Oauth token is not supported for ADO on prem - ${origin} `
7633
- );
7634
- }
7635
7654
  return getAdoRepoList({
7636
7655
  orgName: scmOrg,
7637
7656
  accessToken: this.accessToken,
7638
- tokenOrg: this.scmOrg
7657
+ tokenOrg: this.scmOrg,
7658
+ url: this.url
7639
7659
  });
7640
7660
  }
7641
7661
  async getBranchList() {
@@ -7840,15 +7860,11 @@ var AdoSCMLib = class extends SCMLib {
7840
7860
  // getRepositoriesPaged() API per-project for true server-side pagination.
7841
7861
  async searchRepos(params) {
7842
7862
  this._validateAccessToken();
7843
- if (this.url && new URL(this.url).origin !== scmCloudUrl.Ado) {
7844
- throw new Error(
7845
- `Oauth token is not supported for ADO on prem - ${this.url}`
7846
- );
7847
- }
7848
7863
  const allRepos = await getAdoRepoList({
7849
7864
  orgName: params.scmOrg,
7850
7865
  accessToken: this.accessToken,
7851
- tokenOrg: this.scmOrg
7866
+ tokenOrg: this.scmOrg,
7867
+ url: this.url
7852
7868
  });
7853
7869
  allRepos.sort((a, b) => {
7854
7870
  const dateA = a.repoUpdatedAt ? new Date(a.repoUpdatedAt).getTime() : 0;
@@ -8945,17 +8961,30 @@ function getGithubSdk(params = {}) {
8945
8961
  },
8946
8962
  async getGithubRepoList() {
8947
8963
  try {
8948
- const githubRepos = await octokit.request(GET_USER_REPOS, {
8949
- sort: "updated"
8950
- });
8951
- return githubRepos.data.map((repo) => ({
8952
- repoName: repo.name,
8953
- repoUrl: repo.html_url,
8954
- repoOwner: repo.owner.login,
8955
- repoLanguages: repo.language ? [repo.language] : [],
8956
- repoIsPublic: !repo.private,
8957
- repoUpdatedAt: repo.updated_at
8958
- }));
8964
+ const allRepos = [];
8965
+ let page = 1;
8966
+ const perPage = 100;
8967
+ let hasMore = true;
8968
+ while (hasMore) {
8969
+ const githubRepos = await octokit.request(GET_USER_REPOS, {
8970
+ sort: "updated",
8971
+ per_page: perPage,
8972
+ page
8973
+ });
8974
+ for (const repo of githubRepos.data) {
8975
+ allRepos.push({
8976
+ repoName: repo.name,
8977
+ repoUrl: repo.html_url,
8978
+ repoOwner: repo.owner.login,
8979
+ repoLanguages: repo.language ? [repo.language] : [],
8980
+ repoIsPublic: !repo.private,
8981
+ repoUpdatedAt: repo.updated_at
8982
+ });
8983
+ }
8984
+ hasMore = githubRepos.data.length >= perPage;
8985
+ page++;
8986
+ }
8987
+ return allRepos;
8959
8988
  } catch (e) {
8960
8989
  if (e instanceof RequestError && e.status === 401) {
8961
8990
  console.warn(
@@ -9378,7 +9407,7 @@ function getGithubSdk(params = {}) {
9378
9407
  if (!org) {
9379
9408
  throw new Error("Organization is required for repository search");
9380
9409
  }
9381
- const query = `org:${org}`;
9410
+ const query = `org:${org} fork:true`;
9382
9411
  const githubSortField = sort.field === "name" ? void 0 : "updated";
9383
9412
  const response = await octokit.rest.search.repos({
9384
9413
  q: query,
@@ -10139,7 +10168,9 @@ async function searchGitlabProjects({
10139
10168
  url,
10140
10169
  accessToken,
10141
10170
  perPage = 20,
10142
- page = 1
10171
+ page = 1,
10172
+ orderBy = "created_at",
10173
+ sort = "desc"
10143
10174
  }) {
10144
10175
  if (perPage > GITLAB_MAX_PER_PAGE) {
10145
10176
  throw new Error(
@@ -10147,31 +10178,28 @@ async function searchGitlabProjects({
10147
10178
  );
10148
10179
  }
10149
10180
  const api2 = getGitBeaker({ url, gitlabAuthToken: accessToken });
10181
+ const fetchProjects = (effectiveOrderBy) => api2.Projects.all({
10182
+ membership: true,
10183
+ orderBy: effectiveOrderBy,
10184
+ sort,
10185
+ pagination: "offset",
10186
+ perPage,
10187
+ page,
10188
+ showExpanded: true
10189
+ });
10150
10190
  let response;
10151
- try {
10152
- response = await api2.Projects.all({
10153
- membership: true,
10154
- orderBy: "last_activity_at",
10155
- sort: "desc",
10156
- pagination: "offset",
10157
- perPage,
10158
- page,
10159
- showExpanded: true
10160
- });
10161
- } catch (e) {
10162
- debug4(
10163
- "[searchGitlabProjects] order_by=last_activity_at failed, falling back to created_at: %s",
10164
- e instanceof Error ? e.message : String(e)
10165
- );
10166
- response = await api2.Projects.all({
10167
- membership: true,
10168
- orderBy: "created_at",
10169
- sort: "desc",
10170
- pagination: "offset",
10171
- perPage,
10172
- page,
10173
- showExpanded: true
10174
- });
10191
+ if (orderBy === "last_activity_at") {
10192
+ try {
10193
+ response = await fetchProjects("last_activity_at");
10194
+ } catch (e) {
10195
+ debug4(
10196
+ "[searchGitlabProjects] order_by=last_activity_at failed, falling back to created_at: %s",
10197
+ e instanceof Error ? e.message : String(e)
10198
+ );
10199
+ response = await fetchProjects("created_at");
10200
+ }
10201
+ } else {
10202
+ response = await fetchProjects(orderBy);
10175
10203
  }
10176
10204
  const projects = response.data.map((p) => ({
10177
10205
  id: p.id,
@@ -10933,11 +10961,15 @@ var GitlabSCMLib = class extends SCMLib {
10933
10961
  }
10934
10962
  const page = parseCursorSafe(params.cursor, 1);
10935
10963
  const perPage = params.limit || 10;
10964
+ const sort = params.sort || { field: "updated", order: "desc" };
10965
+ const orderBy = sort.field === "updated" ? "last_activity_at" : "created_at";
10936
10966
  const { projects, hasMore } = await searchGitlabProjects({
10937
10967
  url: this.url,
10938
10968
  accessToken: this.accessToken,
10939
10969
  perPage,
10940
- page
10970
+ page,
10971
+ orderBy,
10972
+ sort: sort.order
10941
10973
  });
10942
10974
  const includeLanguages = params.includeLanguages !== false;
10943
10975
  const languageMap = /* @__PURE__ */ new Map();
@@ -11127,29 +11159,29 @@ var StubSCMLib = class extends SCMLib {
11127
11159
  };
11128
11160
 
11129
11161
  // src/features/analysis/scm/scmFactory.ts
11130
- async function createScmLib({ url, accessToken, scmType, scmOrg }, { propagateExceptions = false } = {}) {
11162
+ async function createScmLib({ url, accessToken, scmType, scmOrg }, { propagateExceptions = false, skipValidation = false } = {}) {
11131
11163
  const trimmedUrl = url ? url.trim().replace(/\/$/, "").replace(/.git$/i, "") : void 0;
11132
11164
  try {
11133
11165
  switch (scmType) {
11134
11166
  case "GITHUB" /* GITHUB */: {
11135
11167
  const scm = new GithubSCMLib(trimmedUrl, accessToken, scmOrg);
11136
- await scm.validateParams();
11168
+ if (!skipValidation) await scm.validateParams();
11137
11169
  return scm;
11138
11170
  }
11139
11171
  case "GITLAB" /* GITLAB */: {
11140
11172
  const scm = new GitlabSCMLib(trimmedUrl, accessToken, scmOrg);
11141
- await scm.validateParams();
11173
+ if (!skipValidation) await scm.validateParams();
11142
11174
  return scm;
11143
11175
  }
11144
11176
  case "ADO" /* ADO */: {
11145
11177
  const scm = new AdoSCMLib(trimmedUrl, accessToken, scmOrg);
11146
11178
  await scm.getAdoSdk();
11147
- await scm.validateParams();
11179
+ if (!skipValidation) await scm.validateParams();
11148
11180
  return scm;
11149
11181
  }
11150
11182
  case "BITBUCKET" /* BITBUCKET */: {
11151
11183
  const scm = new BitbucketSCMLib(trimmedUrl, accessToken, scmOrg);
11152
- await scm.validateParams();
11184
+ if (!skipValidation) await scm.validateParams();
11153
11185
  return scm;
11154
11186
  }
11155
11187
  }
@@ -13659,8 +13691,21 @@ async function uploadAiBlameCommandHandler(args) {
13659
13691
  await uploadAiBlameHandler({ args });
13660
13692
  }
13661
13693
 
13694
+ // src/utils/with-timeout.ts
13695
+ import { setTimeout as delay } from "timers/promises";
13696
+ function withTimeout(promise, ms, label) {
13697
+ const ac = new AbortController();
13698
+ return Promise.race([
13699
+ promise.finally(() => ac.abort()),
13700
+ delay(ms, void 0, { signal: ac.signal }).then(() => {
13701
+ throw new Error(`${label} timed out after ${ms}ms`);
13702
+ })
13703
+ ]);
13704
+ }
13705
+
13662
13706
  // src/features/analysis/graphql/tracy-batch-upload.ts
13663
13707
  var debug10 = Debug9("mobbdev:tracy-batch-upload");
13708
+ var BATCH_TIMEOUT_MS = 3e4;
13664
13709
  async function sanitizeRawData(rawData) {
13665
13710
  try {
13666
13711
  const sanitized = await sanitizeData(rawData);
@@ -13677,6 +13722,7 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir) {
13677
13722
  const repositoryUrl = await getRepositoryUrl(workingDir);
13678
13723
  const { computerName, userName } = getSystemInfo();
13679
13724
  const clientVersion = packageJson.version;
13725
+ debug10("[step:sanitize] Sanitizing %d records", rawRecords.length);
13680
13726
  const serializedRawDataByIndex = /* @__PURE__ */ new Map();
13681
13727
  const records = await Promise.all(
13682
13728
  rawRecords.map(async (record, index) => {
@@ -13696,21 +13742,47 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir) {
13696
13742
  );
13697
13743
  const recordsWithRawData = rawRecords.map((r, i) => ({ recordId: r.recordId, index: i })).filter((entry) => serializedRawDataByIndex.has(entry.index));
13698
13744
  if (recordsWithRawData.length > 0) {
13699
- debug10("Uploading %d rawData files to S3...", recordsWithRawData.length);
13700
- const uploadUrlResult = await client.getTracyRawDataUploadUrl();
13745
+ debug10(
13746
+ "[step:s3-url] Requesting presigned URL for %d rawData files",
13747
+ recordsWithRawData.length
13748
+ );
13749
+ let uploadUrlResult;
13750
+ try {
13751
+ uploadUrlResult = await withTimeout(
13752
+ client.getTracyRawDataUploadUrl(),
13753
+ BATCH_TIMEOUT_MS,
13754
+ "[step:s3-url] getTracyRawDataUploadUrl"
13755
+ );
13756
+ } catch (err) {
13757
+ return {
13758
+ ok: false,
13759
+ errors: [
13760
+ `[step:s3-url] Failed to fetch S3 upload URL: ${err.message}`
13761
+ ]
13762
+ };
13763
+ }
13701
13764
  const { url, uploadFieldsJSON, keyPrefix } = uploadUrlResult.getTracyRawDataUploadUrl;
13702
13765
  if (!url || !uploadFieldsJSON || !keyPrefix) {
13703
13766
  return {
13704
13767
  ok: false,
13705
- errors: ["Failed to get S3 upload URL for rawData"]
13768
+ errors: [
13769
+ `[step:s3-url] Missing S3 upload fields (url=${!!url}, fields=${!!uploadFieldsJSON}, prefix=${!!keyPrefix})`
13770
+ ]
13706
13771
  };
13707
13772
  }
13708
13773
  let uploadFields;
13709
13774
  try {
13710
13775
  uploadFields = JSON.parse(uploadFieldsJSON);
13711
13776
  } catch {
13712
- return { ok: false, errors: ["Malformed uploadFieldsJSON from server"] };
13777
+ return {
13778
+ ok: false,
13779
+ errors: ["[step:s3-url] Malformed uploadFieldsJSON from server"]
13780
+ };
13713
13781
  }
13782
+ debug10(
13783
+ "[step:s3-upload] Uploading %d files to S3",
13784
+ recordsWithRawData.length
13785
+ );
13714
13786
  const uploadResults = await Promise.allSettled(
13715
13787
  recordsWithRawData.map(async (entry) => {
13716
13788
  const rawDataJson = serializedRawDataByIndex.get(entry.index);
@@ -13719,48 +13791,59 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir) {
13719
13791
  return;
13720
13792
  }
13721
13793
  const uploadKey = `${keyPrefix}${entry.recordId}.json`;
13722
- await uploadFile({
13723
- file: Buffer.from(rawDataJson, "utf-8"),
13724
- url,
13725
- uploadKey,
13726
- uploadFields
13727
- });
13794
+ await withTimeout(
13795
+ uploadFile({
13796
+ file: Buffer.from(rawDataJson, "utf-8"),
13797
+ url,
13798
+ uploadKey,
13799
+ uploadFields
13800
+ }),
13801
+ BATCH_TIMEOUT_MS,
13802
+ `[step:s3-upload] uploadFile ${entry.recordId}`
13803
+ );
13728
13804
  records[entry.index].rawDataS3Key = uploadKey;
13729
13805
  })
13730
13806
  );
13731
13807
  const uploadErrors = uploadResults.filter((r) => r.status === "rejected").map((r) => r.reason.message);
13732
13808
  if (uploadErrors.length > 0) {
13733
- debug10("S3 upload errors: %O", uploadErrors);
13809
+ debug10("[step:s3-upload] S3 upload errors: %O", uploadErrors);
13734
13810
  }
13735
13811
  const missingS3Keys = recordsWithRawData.filter(
13736
13812
  (entry) => !records[entry.index].rawDataS3Key
13737
13813
  );
13738
13814
  if (missingS3Keys.length > 0) {
13739
13815
  const missingIds = missingS3Keys.map((e) => e.recordId);
13740
- debug10("Records missing S3 keys after upload: %O", missingIds);
13816
+ debug10("[step:s3-upload] Records missing S3 keys: %O", missingIds);
13741
13817
  return {
13742
13818
  ok: false,
13743
13819
  errors: [
13744
- `Failed to upload rawData to S3 for ${missingS3Keys.length} record(s): ${missingIds.join(", ")}`,
13820
+ `[step:s3-upload] Failed to upload rawData for ${missingS3Keys.length} record(s): ${missingIds.join(", ")}`,
13745
13821
  ...uploadErrors
13746
13822
  ]
13747
13823
  };
13748
13824
  }
13749
- debug10("S3 uploads complete");
13825
+ debug10("[step:s3-upload] S3 uploads complete");
13750
13826
  }
13827
+ debug10("[step:gql-submit] Submitting %d records via GraphQL", records.length);
13751
13828
  try {
13752
- const result = await client.uploadTracyRecords({ records });
13829
+ const result = await withTimeout(
13830
+ client.uploadTracyRecords({ records }),
13831
+ BATCH_TIMEOUT_MS,
13832
+ "[step:gql-submit] uploadTracyRecords"
13833
+ );
13753
13834
  if (result.uploadTracyRecords.status !== "OK") {
13754
13835
  return {
13755
13836
  ok: false,
13756
- errors: [result.uploadTracyRecords.error ?? "Unknown server error"]
13837
+ errors: [
13838
+ `[step:gql-submit] Server rejected: ${result.uploadTracyRecords.error ?? "Unknown server error"}`
13839
+ ]
13757
13840
  };
13758
13841
  }
13759
13842
  } catch (err) {
13760
- debug10("Upload failed: %s", err.message);
13843
+ debug10("[step:gql-submit] Upload failed: %s", err.message);
13761
13844
  return {
13762
13845
  ok: false,
13763
- errors: [err.message]
13846
+ errors: [`[step:gql-submit] ${err.message}`]
13764
13847
  };
13765
13848
  }
13766
13849
  return { ok: true, errors: null };
@@ -16297,7 +16380,7 @@ async function analyzeHandler(args) {
16297
16380
 
16298
16381
  // src/features/claude_code/data_collector.ts
16299
16382
  import { createHash as createHash2 } from "crypto";
16300
- import { open as open4, readdir, readFile, unlink } from "fs/promises";
16383
+ import { access, open as open4, readdir, readFile, unlink } from "fs/promises";
16301
16384
  import path13 from "path";
16302
16385
  import { z as z33 } from "zod";
16303
16386
  init_client_generates();
@@ -16583,7 +16666,7 @@ function createLogger(config2) {
16583
16666
 
16584
16667
  // src/features/claude_code/hook_logger.ts
16585
16668
  var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
16586
- var CLI_VERSION = true ? "1.2.56" : "unknown";
16669
+ var CLI_VERSION = true ? "1.2.57" : "unknown";
16587
16670
  var NAMESPACE = "mobbdev-claude-code-hook-logs";
16588
16671
  function createHookLogger(scopePath) {
16589
16672
  return createLogger({
@@ -16622,10 +16705,15 @@ function createScopedHookLog(scopePath) {
16622
16705
  }
16623
16706
 
16624
16707
  // src/features/claude_code/data_collector.ts
16708
+ var GLOBAL_COOLDOWN_MS = 5e3;
16625
16709
  var HOOK_COOLDOWN_MS = 1e4;
16710
+ var ACTIVE_LOCK_TTL_MS = 6e4;
16711
+ var GQL_AUTH_TIMEOUT_MS = 15e3;
16626
16712
  var STALE_KEY_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
16627
16713
  var CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1e3;
16714
+ var MAX_ENTRIES_PER_INVOCATION = 100;
16628
16715
  var COOLDOWN_KEY = "lastHookRunAt";
16716
+ var ACTIVE_KEY = "hookActiveAt";
16629
16717
  var HookDataSchema = z33.object({
16630
16718
  session_id: z33.string(),
16631
16719
  transcript_path: z33.string(),
@@ -16658,9 +16746,12 @@ async function readStdinData() {
16658
16746
  clearTimeout(timer);
16659
16747
  try {
16660
16748
  const parsedData = JSON.parse(inputData);
16661
- hookLog.debug("Parsed stdin data", {
16662
- keys: Object.keys(parsedData)
16663
- });
16749
+ hookLog.debug(
16750
+ {
16751
+ data: { keys: Object.keys(parsedData) }
16752
+ },
16753
+ "Parsed stdin data"
16754
+ );
16664
16755
  resolve(parsedData);
16665
16756
  } catch (error) {
16666
16757
  const msg = `Failed to parse JSON from stdin: ${error.message}`;
@@ -16672,7 +16763,10 @@ async function readStdinData() {
16672
16763
  if (settled) return;
16673
16764
  settled = true;
16674
16765
  clearTimeout(timer);
16675
- hookLog.error("Error reading from stdin", { error: error.message });
16766
+ hookLog.error(
16767
+ { data: { error: error.message } },
16768
+ "Error reading from stdin"
16769
+ );
16676
16770
  reject(new Error(`Error reading from stdin: ${error.message}`));
16677
16771
  });
16678
16772
  });
@@ -16689,7 +16783,63 @@ function getCursorKey(transcriptPath) {
16689
16783
  const hash = createHash2("sha256").update(transcriptPath).digest("hex").slice(0, 12);
16690
16784
  return `cursor.${hash}`;
16691
16785
  }
16786
+ async function resolveTranscriptPath(transcriptPath, sessionId) {
16787
+ try {
16788
+ await access(transcriptPath);
16789
+ return transcriptPath;
16790
+ } catch {
16791
+ }
16792
+ const filename = path13.basename(transcriptPath);
16793
+ const dirName = path13.basename(path13.dirname(transcriptPath));
16794
+ const projectsDir = path13.dirname(path13.dirname(transcriptPath));
16795
+ const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
16796
+ if (baseDirName !== dirName) {
16797
+ const candidate = path13.join(projectsDir, baseDirName, filename);
16798
+ try {
16799
+ await access(candidate);
16800
+ hookLog.info(
16801
+ {
16802
+ data: {
16803
+ original: transcriptPath,
16804
+ resolved: candidate,
16805
+ sessionId,
16806
+ method: "worktree-strip"
16807
+ }
16808
+ },
16809
+ "Transcript path resolved via fallback"
16810
+ );
16811
+ return candidate;
16812
+ } catch {
16813
+ }
16814
+ }
16815
+ try {
16816
+ const dirs = await readdir(projectsDir);
16817
+ for (const dir of dirs) {
16818
+ if (dir === dirName) continue;
16819
+ const candidate = path13.join(projectsDir, dir, filename);
16820
+ try {
16821
+ await access(candidate);
16822
+ hookLog.info(
16823
+ {
16824
+ data: {
16825
+ original: transcriptPath,
16826
+ resolved: candidate,
16827
+ sessionId,
16828
+ method: "sibling-scan"
16829
+ }
16830
+ },
16831
+ "Transcript path resolved via fallback"
16832
+ );
16833
+ return candidate;
16834
+ } catch {
16835
+ }
16836
+ }
16837
+ } catch {
16838
+ }
16839
+ return transcriptPath;
16840
+ }
16692
16841
  async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore) {
16842
+ transcriptPath = await resolveTranscriptPath(transcriptPath, sessionId);
16693
16843
  const cursor = sessionStore.get(getCursorKey(transcriptPath));
16694
16844
  let content;
16695
16845
  let fileSize;
@@ -16700,8 +16850,8 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore)
16700
16850
  const stat = await fh.stat();
16701
16851
  fileSize = stat.size;
16702
16852
  if (cursor.byteOffset >= stat.size) {
16703
- hookLog.info("No new data in transcript file", { sessionId });
16704
- return { entries: [], fileSize };
16853
+ hookLog.info({ data: { sessionId } }, "No new data in transcript file");
16854
+ return { entries: [], endByteOffset: fileSize };
16705
16855
  }
16706
16856
  const buf = Buffer.alloc(stat.size - cursor.byteOffset);
16707
16857
  await fh.read(buf, 0, buf.length, cursor.byteOffset);
@@ -16710,53 +16860,84 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore)
16710
16860
  await fh.close();
16711
16861
  }
16712
16862
  lineIndexOffset = cursor.byteOffset;
16713
- hookLog.debug("Read transcript file from offset", {
16714
- transcriptPath,
16715
- byteOffset: cursor.byteOffset,
16716
- bytesRead: content.length
16717
- });
16863
+ hookLog.debug(
16864
+ {
16865
+ data: {
16866
+ transcriptPath,
16867
+ byteOffset: cursor.byteOffset,
16868
+ bytesRead: content.length
16869
+ }
16870
+ },
16871
+ "Read transcript file from offset"
16872
+ );
16718
16873
  } else {
16719
16874
  content = await readFile(transcriptPath, "utf-8");
16720
16875
  fileSize = Buffer.byteLength(content, "utf-8");
16721
16876
  lineIndexOffset = 0;
16722
- hookLog.debug("Read full transcript file", {
16723
- transcriptPath,
16724
- totalBytes: fileSize
16725
- });
16877
+ hookLog.debug(
16878
+ { data: { transcriptPath, totalBytes: fileSize } },
16879
+ "Read full transcript file"
16880
+ );
16726
16881
  }
16727
- const lines = content.split("\n").filter((line) => line.trim().length > 0);
16882
+ const startOffset = cursor?.byteOffset ?? 0;
16883
+ const allLines = content.split("\n");
16728
16884
  const parsed = [];
16729
16885
  let malformedLines = 0;
16730
- for (let i = 0; i < lines.length; i++) {
16886
+ let bytesConsumed = 0;
16887
+ let parsedLineIndex = 0;
16888
+ for (let i = 0; i < allLines.length; i++) {
16889
+ const line = allLines[i];
16890
+ const lineBytes = Buffer.byteLength(line, "utf-8") + (i < allLines.length - 1 ? 1 : 0);
16891
+ if (parsed.length >= MAX_ENTRIES_PER_INVOCATION) break;
16892
+ bytesConsumed += lineBytes;
16893
+ if (line.trim().length === 0) continue;
16731
16894
  try {
16732
- const entry = JSON.parse(lines[i]);
16895
+ const entry = JSON.parse(line);
16733
16896
  const recordId = entry.uuid ?? generateSyntheticId(
16734
16897
  entry.sessionId,
16735
16898
  entry.timestamp,
16736
16899
  entry.type,
16737
- lineIndexOffset + i
16900
+ lineIndexOffset + parsedLineIndex
16738
16901
  );
16739
16902
  parsed.push({ ...entry, _recordId: recordId });
16740
16903
  } catch {
16741
16904
  malformedLines++;
16742
16905
  }
16906
+ parsedLineIndex++;
16743
16907
  }
16908
+ const endByteOffset = startOffset + bytesConsumed;
16909
+ const capped = parsed.length >= MAX_ENTRIES_PER_INVOCATION;
16744
16910
  if (malformedLines > 0) {
16745
- hookLog.warn("Skipped malformed lines", { malformedLines, transcriptPath });
16911
+ hookLog.warn(
16912
+ { data: { malformedLines, transcriptPath } },
16913
+ "Skipped malformed lines"
16914
+ );
16746
16915
  }
16747
- if (!cursor) {
16748
- hookLog.info("First invocation for session \u2014 uploading all entries", {
16749
- sessionId,
16750
- totalEntries: parsed.length
16751
- });
16916
+ if (capped) {
16917
+ hookLog.info(
16918
+ {
16919
+ data: {
16920
+ sessionId,
16921
+ entriesParsed: parsed.length,
16922
+ totalLines: allLines.length
16923
+ }
16924
+ },
16925
+ "Capped at MAX_ENTRIES_PER_INVOCATION, remaining entries deferred"
16926
+ );
16927
+ } else if (!cursor) {
16928
+ hookLog.info(
16929
+ { data: { sessionId, totalEntries: parsed.length } },
16930
+ "First invocation for session \u2014 uploading all entries"
16931
+ );
16752
16932
  } else {
16753
- hookLog.info("Resuming from byte offset", {
16754
- sessionId,
16755
- byteOffset: cursor.byteOffset,
16756
- newEntries: parsed.length
16757
- });
16933
+ hookLog.info(
16934
+ {
16935
+ data: { sessionId, byteOffset: startOffset, newEntries: parsed.length }
16936
+ },
16937
+ "Resuming from byte offset"
16938
+ );
16758
16939
  }
16759
- return { entries: parsed, fileSize };
16940
+ return { entries: parsed, endByteOffset };
16760
16941
  }
16761
16942
  var FILTERED_PROGRESS_SUBTYPES = /* @__PURE__ */ new Set([
16762
16943
  // Incremental streaming output from running bash commands, emitted every
@@ -16851,7 +17032,7 @@ async function cleanupStaleSessions(sessionStore) {
16851
17032
  }
16852
17033
  }
16853
17034
  if (deletedCount > 0) {
16854
- hookLog.info("Cleaned up stale session files", { deletedCount });
17035
+ hookLog.info({ data: { deletedCount } }, "Cleaned up stale session files");
16855
17036
  }
16856
17037
  } catch {
16857
17038
  }
@@ -16859,31 +17040,61 @@ async function cleanupStaleSessions(sessionStore) {
16859
17040
  }
16860
17041
  async function processAndUploadTranscriptEntries() {
16861
17042
  hookLog.info("Hook invoked");
17043
+ const globalLastRun = configStore.get("claudeCode.globalLastHookRunAt");
17044
+ const globalNow = Date.now();
17045
+ if (globalLastRun && globalNow - globalLastRun < GLOBAL_COOLDOWN_MS) {
17046
+ return { entriesUploaded: 0, entriesSkipped: 0, errors: 0 };
17047
+ }
17048
+ configStore.set("claudeCode.globalLastHookRunAt", globalNow);
16862
17049
  const rawData = await readStdinData();
16863
17050
  const hookData = validateHookData(rawData);
16864
17051
  const sessionStore = createSessionConfigStore(hookData.session_id);
16865
17052
  await cleanupStaleSessions(sessionStore);
17053
+ const now = Date.now();
16866
17054
  const lastRunAt = sessionStore.get(COOLDOWN_KEY);
16867
- if (lastRunAt && Date.now() - lastRunAt < HOOK_COOLDOWN_MS) {
17055
+ if (lastRunAt && now - lastRunAt < HOOK_COOLDOWN_MS) {
17056
+ return { entriesUploaded: 0, entriesSkipped: 0, errors: 0 };
17057
+ }
17058
+ const activeAt = sessionStore.get(ACTIVE_KEY);
17059
+ if (activeAt && now - activeAt < ACTIVE_LOCK_TTL_MS) {
17060
+ const activeDuration = now - activeAt;
17061
+ if (activeDuration > HOOK_COOLDOWN_MS) {
17062
+ hookLog.warn(
17063
+ {
17064
+ data: {
17065
+ activeDurationMs: activeDuration,
17066
+ sessionId: hookData.session_id
17067
+ }
17068
+ },
17069
+ "Hook still active \u2014 possible slow upload or hung process"
17070
+ );
17071
+ }
16868
17072
  return { entriesUploaded: 0, entriesSkipped: 0, errors: 0 };
16869
17073
  }
16870
- sessionStore.set(COOLDOWN_KEY, Date.now());
17074
+ sessionStore.set(ACTIVE_KEY, now);
17075
+ sessionStore.set(COOLDOWN_KEY, now);
16871
17076
  const log2 = createScopedHookLog(hookData.cwd);
16872
- log2.info("Hook data validated", {
16873
- sessionId: hookData.session_id,
16874
- toolName: hookData.tool_name,
16875
- hookEvent: hookData.hook_event_name,
16876
- cwd: hookData.cwd
16877
- });
17077
+ log2.info(
17078
+ {
17079
+ data: {
17080
+ sessionId: hookData.session_id,
17081
+ toolName: hookData.tool_name,
17082
+ hookEvent: hookData.hook_event_name,
17083
+ cwd: hookData.cwd
17084
+ }
17085
+ },
17086
+ "Hook data validated"
17087
+ );
16878
17088
  try {
16879
17089
  return await processTranscript(hookData, sessionStore, log2);
16880
17090
  } finally {
17091
+ sessionStore.delete(ACTIVE_KEY);
16881
17092
  log2.flushLogs();
16882
17093
  }
16883
17094
  }
16884
17095
  async function processTranscript(hookData, sessionStore, log2) {
16885
17096
  const cursorKey = getCursorKey(hookData.transcript_path);
16886
- const { entries: rawEntries, fileSize } = await readNewTranscriptEntries(
17097
+ const { entries: rawEntries, endByteOffset } = await readNewTranscriptEntries(
16887
17098
  hookData.transcript_path,
16888
17099
  hookData.session_id,
16889
17100
  sessionStore
@@ -16894,10 +17105,10 @@ async function processTranscript(hookData, sessionStore, log2) {
16894
17105
  }
16895
17106
  const { filtered: entries, filteredOut } = filterEntries(rawEntries);
16896
17107
  if (filteredOut > 0) {
16897
- log2.info("Filtered out noise entries", {
16898
- filteredOut,
16899
- remaining: entries.length
16900
- });
17108
+ log2.info(
17109
+ { data: { filteredOut, remaining: entries.length } },
17110
+ "Filtered out noise entries"
17111
+ );
16901
17112
  }
16902
17113
  if (entries.length === 0) {
16903
17114
  log2.info("All entries filtered out, nothing to upload");
@@ -16905,7 +17116,7 @@ async function processTranscript(hookData, sessionStore, log2) {
16905
17116
  const prevCursor = sessionStore.get(cursorKey);
16906
17117
  const cursor = {
16907
17118
  id: lastEntry._recordId,
16908
- byteOffset: fileSize,
17119
+ byteOffset: endByteOffset,
16909
17120
  updatedAt: Date.now(),
16910
17121
  lastModel: prevCursor?.lastModel
16911
17122
  };
@@ -16916,10 +17127,32 @@ async function processTranscript(hookData, sessionStore, log2) {
16916
17127
  errors: 0
16917
17128
  };
16918
17129
  }
16919
- const gqlClient = await log2.timed(
16920
- "GQL auth",
16921
- () => getAuthenticatedGQLClient({ isSkipPrompts: true })
16922
- );
17130
+ let gqlClient;
17131
+ try {
17132
+ gqlClient = await log2.timed(
17133
+ "GQL auth",
17134
+ () => withTimeout(
17135
+ getAuthenticatedGQLClient({ isSkipPrompts: true }),
17136
+ GQL_AUTH_TIMEOUT_MS,
17137
+ "GQL auth"
17138
+ )
17139
+ );
17140
+ } catch (err) {
17141
+ log2.error(
17142
+ {
17143
+ data: {
17144
+ error: String(err),
17145
+ stack: err instanceof Error ? err.stack : void 0
17146
+ }
17147
+ },
17148
+ "GQL auth failed"
17149
+ );
17150
+ return {
17151
+ entriesUploaded: 0,
17152
+ entriesSkipped: filteredOut,
17153
+ errors: entries.length
17154
+ };
17155
+ }
16923
17156
  const cursorForModel = sessionStore.get(cursorKey);
16924
17157
  let lastSeenModel = cursorForModel?.lastModel ?? null;
16925
17158
  const records = entries.map((entry) => {
@@ -16943,12 +17176,17 @@ async function processTranscript(hookData, sessionStore, log2) {
16943
17176
  rawData: rawEntry
16944
17177
  };
16945
17178
  });
16946
- log2.info("Uploading batch", {
16947
- count: records.length,
16948
- skipped: filteredOut,
16949
- firstRecordId: records[0]?.recordId,
16950
- lastRecordId: records[records.length - 1]?.recordId
16951
- });
17179
+ log2.info(
17180
+ {
17181
+ data: {
17182
+ count: records.length,
17183
+ skipped: filteredOut,
17184
+ firstRecordId: records[0]?.recordId,
17185
+ lastRecordId: records[records.length - 1]?.recordId
17186
+ }
17187
+ },
17188
+ "Uploading batch"
17189
+ );
16952
17190
  const result = await log2.timed(
16953
17191
  "Batch upload",
16954
17192
  () => prepareAndSendTracyRecords(gqlClient, records, hookData.cwd)
@@ -16957,7 +17195,7 @@ async function processTranscript(hookData, sessionStore, log2) {
16957
17195
  const lastRawEntry = rawEntries[rawEntries.length - 1];
16958
17196
  const cursor = {
16959
17197
  id: lastRawEntry._recordId,
16960
- byteOffset: fileSize,
17198
+ byteOffset: endByteOffset,
16961
17199
  updatedAt: Date.now(),
16962
17200
  lastModel: lastSeenModel ?? void 0
16963
17201
  };
@@ -16972,7 +17210,10 @@ async function processTranscript(hookData, sessionStore, log2) {
16972
17210
  errors: 0
16973
17211
  };
16974
17212
  }
16975
- log2.error("Batch upload had errors", { errors: result.errors });
17213
+ log2.error(
17214
+ { data: { errors: result.errors, recordCount: entries.length } },
17215
+ "Batch upload had errors"
17216
+ );
16976
17217
  return {
16977
17218
  entriesUploaded: 0,
16978
17219
  entriesSkipped: filteredOut,
@@ -17115,33 +17356,48 @@ var claudeCodeProcessHookHandler = async () => {
17115
17356
  }
17116
17357
  }
17117
17358
  process.on("uncaughtException", (error) => {
17118
- hookLog.error("Uncaught exception in hook", {
17119
- error: String(error),
17120
- stack: error.stack
17121
- });
17359
+ hookLog.error(
17360
+ { data: { error: String(error), stack: error.stack } },
17361
+ "Uncaught exception in hook"
17362
+ );
17122
17363
  void flushAndExit(1);
17123
17364
  });
17124
17365
  process.on("unhandledRejection", (reason) => {
17125
- hookLog.error("Unhandled rejection in hook", {
17126
- error: String(reason),
17127
- stack: reason instanceof Error ? reason.stack : void 0
17128
- });
17366
+ hookLog.error(
17367
+ {
17368
+ data: {
17369
+ error: String(reason),
17370
+ stack: reason instanceof Error ? reason.stack : void 0
17371
+ }
17372
+ },
17373
+ "Unhandled rejection in hook"
17374
+ );
17129
17375
  void flushAndExit(1);
17130
17376
  });
17131
17377
  let exitCode = 0;
17132
17378
  try {
17133
17379
  const result = await processAndUploadTranscriptEntries();
17134
- hookLog.info("Claude Code upload complete", {
17135
- entriesUploaded: result.entriesUploaded,
17136
- entriesSkipped: result.entriesSkipped,
17137
- errors: result.errors
17138
- });
17380
+ hookLog.info(
17381
+ {
17382
+ data: {
17383
+ entriesUploaded: result.entriesUploaded,
17384
+ entriesSkipped: result.entriesSkipped,
17385
+ errors: result.errors
17386
+ }
17387
+ },
17388
+ "Claude Code upload complete"
17389
+ );
17139
17390
  } catch (error) {
17140
17391
  exitCode = 1;
17141
- hookLog.error("Failed to process Claude Code hook", {
17142
- error: String(error),
17143
- stack: error instanceof Error ? error.stack : void 0
17144
- });
17392
+ hookLog.error(
17393
+ {
17394
+ data: {
17395
+ error: String(error),
17396
+ stack: error instanceof Error ? error.stack : void 0
17397
+ }
17398
+ },
17399
+ "Failed to process Claude Code hook"
17400
+ );
17145
17401
  }
17146
17402
  await flushAndExit(exitCode);
17147
17403
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobbdev",
3
- "version": "1.2.56",
3
+ "version": "1.2.57",
4
4
  "description": "Automated secure code remediation tool",
5
5
  "repository": "git+https://github.com/mobb-dev/bugsy.git",
6
6
  "main": "dist/index.mjs",