lurqrun 0.1.7 → 0.1.8

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.
Files changed (122) hide show
  1. package/README.md +312 -148
  2. package/dist/{auditHandler-JCOJSUYW.js → auditHandler-VWCXSDVO.js} +12 -11
  3. package/dist/autopilotInit-Q62MX5M6.js +178 -0
  4. package/dist/bin/lurq.js +235 -83
  5. package/dist/{browserAuth-B6G27USY.js → browserAuth-J7APSD4P.js} +4 -3
  6. package/dist/{cache-DKI34SVS.js → cache-AEAKCV4I.js} +2 -2
  7. package/dist/check-2JTQDQA6.js +2613 -0
  8. package/dist/{check-2FDGUOV7.js → check-PQSQ2MMA.js} +1 -1
  9. package/dist/checkUpgrade-5YW77OBL.js +24 -0
  10. package/dist/{chunk-BCANRN5B.js → chunk-2GAYNIHG.js} +8 -1
  11. package/dist/{chunk-EA3DP5HG.js → chunk-2O6Z37IT.js} +155 -25
  12. package/dist/{chunk-J5FZYN4A.js → chunk-2WD4QAH4.js} +1 -1
  13. package/dist/{chunk-CUP36ND3.js → chunk-3676XOBU.js} +145 -36
  14. package/dist/chunk-3D2MPGOQ.js +181 -0
  15. package/dist/{chunk-LYMPYETA.js → chunk-4JHDSVIN.js} +270 -6
  16. package/dist/chunk-53SG6XWA.js +141 -0
  17. package/dist/chunk-5ABNDB3I.js +37 -0
  18. package/dist/chunk-5ALIJFDA.js +114 -0
  19. package/dist/{chunk-BULZAX2U.js → chunk-64ZF7KO4.js} +55 -38
  20. package/dist/{chunk-2VMOWSRY.js → chunk-6DM6UQFD.js} +19 -1
  21. package/dist/{chunk-HWBBVLOT.js → chunk-6L67QMCP.js} +1 -1
  22. package/dist/{chunk-GPTKSNR6.js → chunk-72Y2FDVS.js} +2 -1
  23. package/dist/{chunk-B43LCXBM.js → chunk-A3TYI2TF.js} +81 -16
  24. package/dist/{chunk-IQ4SDKKY.js → chunk-B2UUYRBX.js} +31 -228
  25. package/dist/{chunk-DB2VF5UD.js → chunk-BJT2LHAR.js} +16 -11
  26. package/dist/chunk-BM3LF5T6.js +132 -0
  27. package/dist/{chunk-3N3Q3DSW.js → chunk-BN3NM47Q.js} +8 -8
  28. package/dist/chunk-D3K6IZVR.js +12845 -0
  29. package/dist/{chunk-AHBP533R.js → chunk-D3VTZS7V.js} +7 -5
  30. package/dist/{chunk-UDVNA7EA.js → chunk-DM37MTOO.js} +161 -8
  31. package/dist/chunk-DONTW3NE.js +171 -0
  32. package/dist/{chunk-M6NYEXCW.js → chunk-DSROTZ3Z.js} +1 -1
  33. package/dist/{chunk-4F7ZESPO.js → chunk-EQCXENKZ.js} +3 -5
  34. package/dist/{chunk-CW5HJ3LX.js → chunk-EYAKM6YN.js} +1 -1
  35. package/dist/{chunk-VL2E5T2A.js → chunk-FDFPMBCA.js} +6 -3
  36. package/dist/{chunk-BV32UNLL.js → chunk-FVYFV5HA.js} +2 -2
  37. package/dist/{chunk-DWAGCNEL.js → chunk-GM5Y555O.js} +36 -103
  38. package/dist/{chunk-PSX3GIAN.js → chunk-HWFX4BC4.js} +2 -1
  39. package/dist/chunk-IFNONGRQ.js +603 -0
  40. package/dist/chunk-J3U2CPSK.js +136 -0
  41. package/dist/{chunk-Y5GI7RL3.js → chunk-J6XQ7OPZ.js} +9 -1
  42. package/dist/{chunk-LBFGSSF2.js → chunk-KMV4Y6R3.js} +2 -1
  43. package/dist/{chunk-L6N4JFW2.js → chunk-KP5ZDDPE.js} +81 -4
  44. package/dist/{chunk-SJOVFF7B.js → chunk-KYKSZ3XZ.js} +3 -2
  45. package/dist/chunk-NEZNAPPN.js +41 -0
  46. package/dist/{chunk-NH5I4KHH.js → chunk-NPVN2THM.js} +1 -5
  47. package/dist/{chunk-6L3U7DZ5.js → chunk-NYZU5NCW.js} +1 -1
  48. package/dist/chunk-O4KHCV6I.js +320 -0
  49. package/dist/{chunk-LO35HLQZ.js → chunk-ONDVTXUO.js} +25 -4
  50. package/dist/{chunk-4ZYCIMY4.js → chunk-OTJFHESY.js} +25 -4
  51. package/dist/chunk-P6MIKSPY.js +243 -0
  52. package/dist/{checkUpgrade-F2WESBPM.js → chunk-QCRBX3LV.js} +72 -10
  53. package/dist/{chunk-LRD4GBYE.js → chunk-QUIMQ7YL.js} +1 -1
  54. package/dist/chunk-RG3YLU6E.js +49 -0
  55. package/dist/{chunk-ST2X2GYN.js → chunk-SHZFOSXC.js} +109 -20
  56. package/dist/{chunk-CIDWNUAP.js → chunk-SOXFMMXC.js} +8 -2
  57. package/dist/chunk-TDTYV6QI.js +119 -0
  58. package/dist/{chunk-FW6BD2NO.js → chunk-TUXQ32WE.js} +457 -121
  59. package/dist/{chunk-WQ23REYT.js → chunk-VGCDRK3S.js} +1 -1
  60. package/dist/{chunk-U5AT3TD4.js → chunk-X4W64MHU.js} +1735 -1858
  61. package/dist/chunk-XBQA4I36.js +92 -0
  62. package/dist/chunk-XGOV6KV4.js +413 -0
  63. package/dist/{chunk-OQS4GUSI.js → chunk-XIFQIFSZ.js} +210 -661
  64. package/dist/{chunk-XBIDMHSK.js → chunk-XLXPVN3Y.js} +2 -34
  65. package/dist/chunk-ZJMLQ37G.js +90 -0
  66. package/dist/{client-IRKAMDYZ.js → client-MU4EUDZV.js} +3 -3
  67. package/dist/{commands-FIL5ET6P.js → commands-UOOTMBKA.js} +145 -33
  68. package/dist/{compat-R3UTFQ5Q.js → compat-Q5DVPITN.js} +6 -6
  69. package/dist/constants-DJFOFZ44.js +24 -0
  70. package/dist/{dashboardIssue-WYYMBFIB.js → dashboardIssue-KBKLQMGC.js} +105 -23
  71. package/dist/{diff-GZA2774U.js → diff-I4SNUHU3.js} +1 -1
  72. package/dist/domains-VRT4BRVZ.js +14 -0
  73. package/dist/env-UN6KGZ7S.js +15 -0
  74. package/dist/envCheck-KUPTORFQ.js +71 -0
  75. package/dist/{extract-NKGKLIVT.js → extract-6WG265BL.js} +14 -3
  76. package/dist/{fetch-QYTUJVG2.js → fetch-PJ3SC32M.js} +4 -4
  77. package/dist/{firstTouch-QQTJH4QX.js → firstTouch-COJCMOB3.js} +1 -1
  78. package/dist/fix-3EDWNKW3.js +220 -0
  79. package/dist/{handlers-GEJAVZDK.js → handlers-4Q37WIFT.js} +19 -17
  80. package/dist/hook-NA66XJLC.js +412 -0
  81. package/dist/{http-ETPYMBGD.js → http-25GJ6SKK.js} +2078 -510
  82. package/dist/{ingestQueue-VKN4KBIF.js → ingestQueue-FSVJC3AA.js} +14 -12
  83. package/dist/{install-EK3W2COX.js → install-G2HHXYJ2.js} +6 -6
  84. package/dist/{installSkill-VQJWJVCL.js → installSkill-5TIMYIJZ.js} +21 -3
  85. package/dist/manifest-ENMIC54B.js +13 -0
  86. package/dist/{mcp-3JHD5SUZ.js → mcp-PENLL2PK.js} +60 -32
  87. package/dist/mcpConfig-3LYPRY5Q.js +80 -0
  88. package/dist/{mcpHandlers-QFN4MCFL.js → mcpHandlers-PO7GENA7.js} +7 -7
  89. package/dist/{mcpScan-QZHWENIP.js → mcpScan-ULP626TQ.js} +306 -442
  90. package/dist/{mcpScanWorkflow-JPYCBGC4.js → mcpScanWorkflow-27O44FOE.js} +29 -12
  91. package/dist/{mcpStack-BYJCZNIV.js → mcpStack-ZNYIFXZ5.js} +1 -1
  92. package/dist/model-ASMZWLM7.js +214 -0
  93. package/dist/{packages-THVFMWFP.js → packages-RWF2LXST.js} +3 -5
  94. package/dist/{plan-V4OEI3J4.js → plan-SDILIDZT.js} +23 -21
  95. package/dist/{planView-JRXGCG2N.js → planView-ZAKRTGVS.js} +4 -1
  96. package/dist/{policy-3P6BGRDN.js → policy-JAGNMQJI.js} +5 -5
  97. package/dist/probe-3IK646WN.js +610 -0
  98. package/dist/publicUpgrades-WTBGLDIK.js +42 -0
  99. package/dist/{references-6N7VEPHT.js → references-I6Y4BSRR.js} +3 -1
  100. package/dist/{release-4U4ABNSJ.js → release-7IU2R4YG.js} +5 -5
  101. package/dist/{remote-YGCXYI4V.js → remote-FN6225AH.js} +13 -3
  102. package/dist/rename-OS6JYBZQ.js +204 -0
  103. package/dist/{reportRuns-CKQQYLY4.js → reportRuns-O4X7ZFTX.js} +15 -2
  104. package/dist/safeFetch-COERXGFN.js +19 -0
  105. package/dist/{sandbox-MEZLAODF.js → sandbox-SAPSRKEI.js} +4 -4
  106. package/dist/sarif-WLVJFJ7U.js +125 -0
  107. package/dist/server-OLTY3NYT.js +59 -0
  108. package/dist/{surface-V62GE554.js → surface-7MHE3RL7.js} +18 -16
  109. package/dist/{surface-YL6O67W3.js → surface-JS2XU3U3.js} +5 -3
  110. package/dist/{surfaceHandlers-X2IZKA3E.js → surfaceHandlers-4B6TMZ34.js} +5 -5
  111. package/dist/{typecheck-X7U5ELMR.js → typecheck-NGOKNAJY.js} +23 -9
  112. package/dist/{uninstall-P7RGBX6G.js → uninstall-I2IJP5JZ.js} +30 -4
  113. package/dist/{upgrade-NQ5IWFZB.js → upgrade-FS26DJGG.js} +86 -32
  114. package/dist/{upgradePlan-A4LRKLEG.js → upgradePlan-ZS5IOVMZ.js} +46 -11
  115. package/dist/upkeepHandler-VFGYXJKB.js +13 -0
  116. package/dist/{userConfig-Z3GQE776.js → userConfig-ICASHI47.js} +1 -1
  117. package/dist/{watch-DGFMWUDL.js → watch-OCNEUXG6.js} +24 -34
  118. package/package.json +3 -2
  119. package/templates/skill-instructions.md +22 -3
  120. package/dist/chunk-6AAEVQ7I.js +0 -6336
  121. package/dist/chunk-I2JUKN6Z.js +0 -24
  122. package/dist/server-KFUD5X57.js +0 -55
@@ -2,7 +2,7 @@ import { createRequire as __lurqCreateRequire } from 'node:module'; const requir
2
2
  import {
3
3
  PACKAGE_NAME,
4
4
  VERSION
5
- } from "./chunk-GPTKSNR6.js";
5
+ } from "./chunk-72Y2FDVS.js";
6
6
  import {
7
7
  init_esm_shims
8
8
  } from "./chunk-I3M2KJXB.js";
@@ -15,6 +15,10 @@ function cliSpec(version = VERSION) {
15
15
  return major === "0" ? `${PACKAGE_NAME}@${major}.${minor}` : `${PACKAGE_NAME}@${major}`;
16
16
  }
17
17
  var DEFAULT_CRON = "0 6 * * 1";
18
+ var SECURITY_CRON = "0 6 * * *";
19
+ function cronForScope(scope) {
20
+ return scope === "security" ? SECURITY_CRON : DEFAULT_CRON;
21
+ }
18
22
  function detectInstallCommand(lockfiles) {
19
23
  if (lockfiles.includes("pnpm-lock.yaml")) return "pnpm install --frozen-lockfile";
20
24
  if (lockfiles.includes("yarn.lock")) return "yarn install --frozen-lockfile";
@@ -26,13 +30,20 @@ function renderWorkflow(opts = {}) {
26
30
  const cron = opts.cron ?? DEFAULT_CRON;
27
31
  const install = opts.installCommand ?? "npm ci";
28
32
  const max = opts.maxUpgrades ?? 3;
29
- const mode = opts.armed ? "pr" : "comment";
33
+ const mode = opts.mode ?? (opts.armed ? "pr" : "comment");
30
34
  const autoMerge = opts.autoMerge ?? false;
31
35
  const cli = cliSpec();
36
+ const envCheck = opts.checkEnv === true ? ` - name: Check environment
37
+ run: |
38
+ npx -y ${cli} check-env . > lurq-env.txt
39
+ { echo '\`\`\`'; cat lurq-env.txt; echo '\`\`\`'; } >> "\${GITHUB_STEP_SUMMARY}"
40
+
41
+ ` : "";
32
42
  return `# Managed by lurq, https://lurq.run
33
43
  #
34
44
  # Keeps this repository's dependencies current and rewrites the call sites an
35
- # upgrade breaks. lurq itself has read-only access to your code; every write
45
+ # upgrade breaks. lurq has read-only access to your code, and may start this
46
+ # workflow when a dependency you declare ships a breaking release; every write
36
47
  # below is made by this workflow's own GITHUB_TOKEN, scoped by the permissions
37
48
  # block. Delete this file to turn the autopilot off.
38
49
  name: lurq upgrade
@@ -43,10 +54,15 @@ on:
43
54
  workflow_dispatch:
44
55
  inputs:
45
56
  mode:
46
- description: "comment = analyse only \xB7 pr = open pull requests"
47
- type: choice
48
- options: [comment, pr]
49
- default: ${mode}
57
+ # No default, and deliberately a string rather than a choice. GitHub
58
+ # applies an input default on EVERY dispatch, including the ones lurq
59
+ # sends when a new major lands \u2014 which would fill LURQ_MODE before the
60
+ # "Resolve mode" step could read this repo's dashboard setting, and
61
+ # silently pin every triggered run to whatever was baked in at
62
+ # generation time. Left empty, a dispatch behaves like a scheduled run.
63
+ description: "leave empty to use your dashboard setting; or comment / fix / pr"
64
+ type: string
65
+ required: false
50
66
 
51
67
  # The blast radius. \`contents: write\` permits pushing a BRANCH; branch
52
68
  # protection on your default branch is what stops anything landing unreviewed.
@@ -62,7 +78,11 @@ jobs:
62
78
  upgrade:
63
79
  runs-on: ubuntu-latest
64
80
  env:
65
- LURQ_MODE: \${{ inputs.mode || vars.LURQ_MODE || '${mode}' }}
81
+ # Empty when nothing explicit set it, which the "Resolve mode" step below
82
+ # then fills from this repository's dashboard setting. Precedence is
83
+ # explicit first: a dispatch input, then a repository variable, then the
84
+ # dashboard, then the mode baked in when this file was generated.
85
+ LURQ_MODE: \${{ inputs.mode || vars.LURQ_MODE || '' }}
66
86
  MAX_UPGRADES: "${max}"
67
87
  steps:
68
88
  - uses: actions/checkout@v6
@@ -78,6 +98,29 @@ jobs:
78
98
  LURQ_API_KEY: \${{ secrets.LURQ_API_KEY }}
79
99
  run: npx -y ${cli} upgrade-plan . --json > lurq-plan.json
80
100
 
101
+ # Without this the autopilot switch on the dashboard would only ever
102
+ # affect NEW installs: lurq is Contents:read-only and cannot rewrite this
103
+ # file or set a repository variable, so the mode is read at runtime from
104
+ # the plan above, which already carries this repository's policy.
105
+ #
106
+ # Skipped entirely when something explicit already set the mode, so a
107
+ # repository variable or a dispatch choice still wins. An older server or
108
+ # an unconnected repo sends no mode and the baked-in '${mode}' stands.
109
+ #
110
+ # The value is checked against the three it may be before it reaches the
111
+ # environment \u2014 it arrives over the network, and GITHUB_ENV is not the
112
+ # place to trust a response.
113
+ - name: Resolve mode
114
+ if: env.LURQ_MODE == ''
115
+ run: |
116
+ MODE=$(jq -r '.mode // empty' lurq-plan.json)
117
+ case "$MODE" in
118
+ pr|fix|comment) ;;
119
+ *) MODE='${mode}' ;;
120
+ esac
121
+ echo "LURQ_MODE=$MODE" >> "\${GITHUB_ENV}"
122
+ echo "Mode for this run: $MODE" >> "\${GITHUB_STEP_SUMMARY}"
123
+
81
124
  # 2. Narrow that to symbols THIS repo references. Runs entirely locally
82
125
  # against both versions' npm tarballs, no API key, no test suite.
83
126
  #
@@ -99,9 +142,10 @@ jobs:
99
142
  { echo '\`\`\`'; cat lurq-report.txt; echo '\`\`\`'; } > lurq-report.md
100
143
  cat lurq-report.md >> "\${GITHUB_STEP_SUMMARY}"
101
144
 
102
- # 3. Editing is opt-in. Until LURQ_MODE is 'pr', the job stops here having
103
- # changed nothing, the brief is in the run summary above, and no
104
- # Anthropic credential is needed to get this far.
145
+ ${envCheck} # 3. Editing is opt-in. In 'comment' the job stops here having changed
146
+ # nothing, with the brief in the run summary above. 'fix' and 'pr' both
147
+ # continue; only 'pr' needs an Anthropic credential, which is why the
148
+ # check below is gated on it alone and not on editing in general.
105
149
  - name: Check agent credentials
106
150
  if: env.LURQ_MODE == 'pr'
107
151
  env:
@@ -109,14 +153,27 @@ jobs:
109
153
  OAUTH_TOKEN: \${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
110
154
  run: |
111
155
  if [ -z "$API_KEY" ] && [ -z "$OAUTH_TOKEN" ]; then
112
- echo "::error::pr mode needs one of ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in this repository's secrets. A Claude Pro/Max subscription token works for the latter. Set LURQ_MODE=comment to run analysis only."
156
+ echo "::error::pr mode needs one of ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in this repository's secrets. A Claude Pro/Max subscription token works for the latter. Set LURQ_MODE=fix to still open pull requests using only the changes lurq can prove (no key needed), or LURQ_MODE=comment to run analysis only."
113
157
  exit 1
114
158
  fi
115
159
 
116
160
  - name: Install dependencies
117
- if: env.LURQ_MODE == 'pr'
161
+ if: env.LURQ_MODE == 'pr' || env.LURQ_MODE == 'fix'
118
162
  run: ${install}
119
163
 
164
+ # Everything the package itself proves, made WITHOUT a model: renamed call
165
+ # sites, and the range bump in every manifest declaring the dependency.
166
+ #
167
+ # After the install, not before: bumping package.json first fails \`npm ci\`
168
+ # outright with a lock-out-of-sync error, and the agent's own install below
169
+ # reconciles the lockfile afterwards. It takes the same cap the agent is
170
+ # given, so the two cannot disagree about blast radius, and it refuses
171
+ # multi-major upgrades and unplannable sequences \u2014 the same rules the
172
+ # prompt states below, applied by a rule instead of a model.
173
+ - name: Apply what needs no judgement
174
+ if: env.LURQ_MODE == 'pr' || env.LURQ_MODE == 'fix'
175
+ run: npx -y ${cli} fix . --plan lurq-plan.json --apply --max \${{ env.MAX_UPGRADES }}
176
+
120
177
  - name: Apply upgrades
121
178
  if: env.LURQ_MODE == 'pr'
122
179
  uses: anthropics/claude-code-action@v1
@@ -144,10 +201,17 @@ jobs:
144
201
  "sequenceNote" is present, the sequence could not be planned,
145
202
  treat that dependency as a migration and skip it.
146
203
 
204
+ A step before you has ALREADY APPLIED every change lurq could
205
+ prove: renamed call sites where the package itself proves the
206
+ replacement, and the range bump in every manifest for those
207
+ upgrades. Read the files before assuming any of it is undone \u2014 you
208
+ cannot run git, so the working tree is the only record. Your job is
209
+ what remains, and re-doing applied work risks reverting it.
210
+
147
211
  Take at most \${{ env.MAX_UPGRADES }} entries, hardest first:
148
212
 
149
213
  1. Bump the dependency's range in every manifest listed in
150
- "declaredIn".
214
+ "declaredIn", where that has not been done already.
151
215
  2. Run \`${install.split(" ")[0]} install\` so node_modules holds the
152
216
  TARGET version. Until you do, the package on disk is the old one
153
217
  and anything you read from it describes the API you are leaving.
@@ -185,7 +249,7 @@ jobs:
185
249
  # 4. The workflow does version control, never the model.
186
250
  - name: Open pull request
187
251
  id: pr
188
- if: env.LURQ_MODE == 'pr'
252
+ if: env.LURQ_MODE == 'pr' || env.LURQ_MODE == 'fix'
189
253
  uses: peter-evans/create-pull-request@v7
190
254
  with:
191
255
  branch: lurq/upgrades
@@ -204,7 +268,7 @@ var MERGE_STEP = `
204
268
  # once the repo's OWN required checks pass; lurq never makes that call.
205
269
  # Requires "Allow auto-merge" in repository settings.
206
270
  - name: Enable auto-merge
207
- if: env.LURQ_MODE == 'pr' && steps.pr.outputs.pull-request-number
271
+ if: (env.LURQ_MODE == 'pr' || env.LURQ_MODE == 'fix') && steps.pr.outputs.pull-request-number
208
272
  env:
209
273
  GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
210
274
  run: gh pr merge --auto --squash "\${{ steps.pr.outputs.pull-request-number }}"
@@ -217,6 +281,7 @@ function newFileUrl(fullName, branch, content) {
217
281
  export {
218
282
  WORKFLOW_PATH,
219
283
  cliSpec,
284
+ cronForScope,
220
285
  detectInstallCommand,
221
286
  renderWorkflow,
222
287
  newFileUrl
@@ -1,8 +1,19 @@
1
1
  import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ checkMcpStack,
4
+ isCrowded
5
+ } from "./chunk-FDFPMBCA.js";
2
6
  import {
3
7
  loadStored,
4
8
  rowsToSurface
5
- } from "./chunk-DB2VF5UD.js";
9
+ } from "./chunk-BJT2LHAR.js";
10
+ import {
11
+ listAccountDeployments
12
+ } from "./chunk-P6MIKSPY.js";
13
+ import {
14
+ MCP_TIER,
15
+ contractOf
16
+ } from "./chunk-6EQ3ARXA.js";
6
17
  import {
7
18
  fetchServerManifest,
8
19
  missingConfig
@@ -10,31 +21,18 @@ import {
10
21
  import {
11
22
  SEVERITY_RANK
12
23
  } from "./chunk-Q5VPF4WI.js";
13
- import {
14
- checkMcpStack,
15
- isCrowded
16
- } from "./chunk-VL2E5T2A.js";
17
- import {
18
- MCP_TIER,
19
- contractOf
20
- } from "./chunk-6EQ3ARXA.js";
21
- import {
22
- enqueueSurface
23
- } from "./chunk-LO35HLQZ.js";
24
24
  import {
25
25
  assessVerdict
26
26
  } from "./chunk-QSL6QN5X.js";
27
27
  import {
28
28
  queryVulnerableInstalls
29
- } from "./chunk-LRD4GBYE.js";
29
+ } from "./chunk-QUIMQ7YL.js";
30
+ import {
31
+ enqueueSurface
32
+ } from "./chunk-ONDVTXUO.js";
30
33
  import {
31
- mcpChangeEvents,
32
- mcpContracts,
33
- mcpDeployments,
34
- mcpObservations,
35
- mcpPublicReports,
36
34
  packages
37
- } from "./chunk-LYMPYETA.js";
35
+ } from "./chunk-4JHDSVIN.js";
38
36
  import {
39
37
  require_semver
40
38
  } from "./chunk-3GHJR7XR.js";
@@ -51,200 +49,6 @@ init_esm_shims();
51
49
 
52
50
  // src/audit/accountScans.ts
53
51
  init_esm_shims();
54
-
55
- // src/db/mcpScans.ts
56
- init_esm_shims();
57
- import { and, desc, eq, inArray, isNull, sql } from "drizzle-orm";
58
- async function putContract(db, row) {
59
- await db.insert(mcpContracts).values(row).onConflictDoNothing();
60
- }
61
- async function getContract(db, contentHash) {
62
- const [row] = await db.select().from(mcpContracts).where(eq(mcpContracts.contentHash, contentHash)).limit(1);
63
- return row ?? null;
64
- }
65
- async function countDeployments(db, ownerId) {
66
- const [row] = await db.select({ n: sql`count(*)::int` }).from(mcpDeployments).where(eq(mcpDeployments.ownerId, ownerId));
67
- return row?.n ?? 0;
68
- }
69
- async function deploymentExists(db, ownerId, serverKey, configFingerprint) {
70
- const [row] = await db.select({ id: mcpDeployments.id }).from(mcpDeployments).where(
71
- and(
72
- eq(mcpDeployments.ownerId, ownerId),
73
- eq(mcpDeployments.serverKey, serverKey),
74
- eq(mcpDeployments.configFingerprint, configFingerprint)
75
- )
76
- ).limit(1);
77
- return !!row;
78
- }
79
- async function recordScan(db, input, diff) {
80
- return db.transaction(async (tx) => {
81
- const now = /* @__PURE__ */ new Date();
82
- const inserted = await tx.insert(mcpDeployments).values({
83
- ownerId: input.ownerId,
84
- serverKey: input.serverKey,
85
- configFingerprint: input.configFingerprint,
86
- alias: input.alias,
87
- registry: input.registry,
88
- packageName: input.packageName,
89
- transport: input.transport,
90
- lastStatus: input.status,
91
- firstSeenAt: now,
92
- lastScannedAt: now
93
- }).onConflictDoNothing({
94
- target: [mcpDeployments.ownerId, mcpDeployments.serverKey, mcpDeployments.configFingerprint]
95
- }).returning({ id: mcpDeployments.id });
96
- const isFirst = inserted.length > 0;
97
- const [dep] = await tx.select().from(mcpDeployments).where(
98
- and(
99
- eq(mcpDeployments.ownerId, input.ownerId),
100
- eq(mcpDeployments.serverKey, input.serverKey),
101
- eq(mcpDeployments.configFingerprint, input.configFingerprint)
102
- )
103
- ).for("update").limit(1);
104
- if (!dep) throw new Error("deployment vanished inside its own transaction");
105
- const [last] = await tx.select().from(mcpObservations).where(eq(mcpObservations.deploymentId, dep.id)).orderBy(desc(mcpObservations.id)).limit(1);
106
- const same = last && last.status === input.status && last.contentHash === input.contentHash && last.serverVersion === input.serverVersion;
107
- if (same) {
108
- await tx.update(mcpObservations).set({ lastSeenAt: now, scanCount: sql`${mcpObservations.scanCount} + 1` }).where(eq(mcpObservations.id, last.id));
109
- } else {
110
- await tx.insert(mcpObservations).values({
111
- deploymentId: dep.id,
112
- ownerId: input.ownerId,
113
- status: input.status,
114
- contentHash: input.contentHash,
115
- serverVersion: input.serverVersion,
116
- error: input.error,
117
- source: input.source,
118
- firstSeenAt: now,
119
- lastSeenAt: now
120
- });
121
- }
122
- let change = isFirst ? "first" : "unchanged";
123
- let event = null;
124
- const moved = !isFirst && input.contentHash && dep.lastContentHash && input.contentHash !== dep.lastContentHash;
125
- if (moved) {
126
- const [previous] = await tx.select().from(mcpContracts).where(eq(mcpContracts.contentHash, dep.lastContentHash)).limit(1);
127
- if (previous) {
128
- const d = diff(previous);
129
- await tx.insert(mcpChangeEvents).values({
130
- deploymentId: dep.id,
131
- ownerId: input.ownerId,
132
- fromHash: dep.lastContentHash,
133
- toHash: input.contentHash,
134
- severity: d.severity,
135
- summary: d.summary.slice(0, 1e3),
136
- diff: d,
137
- createdAt: now
138
- }).onConflictDoUpdate({
139
- target: [mcpChangeEvents.deploymentId, mcpChangeEvents.fromHash, mcpChangeEvents.toHash],
140
- set: { createdAt: now, acknowledgedAt: null, severity: d.severity, summary: d.summary.slice(0, 1e3), diff: d }
141
- });
142
- change = "changed";
143
- event = { at: now, severity: d.severity, summary: d.summary, rugPull: d.rugPull };
144
- }
145
- } else if (!isFirst && dep.lastStatus !== input.status) {
146
- change = "status_changed";
147
- }
148
- await tx.update(mcpDeployments).set({
149
- alias: input.alias,
150
- transport: input.transport,
151
- packageName: input.packageName,
152
- lastStatus: input.status,
153
- lastError: input.error,
154
- lastScannedAt: now,
155
- ...input.serverName !== null ? { serverName: input.serverName } : {},
156
- ...input.serverVersion !== null ? { serverVersion: input.serverVersion } : {},
157
- // A failed scan keeps the last contract that was read: "the server was
158
- // unreachable today" must not erase what it exposed yesterday.
159
- ...input.contentHash ? { lastContentHash: input.contentHash, worstSeverity: input.worstSeverity } : {},
160
- ...moved || isFirst ? { lastChangedAt: now } : {}
161
- }).where(eq(mcpDeployments.id, dep.id));
162
- return { deploymentId: dep.id, change, event };
163
- });
164
- }
165
- async function reportPublic(db, row) {
166
- await db.insert(mcpPublicReports).values(row).onConflictDoNothing();
167
- const [agree] = await db.select({ n: sql`count(distinct ${mcpPublicReports.ownerId})::int` }).from(mcpPublicReports).where(
168
- and(
169
- eq(mcpPublicReports.registry, row.registry),
170
- eq(mcpPublicReports.packageName, row.packageName),
171
- eq(mcpPublicReports.version, row.version),
172
- eq(mcpPublicReports.contentHash, row.contentHash)
173
- )
174
- );
175
- return agree?.n ?? 0;
176
- }
177
- var RANK = { critical: 0, high: 1, moderate: 2, low: 3, info: 4 };
178
- async function listDeployments(db, ownerId) {
179
- const rows = await db.select({ d: mcpDeployments, c: { toolCount: mcpContracts.toolCount, analysis: mcpContracts.analysis } }).from(mcpDeployments).leftJoin(mcpContracts, eq(mcpContracts.contentHash, mcpDeployments.lastContentHash)).where(eq(mcpDeployments.ownerId, ownerId)).orderBy(desc(mcpDeployments.lastScannedAt)).limit(500);
180
- if (rows.length === 0) return [];
181
- const open = await db.select({ deploymentId: mcpChangeEvents.deploymentId, severity: mcpChangeEvents.severity }).from(mcpChangeEvents).where(
182
- and(
183
- eq(mcpChangeEvents.ownerId, ownerId),
184
- isNull(mcpChangeEvents.acknowledgedAt),
185
- inArray(
186
- mcpChangeEvents.deploymentId,
187
- rows.map((r) => r.d.id)
188
- )
189
- )
190
- );
191
- const byDep = /* @__PURE__ */ new Map();
192
- for (const e of open) {
193
- const cur = byDep.get(e.deploymentId) ?? { n: 0, worst: null };
194
- cur.n++;
195
- if (!cur.worst || RANK[e.severity] < RANK[cur.worst]) cur.worst = e.severity;
196
- byDep.set(e.deploymentId, cur);
197
- }
198
- return rows.map(({ d, c }) => ({
199
- ...d,
200
- toolCount: c?.toolCount ?? null,
201
- writes: c?.analysis?.stats.writes ?? null,
202
- destroys: c?.analysis?.stats.destroys ?? null,
203
- capabilities: c?.analysis?.stats.capabilities ?? {},
204
- findings: c?.analysis?.findings.filter((f) => f.severity !== "info").length ?? 0,
205
- openEvents: byDep.get(d.id)?.n ?? 0,
206
- openWorst: byDep.get(d.id)?.worst ?? null
207
- }));
208
- }
209
- async function getDeploymentDetail(db, ownerId, id) {
210
- const [deployment] = await db.select().from(mcpDeployments).where(and(eq(mcpDeployments.id, id), eq(mcpDeployments.ownerId, ownerId))).limit(1);
211
- if (!deployment) return null;
212
- const [contract, observations, events] = await Promise.all([
213
- deployment.lastContentHash ? getContract(db, deployment.lastContentHash) : Promise.resolve(null),
214
- db.select().from(mcpObservations).where(and(eq(mcpObservations.deploymentId, id), eq(mcpObservations.ownerId, ownerId))).orderBy(desc(mcpObservations.id)).limit(60),
215
- db.select().from(mcpChangeEvents).where(and(eq(mcpChangeEvents.deploymentId, id), eq(mcpChangeEvents.ownerId, ownerId))).orderBy(desc(mcpChangeEvents.createdAt)).limit(50)
216
- ]);
217
- return { deployment, contract, observations, events };
218
- }
219
- async function listChangeEvents(db, ownerId, opts = {}) {
220
- const rows = await db.select({ e: mcpChangeEvents, alias: mcpDeployments.alias, serverKey: mcpDeployments.serverKey }).from(mcpChangeEvents).innerJoin(mcpDeployments, eq(mcpDeployments.id, mcpChangeEvents.deploymentId)).where(
221
- and(eq(mcpChangeEvents.ownerId, ownerId), opts.openOnly ? isNull(mcpChangeEvents.acknowledgedAt) : void 0)
222
- ).orderBy(desc(mcpChangeEvents.createdAt)).limit(Math.min(opts.limit ?? 50, 200));
223
- return rows.map((r) => ({ ...r.e, alias: r.alias, serverKey: r.serverKey }));
224
- }
225
- async function acknowledgeEvent(db, ownerId, eventId) {
226
- const rows = await db.update(mcpChangeEvents).set({ acknowledgedAt: /* @__PURE__ */ new Date() }).where(and(eq(mcpChangeEvents.id, eventId), eq(mcpChangeEvents.ownerId, ownerId))).returning({ id: mcpChangeEvents.id });
227
- return rows.length > 0;
228
- }
229
- async function listAccountDeployments(db, ownerId) {
230
- const summaries = await listDeployments(db, ownerId);
231
- const hashes = [...new Set(summaries.map((s) => s.lastContentHash).filter((h) => !!h))];
232
- const analyses = hashes.length ? await db.select({ hash: mcpContracts.contentHash, analysis: mcpContracts.analysis }).from(mcpContracts).where(inArray(mcpContracts.contentHash, hashes)) : [];
233
- const byHash = new Map(analyses.map((a) => [a.hash, a.analysis]));
234
- return summaries.map((s) => ({
235
- serverKey: s.serverKey,
236
- alias: s.alias,
237
- lastStatus: s.lastStatus,
238
- lastError: s.lastError,
239
- lastContentHash: s.lastContentHash,
240
- lastScannedAt: s.lastScannedAt,
241
- analysis: s.lastContentHash ? byHash.get(s.lastContentHash) ?? null : null,
242
- openEvents: s.openEvents,
243
- openWorst: s.openWorst
244
- }));
245
- }
246
-
247
- // src/audit/accountScans.ts
248
52
  var STALE_AFTER_DAYS = 14;
249
53
  var MAX_TOOL_FINDINGS = 5;
250
54
  var DAY_MS = 864e5;
@@ -252,7 +56,8 @@ function matchDeployment(s, deployments) {
252
56
  const host = s.endpoint?.toLowerCase();
253
57
  const candidates = deployments.filter((d) => {
254
58
  if (s.packageName) return d.serverKey === `npm:${s.packageName}`;
255
- if (s.kind === "remote" && host) return d.serverKey === `remote:${host}` || d.serverKey.startsWith(`remote:${host}/`);
59
+ if (s.kind === "remote" && host)
60
+ return d.serverKey === `remote:${host}` || d.serverKey.startsWith(`remote:${host}/`);
256
61
  return d.alias === s.alias;
257
62
  });
258
63
  return candidates.sort((a, b) => b.lastScannedAt.getTime() - a.lastScannedAt.getTime())[0] ?? null;
@@ -281,7 +86,9 @@ function enrich(item, d, now) {
281
86
  if (item.status !== "answered") {
282
87
  item.status = "answered";
283
88
  delete item.skipReason;
284
- item.findings = item.findings.filter((f) => !(f.kind === "contract-drift" && f.severity === "info"));
89
+ item.findings = item.findings.filter(
90
+ (f) => !(f.kind === "contract-drift" && f.severity === "info")
91
+ );
285
92
  }
286
93
  if (age > STALE_AFTER_DAYS) {
287
94
  item.findings.push({
@@ -352,7 +159,7 @@ async function withAccountScans(db, ownerId, report, servers) {
352
159
  // src/audit/assess.ts
353
160
  init_esm_shims();
354
161
  var import_semver = __toESM(require_semver(), 1);
355
- import { inArray as inArray2 } from "drizzle-orm";
162
+ import { inArray } from "drizzle-orm";
356
163
  var NAME_CHUNK = 500;
357
164
  async function loadIndexed(db, names) {
358
165
  const out = /* @__PURE__ */ new Map();
@@ -363,7 +170,7 @@ async function loadIndexed(db, names) {
363
170
  latestVersion: packages.latestVersion,
364
171
  deprecated: packages.deprecated,
365
172
  advisories: packages.advisories
366
- }).from(packages).where(inArray2(packages.name, chunk));
173
+ }).from(packages).where(inArray(packages.name, chunk));
367
174
  for (const r of rows) out.set(r.name, r);
368
175
  }
369
176
  return out;
@@ -669,7 +476,7 @@ async function queueUnknown(db, report, ownerId = null) {
669
476
  const names = report.items.filter((i) => i.unit !== "transitive" && i.skipReason === "not-in-index").map((i) => i.name);
670
477
  if (names.length === 0) return 0;
671
478
  try {
672
- const { enqueueIngest } = await import("./ingestQueue-VKN4KBIF.js");
479
+ const { enqueueIngest } = await import("./ingestQueue-FSVJC3AA.js");
673
480
  for (const name of names) enqueueIngest(db, name, ownerId);
674
481
  } catch (err) {
675
482
  logger.debug(`audit: untracked-dep enqueue failed: ${String(err)}`);
@@ -714,20 +521,16 @@ async function handleAudit(db, input, ownerId = null) {
714
521
  filesRead: [],
715
522
  notes
716
523
  };
717
- const report = await withAccountScans(db, ownerId, await assessInventory(db, inv), inv.mcpServers);
524
+ const report = await withAccountScans(
525
+ db,
526
+ ownerId,
527
+ await assessInventory(db, inv),
528
+ inv.mcpServers
529
+ );
718
530
  await queueUnknown(db, report);
719
531
  return { ...report, root: null };
720
532
  }
721
533
 
722
534
  export {
723
- putContract,
724
- countDeployments,
725
- deploymentExists,
726
- recordScan,
727
- reportPublic,
728
- listDeployments,
729
- getDeploymentDetail,
730
- listChangeEvents,
731
- acknowledgeEvent,
732
535
  handleAudit
733
536
  };
@@ -7,17 +7,17 @@ import {
7
7
  enqueueSurface,
8
8
  mcpSurfaceRef,
9
9
  surfaceRef
10
- } from "./chunk-LO35HLQZ.js";
11
- import {
12
- cached
13
- } from "./chunk-HWBBVLOT.js";
10
+ } from "./chunk-ONDVTXUO.js";
14
11
  import {
15
12
  UNBOUNDED_ARITY,
16
13
  claims,
17
14
  entities,
18
15
  observations,
19
16
  symbols
20
- } from "./chunk-LYMPYETA.js";
17
+ } from "./chunk-4JHDSVIN.js";
18
+ import {
19
+ cached
20
+ } from "./chunk-6L67QMCP.js";
21
21
  import {
22
22
  logger
23
23
  } from "./chunk-MYGSLK44.js";
@@ -65,7 +65,9 @@ async function drain() {
65
65
  const updated = await job.fill(job.db, job.pkg, job.version, job.entityId);
66
66
  logger.info(`surface fill ${key}: ${updated} symbol row(s) updated`);
67
67
  } catch (err) {
68
- logger.warn(`surface fill failed for ${key}: ${err instanceof Error ? err.message : String(err)}`);
68
+ logger.warn(
69
+ `surface fill failed for ${key}: ${err instanceof Error ? err.message : String(err)}`
70
+ );
69
71
  } finally {
70
72
  pending.delete(key);
71
73
  }
@@ -73,10 +75,12 @@ async function drain() {
73
75
  }
74
76
  }
75
77
  async function fillOne(db, pkg, version, entityId) {
76
- const { fetchAndExtract } = await import("./fetch-QYTUJVG2.js");
78
+ const { fetchAndExtract } = await import("./fetch-PJ3SC32M.js");
77
79
  const fetched = await fetchAndExtract(pkg, version);
78
80
  if (!fetched) return 0;
79
- const values = fetched.surface.symbols.filter((s) => s.tier === TIER && (s.sourceRef?.offset !== void 0 || s.maxArity !== void 0)).map((s) => ({
81
+ const values = fetched.surface.symbols.filter(
82
+ (s) => s.tier === TIER && (s.sourceRef?.offset !== void 0 || s.maxArity !== void 0)
83
+ ).map((s) => ({
80
84
  path: s.path,
81
85
  offset: s.sourceRef?.offset ?? null,
82
86
  maxArity: s.maxArity === void 0 ? null : s.maxArity ?? UNBOUNDED_ARITY
@@ -189,7 +193,7 @@ async function resolveSurfaceUncached(db, input) {
189
193
  const version = input.version ?? null;
190
194
  let stored = await loadStored(db, input.package, version);
191
195
  if (!stored || stored.verdict === "unknown" && stored.rows.length === 0) {
192
- const { firstTouchSurface } = await import("./firstTouch-QQTJH4QX.js");
196
+ const { firstTouchSurface } = await import("./firstTouch-COJCMOB3.js");
193
197
  if (await firstTouchSurface(db, input.package, version)) {
194
198
  stored = await loadStored(db, input.package, version);
195
199
  }
@@ -244,7 +248,7 @@ async function diffSurfaceUncached(db, input, onFill) {
244
248
  loadStored(db, input.package, input.toVersion)
245
249
  ]);
246
250
  if (!a || !b || a.rows.length === 0 || b.rows.length === 0) {
247
- const { firstTouchPair } = await import("./firstTouch-QQTJH4QX.js");
251
+ const { firstTouchPair } = await import("./firstTouch-COJCMOB3.js");
248
252
  if (await firstTouchPair(db, input.package, input.fromVersion, input.toVersion)) {
249
253
  [a, b] = await Promise.all([
250
254
  loadStored(db, input.package, input.fromVersion),
@@ -279,7 +283,8 @@ async function diffSurfaceUncached(db, input, onFill) {
279
283
  [a, input.fromVersion],
280
284
  [b, input.toVersion]
281
285
  ]) {
282
- if (needsFill(stored.rows) && scheduleFill(db, input.package, version, stored.entityId)) onFill();
286
+ if (needsFill(stored.rows) && scheduleFill(db, input.package, version, stored.entityId))
287
+ onFill();
283
288
  }
284
289
  const diff = diffSurfaces(
285
290
  rowsToSurface(input.package, input.fromVersion, a.rows, a.tier ?? "shipped_js_ast"),