nexus-agents 9.4.1 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ast-rule-runner-5U6TE6YJ.js → ast-rule-runner-AAOBENF2.js} +3 -3
- package/dist/{child-mcp-config-IR7LRMIK.js → child-mcp-config-FRWGQIHP.js} +2 -2
- package/dist/{chunk-DG2UY5QU.js → chunk-2VD5OQQR.js} +2 -2
- package/dist/{chunk-72IBCL2X.js → chunk-33C22IY4.js} +3 -3
- package/dist/{chunk-FTUBCD52.js → chunk-52Q72CPP.js} +3 -3
- package/dist/{chunk-L34WFGDZ.js → chunk-AMK4BTAV.js} +3 -3
- package/dist/{chunk-L34WFGDZ.js.map → chunk-AMK4BTAV.js.map} +1 -1
- package/dist/{chunk-Q7RFJSR5.js → chunk-BJLP4E6Q.js} +3 -3
- package/dist/{chunk-Q5F4OBOK.js → chunk-BK6Q3VQK.js} +6 -42
- package/dist/chunk-BK6Q3VQK.js.map +1 -0
- package/dist/{chunk-OCKU3I4A.js → chunk-C6MM6Z65.js} +728 -54
- package/dist/chunk-C6MM6Z65.js.map +1 -0
- package/dist/{chunk-FWSAYHGV.js → chunk-EGUISJBB.js} +4 -4
- package/dist/{chunk-IU7CN5UQ.js → chunk-HQWQ7MR5.js} +5 -5
- package/dist/{chunk-XJ65J3O5.js → chunk-HVZVVDFN.js} +10 -6
- package/dist/chunk-HVZVVDFN.js.map +1 -0
- package/dist/{chunk-2AW2VHMK.js → chunk-IRFKJ2KR.js} +2 -2
- package/dist/{chunk-B5YROFD2.js → chunk-IYEZVAJT.js} +2506 -3219
- package/dist/chunk-IYEZVAJT.js.map +1 -0
- package/dist/{chunk-HK4LBUYY.js → chunk-K6M2ZWX5.js} +2 -2
- package/dist/{chunk-RI4IXOZT.js → chunk-KPGWNBDA.js} +2 -2
- package/dist/{chunk-6WPXQTQY.js → chunk-L4P4A7Z2.js} +2 -2
- package/dist/{chunk-P7327767.js → chunk-M4IJ3H6M.js} +2 -2
- package/dist/{chunk-FKMBZ53Y.js → chunk-MCWIMVG2.js} +2 -2
- package/dist/{chunk-JNRDNE26.js → chunk-MYM2TPNL.js} +2 -2
- package/dist/{chunk-TMHCN5EY.js → chunk-NUXSGWDS.js} +5 -5
- package/dist/{chunk-ONYCBTOH.js → chunk-O3VPGING.js} +11 -15
- package/dist/chunk-O3VPGING.js.map +1 -0
- package/dist/{chunk-AFAM6GK3.js → chunk-O46IOND3.js} +7 -7
- package/dist/{chunk-DYTB2XW2.js → chunk-O5OUJ3PX.js} +5 -5
- package/dist/{chunk-VQGHQVD2.js → chunk-QPU7QKPT.js} +3 -3
- package/dist/{chunk-DEQRRCHK.js → chunk-RBFHY2LF.js} +2 -2
- package/dist/{chunk-H4ACPMJS.js → chunk-SCQ5YAYO.js} +4 -4
- package/dist/{chunk-PFGLZZ7E.js → chunk-TLITBKMZ.js} +13 -13
- package/dist/{chunk-ECJ52NSK.js → chunk-TYLE7Z2S.js} +5 -3
- package/dist/{chunk-ECJ52NSK.js.map → chunk-TYLE7Z2S.js.map} +1 -1
- package/dist/{chunk-TFQMVUWQ.js → chunk-V5FGXVSX.js} +5 -7
- package/dist/{chunk-TFQMVUWQ.js.map → chunk-V5FGXVSX.js.map} +1 -1
- package/dist/{chunk-2AN5NGCQ.js → chunk-VGCRYAEH.js} +4 -4
- package/dist/{chunk-AYBOQE2Y.js → chunk-VHOOJ4FG.js} +3 -3
- package/dist/{chunk-AYBOQE2Y.js.map → chunk-VHOOJ4FG.js.map} +1 -1
- package/dist/{chunk-6H4NUU7G.js → chunk-WCHN7B2Q.js} +6 -20
- package/dist/{chunk-6H4NUU7G.js.map → chunk-WCHN7B2Q.js.map} +1 -1
- package/dist/{chunk-7XJZTSHW.js → chunk-WI73OZAU.js} +22 -125
- package/dist/chunk-WI73OZAU.js.map +1 -0
- package/dist/{chunk-EPCR2GOE.js → chunk-XFTTKOHK.js} +2 -2
- package/dist/{chunk-C5WECFBR.js → chunk-XQMQGUJT.js} +5 -5
- package/dist/{chunk-PRX4ERTK.js → chunk-XSAUVC6D.js} +2 -2
- package/dist/{chunk-6Z3MAHAC.js → chunk-XYYXVJSO.js} +2 -2
- package/dist/{chunk-HESQ746B.js → chunk-YHY7GMAL.js} +2 -2
- package/dist/{cli-circuit-breaker-AZHN2TMH.js → cli-circuit-breaker-AM425BRL.js} +3 -3
- package/dist/{cli-readiness-H4JTTKKU.js → cli-readiness-LWOMHZ34.js} +7 -7
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +55 -57
- package/dist/cli.js.map +1 -1
- package/dist/{composite-router-WCTNVMSX.js → composite-router-VXQFXNET.js} +2 -2
- package/dist/{consensus-vote-5PNCR3FT.js → consensus-vote-V2JQHILD.js} +19 -19
- package/dist/{consensus-vote-types-G7Lnj3B3.d.ts → consensus-vote-types-DexdhOdB.d.ts} +9 -15
- package/dist/{context-retriever-YM2PPNSY.js → context-retriever-GEQIPCVL.js} +8 -8
- package/dist/{doctor-deep-6P23G7PN.js → doctor-deep-DSOZGB5J.js} +3 -3
- package/dist/doctor-gateway-report-RJOSOHGA.js +21 -0
- package/dist/{doctor-live-MYXZCBSP.js → doctor-live-7LVRGOS5.js} +13 -13
- package/dist/{expert-bridge-CXHYMQRL.js → expert-bridge-5A4ADUBO.js} +4 -4
- package/dist/{factory-KH2MJQ5U.js → factory-4RZLP7QV.js} +5 -5
- package/dist/{factory-3Z46455N.js → factory-UBAUIA2M.js} +12 -12
- package/dist/{improvement-review-SRQQBAGG.js → improvement-review-UIYSAI36.js} +7 -7
- package/dist/index.d.ts +40 -559
- package/dist/index.js +44 -92
- package/dist/index.js.map +1 -1
- package/dist/{init-opencode-WXRTDPFP.js → init-opencode-WUN3CTBM.js} +6 -6
- package/dist/{issue-triage-KF47ZHU7.js → issue-triage-VKEHVHO4.js} +6 -6
- package/dist/{model-config-helpers-ZH2QAFVR.js → model-config-helpers-GYPIIXOP.js} +2 -2
- package/dist/{openai-compat-adapter-PQ3JKWPZ.js → openai-compat-adapter-GSDOJ7TQ.js} +6 -6
- package/dist/{outcome-store-adapter-NTYEZXLH.js → outcome-store-adapter-PXT7AL6T.js} +2 -2
- package/dist/{pr-reviewer-helpers-AQNGDRQT.js → pr-reviewer-helpers-GUUT7G43.js} +4 -4
- package/dist/{registry-command-6EHIJ3PJ.js → registry-command-4G3WGKS5.js} +2 -2
- package/dist/{repo-security-plan-AP3QEWJ5.js → repo-security-plan-QO6ACTPZ.js} +3 -3
- package/dist/{research-helpers-synthesize-XE3RMFQT.js → research-helpers-synthesize-MPEJHPYR.js} +4 -4
- package/dist/{routing-memory-PBZIE5NX.js → routing-memory-VHQDJ6XZ.js} +2 -2
- package/dist/{session-memory-6NUK6ZAG.js → session-memory-M3CQFFL6.js} +3 -3
- package/dist/{setup-config-KEPU4NTT.js → setup-config-VGEBCEKX.js} +3 -3
- package/dist/{setup-custom-api-XY455LVS.js → setup-custom-api-DX7CNYKT.js} +5 -5
- package/dist/{setup-custom-api-XY455LVS.js.map → setup-custom-api-DX7CNYKT.js.map} +1 -1
- package/dist/setup-data-dir-75Z5PSSB.js +36 -0
- package/dist/{tool-memory-MO2KOJJ7.js → tool-memory-7QUPGCWV.js} +5 -5
- package/dist/{unified-registry-JE22THXP.js → unified-registry-CLZK3H64.js} +13 -13
- package/dist/{weather-report-TQ6KM2F2.js → weather-report-OKMXF73V.js} +2 -2
- package/package.json +3 -2
- package/dist/chunk-7XJZTSHW.js.map +0 -1
- package/dist/chunk-B5YROFD2.js.map +0 -1
- package/dist/chunk-OCKU3I4A.js.map +0 -1
- package/dist/chunk-ONYCBTOH.js.map +0 -1
- package/dist/chunk-Q5F4OBOK.js.map +0 -1
- package/dist/chunk-XJ65J3O5.js.map +0 -1
- package/dist/doctor-gateway-report-4TXHTWUM.js +0 -21
- package/dist/setup-data-dir-B6URVYUD.js +0 -36
- /package/dist/{ast-rule-runner-5U6TE6YJ.js.map → ast-rule-runner-AAOBENF2.js.map} +0 -0
- /package/dist/{child-mcp-config-IR7LRMIK.js.map → child-mcp-config-FRWGQIHP.js.map} +0 -0
- /package/dist/{chunk-DG2UY5QU.js.map → chunk-2VD5OQQR.js.map} +0 -0
- /package/dist/{chunk-72IBCL2X.js.map → chunk-33C22IY4.js.map} +0 -0
- /package/dist/{chunk-FTUBCD52.js.map → chunk-52Q72CPP.js.map} +0 -0
- /package/dist/{chunk-Q7RFJSR5.js.map → chunk-BJLP4E6Q.js.map} +0 -0
- /package/dist/{chunk-FWSAYHGV.js.map → chunk-EGUISJBB.js.map} +0 -0
- /package/dist/{chunk-IU7CN5UQ.js.map → chunk-HQWQ7MR5.js.map} +0 -0
- /package/dist/{chunk-2AW2VHMK.js.map → chunk-IRFKJ2KR.js.map} +0 -0
- /package/dist/{chunk-HK4LBUYY.js.map → chunk-K6M2ZWX5.js.map} +0 -0
- /package/dist/{chunk-RI4IXOZT.js.map → chunk-KPGWNBDA.js.map} +0 -0
- /package/dist/{chunk-6WPXQTQY.js.map → chunk-L4P4A7Z2.js.map} +0 -0
- /package/dist/{chunk-P7327767.js.map → chunk-M4IJ3H6M.js.map} +0 -0
- /package/dist/{chunk-FKMBZ53Y.js.map → chunk-MCWIMVG2.js.map} +0 -0
- /package/dist/{chunk-JNRDNE26.js.map → chunk-MYM2TPNL.js.map} +0 -0
- /package/dist/{chunk-TMHCN5EY.js.map → chunk-NUXSGWDS.js.map} +0 -0
- /package/dist/{chunk-AFAM6GK3.js.map → chunk-O46IOND3.js.map} +0 -0
- /package/dist/{chunk-DYTB2XW2.js.map → chunk-O5OUJ3PX.js.map} +0 -0
- /package/dist/{chunk-VQGHQVD2.js.map → chunk-QPU7QKPT.js.map} +0 -0
- /package/dist/{chunk-DEQRRCHK.js.map → chunk-RBFHY2LF.js.map} +0 -0
- /package/dist/{chunk-H4ACPMJS.js.map → chunk-SCQ5YAYO.js.map} +0 -0
- /package/dist/{chunk-PFGLZZ7E.js.map → chunk-TLITBKMZ.js.map} +0 -0
- /package/dist/{chunk-2AN5NGCQ.js.map → chunk-VGCRYAEH.js.map} +0 -0
- /package/dist/{chunk-EPCR2GOE.js.map → chunk-XFTTKOHK.js.map} +0 -0
- /package/dist/{chunk-C5WECFBR.js.map → chunk-XQMQGUJT.js.map} +0 -0
- /package/dist/{chunk-PRX4ERTK.js.map → chunk-XSAUVC6D.js.map} +0 -0
- /package/dist/{chunk-6Z3MAHAC.js.map → chunk-XYYXVJSO.js.map} +0 -0
- /package/dist/{chunk-HESQ746B.js.map → chunk-YHY7GMAL.js.map} +0 -0
- /package/dist/{cli-circuit-breaker-AZHN2TMH.js.map → cli-circuit-breaker-AM425BRL.js.map} +0 -0
- /package/dist/{cli-readiness-H4JTTKKU.js.map → cli-readiness-LWOMHZ34.js.map} +0 -0
- /package/dist/{composite-router-WCTNVMSX.js.map → composite-router-VXQFXNET.js.map} +0 -0
- /package/dist/{consensus-vote-5PNCR3FT.js.map → consensus-vote-V2JQHILD.js.map} +0 -0
- /package/dist/{context-retriever-YM2PPNSY.js.map → context-retriever-GEQIPCVL.js.map} +0 -0
- /package/dist/{doctor-deep-6P23G7PN.js.map → doctor-deep-DSOZGB5J.js.map} +0 -0
- /package/dist/{doctor-gateway-report-4TXHTWUM.js.map → doctor-gateway-report-RJOSOHGA.js.map} +0 -0
- /package/dist/{doctor-live-MYXZCBSP.js.map → doctor-live-7LVRGOS5.js.map} +0 -0
- /package/dist/{expert-bridge-CXHYMQRL.js.map → expert-bridge-5A4ADUBO.js.map} +0 -0
- /package/dist/{factory-3Z46455N.js.map → factory-4RZLP7QV.js.map} +0 -0
- /package/dist/{factory-KH2MJQ5U.js.map → factory-UBAUIA2M.js.map} +0 -0
- /package/dist/{improvement-review-SRQQBAGG.js.map → improvement-review-UIYSAI36.js.map} +0 -0
- /package/dist/{init-opencode-WXRTDPFP.js.map → init-opencode-WUN3CTBM.js.map} +0 -0
- /package/dist/{issue-triage-KF47ZHU7.js.map → issue-triage-VKEHVHO4.js.map} +0 -0
- /package/dist/{model-config-helpers-ZH2QAFVR.js.map → model-config-helpers-GYPIIXOP.js.map} +0 -0
- /package/dist/{openai-compat-adapter-PQ3JKWPZ.js.map → openai-compat-adapter-GSDOJ7TQ.js.map} +0 -0
- /package/dist/{outcome-store-adapter-NTYEZXLH.js.map → outcome-store-adapter-PXT7AL6T.js.map} +0 -0
- /package/dist/{pr-reviewer-helpers-AQNGDRQT.js.map → pr-reviewer-helpers-GUUT7G43.js.map} +0 -0
- /package/dist/{registry-command-6EHIJ3PJ.js.map → registry-command-4G3WGKS5.js.map} +0 -0
- /package/dist/{repo-security-plan-AP3QEWJ5.js.map → repo-security-plan-QO6ACTPZ.js.map} +0 -0
- /package/dist/{research-helpers-synthesize-XE3RMFQT.js.map → research-helpers-synthesize-MPEJHPYR.js.map} +0 -0
- /package/dist/{routing-memory-PBZIE5NX.js.map → routing-memory-VHQDJ6XZ.js.map} +0 -0
- /package/dist/{session-memory-6NUK6ZAG.js.map → session-memory-M3CQFFL6.js.map} +0 -0
- /package/dist/{setup-config-KEPU4NTT.js.map → setup-config-VGEBCEKX.js.map} +0 -0
- /package/dist/{setup-data-dir-B6URVYUD.js.map → setup-data-dir-75Z5PSSB.js.map} +0 -0
- /package/dist/{tool-memory-MO2KOJJ7.js.map → tool-memory-7QUPGCWV.js.map} +0 -0
- /package/dist/{unified-registry-JE22THXP.js.map → unified-registry-CLZK3H64.js.map} +0 -0
- /package/dist/{weather-report-TQ6KM2F2.js.map → weather-report-OKMXF73V.js.map} +0 -0
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
getBuiltInAstRulesPath,
|
|
10
10
|
loadRules,
|
|
11
11
|
runAstQaRules
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-XYYXVJSO.js";
|
|
13
|
+
import "./chunk-WI73OZAU.js";
|
|
14
14
|
import "./chunk-XBEKMUTO.js";
|
|
15
15
|
import "./chunk-UE7A2XHM.js";
|
|
16
16
|
import "./chunk-LWOX65CP.js";
|
|
@@ -28,4 +28,4 @@ export {
|
|
|
28
28
|
loadRules,
|
|
29
29
|
runAstQaRules
|
|
30
30
|
};
|
|
31
|
-
//# sourceMappingURL=ast-rule-runner-
|
|
31
|
+
//# sourceMappingURL=ast-rule-runner-AAOBENF2.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-JPFRRNRR.js";
|
|
4
4
|
import {
|
|
5
5
|
createLogger
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WI73OZAU.js";
|
|
7
7
|
import "./chunk-XBEKMUTO.js";
|
|
8
8
|
import "./chunk-UE7A2XHM.js";
|
|
9
9
|
import "./chunk-LWOX65CP.js";
|
|
@@ -75,4 +75,4 @@ async function generateMcpConfig(options) {
|
|
|
75
75
|
export {
|
|
76
76
|
generateMcpConfig
|
|
77
77
|
};
|
|
78
|
-
//# sourceMappingURL=child-mcp-config-
|
|
78
|
+
//# sourceMappingURL=child-mcp-config-FRWGQIHP.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
getAdaptiveBonus,
|
|
8
8
|
getOutcomeStore,
|
|
9
9
|
hasMeasuredCategory
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-WI73OZAU.js";
|
|
11
11
|
import {
|
|
12
12
|
TASK_CATEGORIES
|
|
13
13
|
} from "./chunk-4PN3UVZV.js";
|
|
@@ -150,4 +150,4 @@ export {
|
|
|
150
150
|
runDeepDiagnostics,
|
|
151
151
|
formatDeepDiagnostics
|
|
152
152
|
};
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-2VD5OQQR.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
toSdkCallback,
|
|
8
8
|
toolStructuredError,
|
|
9
9
|
wrapToolWithTimeout
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HQWQ7MR5.js";
|
|
11
11
|
import {
|
|
12
12
|
JsonlStore,
|
|
13
13
|
SECURITY_KEYWORDS,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
hasMeasuredCategory,
|
|
22
22
|
isDeclaredNeverDeprecate,
|
|
23
23
|
isRegisteredToolName
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-WI73OZAU.js";
|
|
25
25
|
import {
|
|
26
26
|
getNexusDataDir,
|
|
27
27
|
nexusDataPath
|
|
@@ -2431,4 +2431,4 @@ export {
|
|
|
2431
2431
|
runImprovementReview,
|
|
2432
2432
|
registerImprovementReviewTool
|
|
2433
2433
|
};
|
|
2434
|
-
//# sourceMappingURL=chunk-
|
|
2434
|
+
//# sourceMappingURL=chunk-33C22IY4.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
assessReputation,
|
|
6
6
|
sanitizeInput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MYM2TPNL.js";
|
|
8
8
|
import {
|
|
9
9
|
allOf
|
|
10
10
|
} from "./chunk-EZNIYU5T.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
err,
|
|
14
14
|
getTimeProvider,
|
|
15
15
|
ok
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WI73OZAU.js";
|
|
17
17
|
|
|
18
18
|
// src/dogfooding/pr-reviewer-helpers.ts
|
|
19
19
|
import { randomUUID } from "node:crypto";
|
|
@@ -477,4 +477,4 @@ export {
|
|
|
477
477
|
createFailedReview,
|
|
478
478
|
reviewPostingBlock
|
|
479
479
|
};
|
|
480
|
-
//# sourceMappingURL=chunk-
|
|
480
|
+
//# sourceMappingURL=chunk-52Q72CPP.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getTimeProvider
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WI73OZAU.js";
|
|
4
4
|
|
|
5
5
|
// src/version.ts
|
|
6
6
|
import semver from "semver";
|
|
7
|
-
var VERSION = true ? "
|
|
7
|
+
var VERSION = true ? "10.1.0" : "dev";
|
|
8
8
|
var NODE_ENGINE_RANGE = ">=24";
|
|
9
9
|
function isNodeVersionSupported(version) {
|
|
10
10
|
return semver.satisfies(version, NODE_ENGINE_RANGE);
|
|
@@ -106,4 +106,4 @@ export {
|
|
|
106
106
|
contextForLogging,
|
|
107
107
|
measuredTrustTier
|
|
108
108
|
};
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
109
|
+
//# sourceMappingURL=chunk-AMK4BTAV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/version.ts","../src/mcp/middleware/request-context.ts"],"sourcesContent":["/**\n * nexus-agents - Version constant\n *\n * Injected at build time via tsup define from package.json.\n * Do NOT edit VERSION manually — it is replaced during build.\n */\n\nimport semver from 'semver';\n\ndeclare const __NEXUS_VERSION__: string;\n\nexport const VERSION: string = typeof __NEXUS_VERSION__ !== 'undefined' ? __NEXUS_VERSION__ : 'dev';\n\n/** Supported Node.js versions. Pinned to package.json by a regression test. */\nexport const NODE_ENGINE_RANGE = '>=24';\n\n/** Returns whether a Node.js version satisfies the package engine requirement. */\nexport function isNodeVersionSupported(version: string): boolean {\n return semver.satisfies(version, NODE_ENGINE_RANGE);\n}\n","/**\n * nexus-agents/mcp - Request Context Middleware\n *\n * Provides request ID generation and caller context tracking for MCP tools.\n * (Source: Issue #185 Phase 1 - Request context & PolicyFirewall integration)\n *\n * @module mcp/middleware/request-context\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { randomBytes } from 'node:crypto';\nimport { getTimeProvider } from '../../core/index.js';\nimport type { TrustTier } from '../../security/trust-types.js';\n\n/**\n * Authenticated user information.\n * (Source: Issue #739 - MCP authentication)\n */\nexport interface AuthenticatedUser {\n /** Unique user/client identifier */\n readonly id: string;\n /** Human-readable name (optional) */\n readonly name?: string;\n /** Granted permissions/scopes (optional) */\n readonly permissions?: readonly string[];\n}\n\n/**\n * Caller identification for audit trails.\n */\nexport interface CallerInfo {\n /** Client identifier (e.g., 'claude-cli', 'gemini-cli') */\n readonly clientId?: string;\n /** User agent string if available */\n readonly userAgent?: string;\n /** Session ID for request correlation */\n readonly sessionId?: string;\n /** IP address or transport identifier */\n readonly transport?: string;\n /** Whether the request is authenticated (Issue #739) */\n readonly authenticated?: boolean;\n /** Authenticated user information (Issue #739) */\n readonly authenticatedUser?: AuthenticatedUser;\n}\n\n/**\n * Request context for MCP tool invocations.\n * Immutable once created.\n */\nexport interface RequestContext {\n /** Unique request identifier (format: req_<16 hex chars>) */\n readonly requestId: string;\n /** Timestamp when request was received (ISO 8601, ET) */\n readonly timestamp: string;\n /** Tool being invoked */\n readonly toolName: string;\n /** Caller information for audit */\n readonly caller: CallerInfo;\n /**\n * Trust tier for this request (Issue #828).\n * Derived from caller authentication state:\n * - '1' = Authenticated + known client, or stdio (local-only)\n * - '2' = Authenticated via network\n * - '3' = Unauthenticated network request\n * - '4' = Request with detected injection patterns (set by sanitizer)\n */\n readonly trustTier: TrustTier;\n /** Trace ID for distributed tracing correlation */\n readonly traceId?: string;\n /** Parent span ID if part of a larger trace */\n readonly parentSpanId?: string;\n}\n\n/**\n * Options for creating a request context.\n */\nexport interface CreateContextOptions {\n /** Tool name being invoked */\n toolName: string;\n /** Optional caller information */\n caller?: CallerInfo;\n /** Optional explicit trust tier override (defaults to derived from caller) */\n trustTier?: TrustTier;\n /** Optional trace ID for correlation */\n traceId?: string;\n /** Optional parent span ID */\n parentSpanId?: string;\n /**\n * Reuse this request id instead of minting a new one (#4981).\n *\n * Lets a nested layer join an outer layer's request identity while still\n * deriving its OWN caller, trust tier and timestamp. Adopting the outer\n * context object wholesale would discard those, which for a handler\n * configured with `callerInfo` silently downgrades the trust tier and the\n * audit actor.\n */\n inheritRequestId?: string;\n}\n\n/**\n * Generates a cryptographically secure request ID.\n * Format: req_<16 hex characters>\n *\n * @returns Unique request identifier\n */\nexport function generateRequestId(): string {\n const bytes = randomBytes(8);\n return `req_${bytes.toString('hex')}`;\n}\n\n/**\n * Generates a session ID for request correlation.\n * Format: sess_<12 hex characters>\n *\n * @returns Unique session identifier\n */\nexport function generateSessionId(): string {\n const bytes = randomBytes(6);\n return `sess_${bytes.toString('hex')}`;\n}\n\n/**\n * Formats timestamp in ISO 8601 format with ET timezone.\n * (Source: CLAUDE.md - Time Authority section)\n */\nfunction formatTimestamp(): string {\n const now = new Date(getTimeProvider().now());\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'America/New_York',\n timeZoneName: 'shortOffset',\n });\n const parts = formatter.formatToParts(now);\n const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? '-05:00';\n const base = now.toLocaleString('sv-SE', {\n timeZone: 'America/New_York',\n hour12: false,\n });\n return base.replace(' ', 'T') + offset.replace('GMT', '');\n}\n\n/**\n * Derives trust tier from caller authentication state (Issue #828).\n *\n * - Authenticated + known CLI client (claude, gemini, codex) → Tier 1\n * - stdio transport (local-only, no network) → Tier 1\n * - Authenticated via network → Tier 2\n * - Unauthenticated → Tier 3\n */\nexport function deriveTrustTier(caller: CallerInfo): TrustTier {\n const knownClients = ['claude-cli', 'gemini-cli', 'codex-cli'];\n\n if (caller.transport === 'stdio') return '1';\n\n if (caller.authenticated === true) {\n if (caller.clientId !== undefined && knownClients.includes(caller.clientId)) {\n return '1';\n }\n return '2';\n }\n\n return '3';\n}\n\n/**\n * The MCP transports this server can identify (#6795). Only stdio ships in\n * this tree; a transport the server cannot identify is not recorded at all, so\n * its requests stay on the authentication branch of {@link deriveTrustTier}.\n */\nexport type McpServerTransport = 'stdio';\n\n/**\n * The transport the server connected, recorded by `connectTransport` (#6795).\n * Process-wide because an MCP server process connects one transport, and the\n * request context is built per call by middleware that never sees it.\n */\nlet serverTransport: McpServerTransport | undefined;\n\n/**\n * Record the transport the server is connected over, or `undefined` to clear\n * it (an unidentified transport, a failed connect, or test cleanup).\n */\nexport function recordServerTransport(transport: McpServerTransport | undefined): void {\n serverTransport = transport;\n}\n\n/**\n * The caller information the server itself measured for every request: the\n * transport it arrived over (#6795). Empty when no transport was recorded, which\n * {@link measuredTrustTier} reports as unmeasured rather than as a tier.\n */\nexport function serverCallerInfo(): CallerInfo {\n return serverTransport === undefined ? {} : { transport: serverTransport };\n}\n\n/**\n * Creates an immutable request context for an MCP tool invocation.\n *\n * @param options - Context creation options\n * @returns Immutable request context\n */\nexport function createRequestContext(options: CreateContextOptions): RequestContext {\n const caller = options.caller ?? {};\n const context: RequestContext = {\n requestId: options.inheritRequestId ?? generateRequestId(),\n timestamp: formatTimestamp(),\n toolName: options.toolName,\n caller,\n trustTier: options.trustTier ?? deriveTrustTier(caller),\n ...(options.traceId !== undefined && { traceId: options.traceId }),\n ...(options.parentSpanId !== undefined && { parentSpanId: options.parentSpanId }),\n };\n\n // Freeze to ensure immutability\n return Object.freeze(context);\n}\n\n/**\n * The request context for the call currently in flight (#4981).\n *\n * Exists because a tool call passes through two middleware implementations\n * that each used to mint their own `RequestContext`, producing two unlinked\n * `req_*` ids and two `Tool invocation started` pairs per call. Argument\n * threading cannot fix that: `createSecureHandler` returns a 1-arity function,\n * so the chain's arity dispatch drops `ctx`, and 1-arity intermediaries such\n * as the `withPrerequisite` wrappers sit between the layers on some tools.\n * An ambient channel survives both.\n *\n * This mirrors the AbortSignal and progress-context storages the chain already\n * uses, so it is the established idiom here rather than a new mechanism.\n */\nconst requestContextStorage = new AsyncLocalStorage<ContextHolder>();\n\n/**\n * The ambient context plus its liveness.\n *\n * AsyncLocalStorage keeps a store reachable from anything spawned inside the\n * scope, including work that OUTLIVES the call — a `setTimeout`, or the\n * detached body `runAsJob` fires from inside the secure handler. Without this\n * flag such work still reads the context of a request that already completed,\n * and a nested handler for the same tool would adopt a finished identity, then\n * log neither a start (suppressed) nor a completion (the chain already closed\n * its own pair).\n *\n * Identity is not liveness, so the adoption gate needs both.\n */\ninterface ContextHolder {\n readonly context: RequestContext;\n settled: boolean;\n}\n\n/**\n * Runs `fn` with `context` as the ambient request context, so any nested layer\n * can adopt it instead of minting a second one.\n */\nexport function runWithRequestContext<T>(\n context: RequestContext,\n fn: () => Promise<T>\n): Promise<T> {\n const holder: ContextHolder = { context, settled: false };\n return requestContextStorage.run(holder, () =>\n fn().finally(() => {\n holder.settled = true;\n })\n );\n}\n\n/**\n * The ambient request context, or `undefined` when nothing established one.\n *\n * `undefined` is a real answer, not a failure: a caller that composes a secure\n * handler without the middleware chain — every direct test of it does — has no\n * outer context to inherit, and must mint its own.\n */\nexport function getCurrentRequestContext(): RequestContext | undefined {\n const holder = requestContextStorage.getStore();\n if (holder === undefined || holder.settled) return undefined;\n return holder.context;\n}\n\n/**\n * Formats request context for logging.\n * Extracts essential fields for log context.\n *\n * @param ctx - Request context\n * @returns Log-friendly context object\n */\nexport function contextForLogging(ctx: RequestContext): Record<string, unknown> {\n return {\n requestId: ctx.requestId,\n toolName: ctx.toolName,\n trustTier: ctx.trustTier,\n ...(ctx.caller.clientId !== undefined && { clientId: ctx.caller.clientId }),\n ...(ctx.traceId !== undefined && { traceId: ctx.traceId }),\n };\n}\n\n/**\n * Type guard to check if a value is a valid RequestContext.\n */\nexport function isRequestContext(value: unknown): value is RequestContext {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n return (\n typeof obj['requestId'] === 'string' &&\n obj['requestId'].startsWith('req_') &&\n typeof obj['timestamp'] === 'string' &&\n typeof obj['toolName'] === 'string' &&\n typeof obj['caller'] === 'object' &&\n typeof obj['trustTier'] === 'string' &&\n ['1', '2', '3', '4'].includes(obj['trustTier'])\n );\n}\n\n/**\n * The request's trust tier ONLY when it was actually derived from caller\n * information (#4733).\n *\n * `createRequestContext` falls back to `caller = {}`, and `deriveTrustTier({})`\n * returns `'3'` — so an absent caller is indistinguishable from a genuinely\n * untrusted one at the reading end. Since #6795 the middleware chain supplies\n * the transport the server connected ({@link serverCallerInfo}), so a stdio\n * server measures tier 1; a context built with no recorded transport and no\n * authentication fact is still that fallback.\n *\n * Returning `undefined` for the fallback lets a consumer record `unmeasured`\n * rather than a constant that reads as a measurement.\n *\n * NOTE this is caller AUTHENTICATION (transport / authenticated / clientId),\n * not content provenance. A trusted client can submit hostile content. Consumers\n * wanting provenance need a different signal — `classifyTrust` is the closest,\n * but it requires a GitHub actor and does not apply to a bare goal string.\n */\nexport function measuredTrustTier(context: RequestContext): string | undefined {\n // `caller` is always set by `createRequestContext` (to `{}` at minimum), but\n // a partially-constructed context can omit it — and an absent caller is the\n // same condition as an empty one: nothing was measured.\n const caller: unknown = context.caller;\n if (typeof caller !== 'object' || caller === null) return undefined;\n\n // Gate on the fields `deriveTrustTier` actually reads, not on \"the object has\n // any key at all\" (#4738 review). A caller carrying only `sessionId` or\n // `userAgent` feeds nothing into the derivation, so a non-empty check would\n // call the '3' fallback a measurement — reintroducing the constant this\n // function exists to prevent, in the function that prevents it.\n // `clientId` is NOT sufficient either: `deriveTrustTier` reads `clientId`\n // only INSIDE the `authenticated === true` branch, so\n // `{ clientId: 'claude-cli' }` returns the same '3' fallback as `{}`. (The\n // removed env-sniffing `extractCallerInfo` produced exactly that shape.)\n const info = caller as Partial<CallerInfo>;\n // `authenticated: false` counts: it is a measured fact (\"we checked, they\n // are not authenticated\"), which is different from the field being absent.\n const derivable = info.transport !== undefined || info.authenticated !== undefined;\n\n return derivable ? context.trustTier : undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/version.ts","../src/mcp/middleware/request-context.ts"],"sourcesContent":["/**\n * nexus-agents - Version constant\n *\n * Injected at build time via tsup define from package.json.\n * Do NOT edit VERSION manually — it is replaced during build.\n */\n\nimport semver from 'semver';\n\ndeclare const __NEXUS_VERSION__: string;\n\nexport const VERSION: string = typeof __NEXUS_VERSION__ !== 'undefined' ? __NEXUS_VERSION__ : 'dev';\n\n/** Supported Node.js versions. Pinned to package.json by a regression test. */\nexport const NODE_ENGINE_RANGE = '>=24';\n\n/** Returns whether a Node.js version satisfies the package engine requirement. */\nexport function isNodeVersionSupported(version: string): boolean {\n return semver.satisfies(version, NODE_ENGINE_RANGE);\n}\n","/**\n * nexus-agents/mcp - Request Context Middleware\n *\n * Provides request ID generation and caller context tracking for MCP tools.\n * (Source: Issue #185 Phase 1 - Request context & PolicyFirewall integration)\n *\n * @module mcp/middleware/request-context\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { randomBytes } from 'node:crypto';\nimport { getTimeProvider } from '../../core/index.js';\nimport type { TrustTier } from '../../security/trust-types.js';\n\n/**\n * Authenticated user information.\n * (Source: Issue #739 - MCP authentication)\n */\nexport interface AuthenticatedUser {\n /** Unique user/client identifier */\n readonly id: string;\n /** Human-readable name (optional) */\n readonly name?: string;\n /** Granted permissions/scopes (optional) */\n readonly permissions?: readonly string[];\n}\n\n/**\n * Caller identification for audit trails.\n */\nexport interface CallerInfo {\n /** Client identifier (e.g., 'claude-cli', 'gemini-cli') */\n readonly clientId?: string;\n /** User agent string if available */\n readonly userAgent?: string;\n /** Session ID for request correlation */\n readonly sessionId?: string;\n /** IP address or transport identifier */\n readonly transport?: string;\n /** Whether the request is authenticated (Issue #739) */\n readonly authenticated?: boolean;\n /** Authenticated user information (Issue #739) */\n readonly authenticatedUser?: AuthenticatedUser;\n}\n\n/**\n * Request context for MCP tool invocations.\n * Immutable once created.\n */\nexport interface RequestContext {\n /** Unique request identifier (format: req_<16 hex chars>) */\n readonly requestId: string;\n /** Timestamp when request was received (ISO 8601, ET) */\n readonly timestamp: string;\n /** Tool being invoked */\n readonly toolName: string;\n /** Caller information for audit */\n readonly caller: CallerInfo;\n /**\n * Trust tier for this request (Issue #828).\n * Derived from caller authentication state:\n * - '1' = Authenticated + known client, or stdio (local-only)\n * - '2' = Authenticated via network\n * - '3' = Unauthenticated network request\n * - '4' = Request with detected injection patterns (set by sanitizer)\n */\n readonly trustTier: TrustTier;\n /** Trace ID for distributed tracing correlation */\n readonly traceId?: string;\n /** Parent span ID if part of a larger trace */\n readonly parentSpanId?: string;\n}\n\n/**\n * Options for creating a request context.\n */\nexport interface CreateContextOptions {\n /** Tool name being invoked */\n toolName: string;\n /** Optional caller information */\n caller?: CallerInfo;\n /** Optional explicit trust tier override (defaults to derived from caller) */\n trustTier?: TrustTier;\n /** Optional trace ID for correlation */\n traceId?: string;\n /** Optional parent span ID */\n parentSpanId?: string;\n /**\n * Reuse this request id instead of minting a new one (#4981).\n *\n * Lets a nested layer join an outer layer's request identity while still\n * deriving its OWN caller, trust tier and timestamp. Adopting the outer\n * context object wholesale would discard those, which for a handler\n * configured with `callerInfo` silently downgrades the trust tier and the\n * audit actor.\n */\n inheritRequestId?: string;\n}\n\n/**\n * Generates a cryptographically secure request ID.\n * Format: req_<16 hex characters>\n *\n * @returns Unique request identifier\n */\nexport function generateRequestId(): string {\n const bytes = randomBytes(8);\n return `req_${bytes.toString('hex')}`;\n}\n\n/**\n * Generates a session ID for request correlation.\n * Format: sess_<12 hex characters>\n *\n * @returns Unique session identifier\n */\nexport function generateSessionId(): string {\n const bytes = randomBytes(6);\n return `sess_${bytes.toString('hex')}`;\n}\n\n/**\n * Formats timestamp in ISO 8601 format with ET timezone.\n * (Source: CLAUDE.md - Time Authority section)\n */\nfunction formatTimestamp(): string {\n const now = new Date(getTimeProvider().now());\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'America/New_York',\n timeZoneName: 'shortOffset',\n });\n const parts = formatter.formatToParts(now);\n const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? '-05:00';\n const base = now.toLocaleString('sv-SE', {\n timeZone: 'America/New_York',\n hour12: false,\n });\n return base.replace(' ', 'T') + offset.replace('GMT', '');\n}\n\n/**\n * Derives trust tier from caller authentication state (Issue #828).\n *\n * - Authenticated + known CLI client (claude, gemini, codex) → Tier 1\n * - stdio transport (local-only, no network) → Tier 1\n * - Authenticated via network → Tier 2\n * - Unauthenticated → Tier 3\n */\nexport function deriveTrustTier(caller: CallerInfo): TrustTier {\n const knownClients = ['claude-cli', 'gemini-cli', 'codex-cli'];\n\n if (caller.transport === 'stdio') return '1';\n\n if (caller.authenticated === true) {\n if (caller.clientId !== undefined && knownClients.includes(caller.clientId)) {\n return '1';\n }\n return '2';\n }\n\n return '3';\n}\n\n/**\n * The MCP transports this server can identify (#6795). Only stdio ships in\n * this tree; a transport the server cannot identify is not recorded at all, so\n * its requests stay on the authentication branch of {@link deriveTrustTier}.\n */\nexport type McpServerTransport = 'stdio';\n\n/**\n * The transport the server connected, recorded by `connectTransport` (#6795).\n * Process-wide because an MCP server process connects one transport, and the\n * request context is built per call by middleware that never sees it.\n */\nlet serverTransport: McpServerTransport | undefined;\n\n/**\n * Record the transport the server is connected over, or `undefined` to clear\n * it (an unidentified transport, a failed connect, or test cleanup).\n */\nexport function recordServerTransport(transport: McpServerTransport | undefined): void {\n serverTransport = transport;\n}\n\n/**\n * The caller information the server itself measured for every request: the\n * transport it arrived over (#6795). Empty when no transport was recorded, which\n * {@link measuredTrustTier} reports as unmeasured rather than as a tier.\n */\nexport function serverCallerInfo(): CallerInfo {\n return serverTransport === undefined ? {} : { transport: serverTransport };\n}\n\n/**\n * Creates an immutable request context for an MCP tool invocation.\n *\n * @param options - Context creation options\n * @returns Immutable request context\n */\nexport function createRequestContext(options: CreateContextOptions): RequestContext {\n const caller = options.caller ?? {};\n const context: RequestContext = {\n requestId: options.inheritRequestId ?? generateRequestId(),\n timestamp: formatTimestamp(),\n toolName: options.toolName,\n caller,\n trustTier: options.trustTier ?? deriveTrustTier(caller),\n ...(options.traceId !== undefined && { traceId: options.traceId }),\n ...(options.parentSpanId !== undefined && { parentSpanId: options.parentSpanId }),\n };\n\n // Freeze to ensure immutability\n return Object.freeze(context);\n}\n\n/**\n * The request context for the call currently in flight (#4981).\n *\n * Exists because a tool call passes through two middleware implementations\n * that each used to mint their own `RequestContext`, producing two unlinked\n * `req_*` ids and two `Tool invocation started` pairs per call. Argument\n * threading cannot fix that: `createSecureHandler` returns a 1-arity function,\n * so the chain's arity dispatch drops `ctx`, and 1-arity intermediaries such\n * as the `withPrerequisite` wrappers sit between the layers on some tools.\n * An ambient channel survives both.\n *\n * This mirrors the AbortSignal and progress-context storages the chain already\n * uses, so it is the established idiom here rather than a new mechanism.\n */\nconst requestContextStorage = new AsyncLocalStorage<ContextHolder>();\n\n/**\n * The ambient context plus its liveness.\n *\n * AsyncLocalStorage keeps a store reachable from anything spawned inside the\n * scope, including work that OUTLIVES the call — a `setTimeout`, or the\n * detached body `runAsJob` fires from inside the secure handler. Without this\n * flag such work still reads the context of a request that already completed,\n * and a nested handler for the same tool would adopt a finished identity, then\n * log neither a start (suppressed) nor a completion (the chain already closed\n * its own pair).\n *\n * Identity is not liveness, so the adoption gate needs both.\n */\ninterface ContextHolder {\n readonly context: RequestContext;\n settled: boolean;\n}\n\n/**\n * Runs `fn` with `context` as the ambient request context, so any nested layer\n * can adopt it instead of minting a second one.\n */\nexport function runWithRequestContext<T>(\n context: RequestContext,\n fn: () => Promise<T>\n): Promise<T> {\n const holder: ContextHolder = { context, settled: false };\n return requestContextStorage.run(holder, () =>\n fn().finally(() => {\n holder.settled = true;\n })\n );\n}\n\n/**\n * The ambient request context, or `undefined` when nothing established one.\n *\n * `undefined` is a real answer, not a failure: a caller that composes a secure\n * handler without the middleware chain — every direct test of it does — has no\n * outer context to inherit, and must mint its own.\n */\nexport function getCurrentRequestContext(): RequestContext | undefined {\n const holder = requestContextStorage.getStore();\n if (holder === undefined || holder.settled) return undefined;\n return holder.context;\n}\n\n/**\n * Formats request context for logging.\n * Extracts essential fields for log context.\n *\n * @param ctx - Request context\n * @returns Log-friendly context object\n */\nexport function contextForLogging(ctx: RequestContext): Record<string, unknown> {\n return {\n requestId: ctx.requestId,\n toolName: ctx.toolName,\n trustTier: ctx.trustTier,\n ...(ctx.caller.clientId !== undefined && { clientId: ctx.caller.clientId }),\n ...(ctx.traceId !== undefined && { traceId: ctx.traceId }),\n };\n}\n\n/**\n * Type guard to check if a value is a valid RequestContext.\n */\nexport function isRequestContext(value: unknown): value is RequestContext {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n return (\n typeof obj['requestId'] === 'string' &&\n obj['requestId'].startsWith('req_') &&\n typeof obj['timestamp'] === 'string' &&\n typeof obj['toolName'] === 'string' &&\n typeof obj['caller'] === 'object' &&\n typeof obj['trustTier'] === 'string' &&\n ['1', '2', '3', '4'].includes(obj['trustTier'])\n );\n}\n\n/**\n * The request's trust tier ONLY when it was actually derived from caller\n * information (#4733).\n *\n * `createRequestContext` falls back to `caller = {}`, and `deriveTrustTier({})`\n * returns `'3'` — so an absent caller is indistinguishable from a genuinely\n * untrusted one at the reading end. Since #6795 the middleware chain supplies\n * the transport the server connected ({@link serverCallerInfo}), so a stdio\n * server measures tier 1; a context built with no recorded transport and no\n * authentication fact is still that fallback.\n *\n * Returning `undefined` for the fallback lets a consumer record `unmeasured`\n * rather than a constant that reads as a measurement.\n *\n * NOTE this is caller AUTHENTICATION (transport / authenticated / clientId),\n * not content provenance. A trusted client can submit hostile content. Consumers\n * wanting provenance need a different signal — `classifyTrust` is the closest,\n * but it requires a GitHub actor and does not apply to a bare goal string.\n */\nexport function measuredTrustTier(context: RequestContext): string | undefined {\n // `caller` is always set by `createRequestContext` (to `{}` at minimum), but\n // a partially-constructed context can omit it — and an absent caller is the\n // same condition as an empty one: nothing was measured.\n const caller: unknown = context.caller;\n if (typeof caller !== 'object' || caller === null) return undefined;\n\n // Gate on the fields `deriveTrustTier` actually reads, not on \"the object has\n // any key at all\" (#4738 review). A caller carrying only `sessionId` or\n // `userAgent` feeds nothing into the derivation, so a non-empty check would\n // call the '3' fallback a measurement — reintroducing the constant this\n // function exists to prevent, in the function that prevents it.\n // `clientId` is NOT sufficient either: `deriveTrustTier` reads `clientId`\n // only INSIDE the `authenticated === true` branch, so\n // `{ clientId: 'claude-cli' }` returns the same '3' fallback as `{}`. (The\n // removed env-sniffing `extractCallerInfo` produced exactly that shape.)\n const info = caller as Partial<CallerInfo>;\n // `authenticated: false` counts: it is a measured fact (\"we checked, they\n // are not authenticated\"), which is different from the field being absent.\n const derivable = info.transport !== undefined || info.authenticated !== undefined;\n\n return derivable ? context.trustTier : undefined;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadPapersRegistry
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YHY7GMAL.js";
|
|
4
4
|
import {
|
|
5
5
|
getErrorMessage
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WI73OZAU.js";
|
|
7
7
|
|
|
8
8
|
// src/cli/research-helpers-synthesize.ts
|
|
9
9
|
import { z } from "zod";
|
|
@@ -876,4 +876,4 @@ export {
|
|
|
876
876
|
AttributedInsightSchema,
|
|
877
877
|
synthesizeResearch
|
|
878
878
|
};
|
|
879
|
-
//# sourceMappingURL=chunk-
|
|
879
|
+
//# sourceMappingURL=chunk-BJLP4E6Q.js.map
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CUSTOM_API_ALLOW_PRIVATE_ENV,
|
|
3
3
|
ConfigError,
|
|
4
|
-
DEPRECATED_GATEWAY_ENV_ALIASES,
|
|
5
4
|
OPENAI_COMPAT_KEY_ENV,
|
|
6
5
|
OPENAI_COMPAT_URL_ENV,
|
|
7
6
|
createLogger,
|
|
8
7
|
err,
|
|
9
8
|
ok
|
|
10
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-WI73OZAU.js";
|
|
11
10
|
|
|
12
11
|
// src/adapters/gateway-redaction.ts
|
|
13
12
|
var GATEWAY_REDACTED = "<redacted>";
|
|
@@ -22,46 +21,13 @@ function readTrimmed(env, name) {
|
|
|
22
21
|
return value === void 0 || value === "" ? void 0 : value;
|
|
23
22
|
}
|
|
24
23
|
function resolveGatewayEnv(env = process.env) {
|
|
25
|
-
const deprecated = [];
|
|
26
|
-
for (const alias of DEPRECATED_GATEWAY_ENV_ALIASES) {
|
|
27
|
-
if (readTrimmed(env, alias.deprecated) === void 0) continue;
|
|
28
|
-
deprecated.push({
|
|
29
|
-
name: alias.deprecated,
|
|
30
|
-
replacement: alias.replacement,
|
|
31
|
-
shadowed: readTrimmed(env, alias.replacement) !== void 0
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
24
|
return {
|
|
35
|
-
baseUrl:
|
|
36
|
-
apiKey:
|
|
37
|
-
deprecated
|
|
25
|
+
baseUrl: readTrimmed(env, OPENAI_COMPAT_URL_ENV),
|
|
26
|
+
apiKey: readTrimmed(env, OPENAI_COMPAT_KEY_ENV)
|
|
38
27
|
};
|
|
39
28
|
}
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
if (current !== void 0) return current;
|
|
43
|
-
const alias = DEPRECATED_GATEWAY_ENV_ALIASES.find((a) => a.replacement === replacement);
|
|
44
|
-
return alias === void 0 ? void 0 : readTrimmed(env, alias.deprecated);
|
|
45
|
-
}
|
|
46
|
-
var defaultLogger = createLogger({ component: "gateway-env" });
|
|
47
|
-
var deprecationWarned = false;
|
|
48
|
-
function warnDeprecatedGatewayEnvOnce(env = process.env, logger2 = defaultLogger) {
|
|
49
|
-
if (deprecationWarned) return;
|
|
50
|
-
const { deprecated } = resolveGatewayEnv(env);
|
|
51
|
-
if (deprecated.length === 0) return;
|
|
52
|
-
deprecationWarned = true;
|
|
53
|
-
logger2.warn(formatDeprecationWarning(deprecated));
|
|
54
|
-
}
|
|
55
|
-
function formatDeprecationWarning(deprecated) {
|
|
56
|
-
const uses = deprecated.map(
|
|
57
|
-
(d) => d.shadowed ? `${d.name} (ignored: ${d.replacement} is set)` : `${d.name} (honoured; rename to ${d.replacement})`
|
|
58
|
-
).join("; ");
|
|
59
|
-
return `Deprecated gateway environment variable(s) set (#4392): ${uses}. These names configure only the single-model custom-openai path and stay aliases until the next major (#6291). Rename to NEXUS_OPENAI_COMPAT_* to opt into the gateway path (model discovery, in-process voter transport, api:<endpoint> arm).`;
|
|
60
|
-
}
|
|
61
|
-
function readGatewayEnv(env = process.env, logger2 = defaultLogger) {
|
|
62
|
-
const resolved = resolveGatewayEnv(env);
|
|
63
|
-
if (resolved.deprecated.length > 0) warnDeprecatedGatewayEnvOnce(env, logger2);
|
|
64
|
-
return resolved;
|
|
29
|
+
function readGatewayEnv(env = process.env) {
|
|
30
|
+
return resolveGatewayEnv(env);
|
|
65
31
|
}
|
|
66
32
|
var CUSTOM_API_SURFACE_ENV = "NEXUS_CUSTOM_API_SURFACE";
|
|
67
33
|
function readCustomApiSurface(env = process.env) {
|
|
@@ -331,8 +297,6 @@ function classifyIPv6(ip) {
|
|
|
331
297
|
|
|
332
298
|
export {
|
|
333
299
|
redactGatewaySecrets,
|
|
334
|
-
resolveGatewayEnv,
|
|
335
|
-
warnDeprecatedGatewayEnvOnce,
|
|
336
300
|
readGatewayEnv,
|
|
337
301
|
readCustomApiSurface,
|
|
338
302
|
readDirectOpenAiSurface,
|
|
@@ -341,4 +305,4 @@ export {
|
|
|
341
305
|
validateCustomApiBaseUrl,
|
|
342
306
|
assertCustomApiHostResolvesPublic
|
|
343
307
|
};
|
|
344
|
-
//# sourceMappingURL=chunk-
|
|
308
|
+
//# sourceMappingURL=chunk-BK6Q3VQK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/gateway-redaction.ts","../src/adapters/sdk/gateway-env.ts","../src/adapters/sdk/custom-api-validation.ts"],"sourcesContent":["/**\n * Redaction of gateway credentials from error text.\n *\n * A gateway request carries the key (in `Authorization: Bearer` or in the\n * header `NEXUS_OPENAI_COMPAT_AUTH_HEADER` names) and every static header\n * from `NEXUS_OPENAI_COMPAT_EXTRA_HEADERS`. A gateway's error body may echo\n * any of them, and none has a vendor shape the pattern sanitizer knows, so\n * each is removed by exact match. One helper, so the discovery path, the\n * per-model adapter and `doctor --gateway` cannot disagree on what is secret.\n *\n * @module adapters/gateway-redaction\n */\n\n/** Replacement text for a redacted gateway credential. */\nconst GATEWAY_REDACTED = '<redacted>';\n\n/** The credentials one gateway call site sends. */\nexport interface GatewaySecretSource {\n /** The gateway key — also the auth-header value when a custom header carries it. */\n readonly apiKey?: string | undefined;\n /**\n * Headers sent on every request. EVERY value is treated as secret; a\n * `null` value removes a default header and carries nothing.\n */\n readonly headers?: Readonly<Record<string, string | null>> | undefined;\n}\n\n/**\n * `message` with the key and every header value replaced by `placeholder`.\n * Blank values are skipped (`replaceAll('')` would interleave the marker\n * between every character). Longer values are replaced first, so a value\n * that contains another is removed whole rather than leaving its remainder.\n */\nexport function redactGatewaySecrets(\n message: string,\n source: GatewaySecretSource,\n placeholder: string = GATEWAY_REDACTED\n): string {\n const secrets = [source.apiKey, ...Object.values(source.headers ?? {})]\n .filter((s): s is string => typeof s === 'string' && s.trim() !== '')\n .sort((a, b) => b.length - a.length);\n return secrets.reduce((text, secret) => text.replaceAll(secret, placeholder), message);\n}\n","/**\n * Canonical env resolver for the single-model `custom-openai` gateway path.\n *\n * The SDK path (`api:custom-openai`) and discovery path (`api:<endpoint>`)\n * both read `NEXUS_OPENAI_COMPAT_URL` / `NEXUS_OPENAI_COMPAT_KEY`.\n *\n * @module adapters/sdk/gateway-env\n */\n\nimport { ConfigError } from '../../core/index.js';\nimport { OPENAI_COMPAT_KEY_ENV, OPENAI_COMPAT_URL_ENV } from './types.js';\nimport { redactGatewaySecrets } from '../gateway-redaction.js';\n\n/** The resolved mechanism-A gateway pair. */\nexport interface GatewayEnv {\n /** `NEXUS_OPENAI_COMPAT_URL`, trimmed; empty is unset. */\n readonly baseUrl: string | undefined;\n /** `NEXUS_OPENAI_COMPAT_KEY`, trimmed; empty is unset. */\n readonly apiKey: string | undefined;\n}\n\n/** A set env value, trimmed; `undefined` for unset, empty, or whitespace-only. */\nfunction readTrimmed(env: NodeJS.ProcessEnv, name: string): string | undefined {\n const value = env[name]?.trim();\n return value === undefined || value === '' ? undefined : value;\n}\n\n/** Resolve the canonical mechanism-A gateway pair without logging. */\nexport function resolveGatewayEnv(env: NodeJS.ProcessEnv = process.env): GatewayEnv {\n return {\n baseUrl: readTrimmed(env, OPENAI_COMPAT_URL_ENV),\n apiKey: readTrimmed(env, OPENAI_COMPAT_KEY_ENV),\n };\n}\n\n/** The production reader for the canonical mechanism-A gateway pair. */\nexport function readGatewayEnv(env: NodeJS.ProcessEnv = process.env): GatewayEnv {\n return resolveGatewayEnv(env);\n}\n\n/**\n * The OpenAI API surface the single-model `custom-openai` path calls (#6645):\n * `chat` is `POST <base>/chat/completions`, `responses` is `POST <base>/responses`.\n */\ntype CustomApiSurface = 'chat' | 'responses';\n\nconst CUSTOM_API_SURFACE_ENV = 'NEXUS_CUSTOM_API_SURFACE';\n\n/**\n * Read `NEXUS_CUSTOM_API_SURFACE` (#6645). Unset or empty means `chat`: the\n * AI SDK's default is the Responses API, but OpenAI-spec gateways commonly\n * serve `/chat/completions` only, so `responses` is the opt-in. Any other\n * value throws a `ConfigError` rather than guessing a surface.\n */\nexport function readCustomApiSurface(env: NodeJS.ProcessEnv = process.env): CustomApiSurface {\n const raw = env[CUSTOM_API_SURFACE_ENV]?.trim().toLowerCase() ?? '';\n if (raw === '' || raw === 'chat') return 'chat';\n if (raw === 'responses') return 'responses';\n throw new ConfigError(\n `${CUSTOM_API_SURFACE_ENV} must be one of: responses, chat (default chat); got an unrecognised value`\n );\n}\n\n/** Read by `@ai-sdk/openai` itself; the direct OpenAI adapter never passes a baseURL. */\nconst OPENAI_BASE_URL_ENV = 'OPENAI_BASE_URL';\nconst OPENAI_API_HOST = 'api.openai.com';\n\n/**\n * The surface the direct OpenAI adapter (`OPENAI_API_KEY`) calls (#6654).\n * `undefined` means the provider's own default (the Responses API), exactly\n * as before: `OPENAI_BASE_URL` unset, blank, or naming `api.openai.com`, and\n * `NEXUS_CUSTOM_API_SURFACE` is then not read at all. Any other host is an\n * OpenAI-compatible gateway, so it gets {@link readCustomApiSurface}'s answer:\n * chat completions unless `NEXUS_CUSTOM_API_SURFACE=responses`.\n */\nexport function readDirectOpenAiSurface(\n env: NodeJS.ProcessEnv = process.env\n): CustomApiSurface | undefined {\n const baseUrl = env[OPENAI_BASE_URL_ENV]?.trim() ?? '';\n if (baseUrl === '' || hostnameOf(baseUrl) === OPENAI_API_HOST) return undefined;\n return readCustomApiSurface(env);\n}\n\n/**\n * The host of a gateway base URL, for log lines and error messages. A base\n * URL can carry userinfo (`https://user:key@host/v1`), so the full string\n * must never reach a log. Two failure shapes, both replaced rather than\n * echoed: a string that does not parse at all, and a scheme-less paste such\n * as `key@host/v1`, which WHATWG parses with `key:` as the SCHEME and an\n * empty hostname (measured: `new URL('u:pw@host/v1').hostname === ''`).\n */\nexport function hostnameOf(baseUrl: string): string {\n try {\n const host = new URL(baseUrl).hostname;\n return host === '' ? '<no host>' : host;\n } catch {\n return '<unparseable url>';\n }\n}\n\n/**\n * Every exact occurrence of `apiKey` in `message` replaced with `<redacted>`.\n * The pattern-based sanitizer only knows vendor key shapes; a gateway key can\n * be any string, and a 401 body may echo the one it rejected. A blank key\n * matches nothing. The key-only case of {@link redactGatewaySecrets}.\n */\nexport function redactApiKey(message: string, apiKey: string | undefined): string {\n return redactGatewaySecrets(message, { apiKey });\n}\n","/**\n * Validation for the `custom-openai` SDK adapter's gateway URL.\n *\n * The base URL is user-provided, so without validation the adapter becomes\n * an SSRF vector: a malicious prompt that reshaped env vars, or a typo in\n * the config, could point nexus-agents at `http://169.254.169.254/` (AWS\n * metadata) or `http://localhost:5432/` (internal services). This module\n * rejects such URLs unless the user explicitly opts in via\n * `NEXUS_CUSTOM_API_ALLOW_PRIVATE=1`.\n *\n * Called out in the #2119 consensus vote by the Security Engineer role.\n *\n * @module adapters/sdk/custom-api-validation\n */\n\nimport { isIPv4, isIPv6 } from 'node:net';\nimport { lookup as dnsLookup } from 'node:dns/promises';\nimport { ConfigError, ok, err, createLogger, type Result } from '../../core/index.js';\nimport { CUSTOM_API_ALLOW_PRIVATE_ENV } from './types.js';\n\nconst logger = createLogger({ module: 'custom-api-validation' });\n\n/**\n * Why a given URL was rejected. Machine-readable so error messages can\n * distinguish root causes in downstream tooling.\n */\nexport type BaseUrlRejectionReason =\n | 'empty'\n | 'not_a_url'\n | 'not_http_https'\n | 'loopback'\n | 'link_local'\n | 'private_range'\n | 'reserved';\n\ninterface RejectionDetail {\n readonly reason: BaseUrlRejectionReason;\n readonly message: string;\n}\n\n/**\n * Validates a user-provided custom-gateway base URL. Returns the URL as an\n * `ok` Result if it passes, or a `ConfigError` with a machine-readable\n * reason if it fails.\n *\n * Pass `{ allowPrivate: true }` (or set the env var) to bypass the SSRF\n * checks — use this only when the gateway is on a trusted internal host\n * and you accept the risk.\n */\nexport function validateCustomApiBaseUrl(\n raw: string | undefined,\n opts: { readonly allowPrivate?: boolean } = {}\n): Result<URL, ConfigError> {\n if (raw === undefined || raw.trim() === '') {\n return err(\n new ConfigError(\n 'Custom API base URL is required but missing. Set NEXUS_OPENAI_COMPAT_URL or pass `baseUrl` in config.'\n )\n );\n }\n\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n // Not echoed: these messages reach the resilient-adapter warn line, and\n // the raw string can carry userinfo (#4392 inc 3).\n return err(new ConfigError('Custom API base URL is not a valid URL'));\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n return err(\n new ConfigError(`Custom API base URL must use http or https, got \"${url.protocol}\"`)\n );\n }\n\n const allowPrivate = opts.allowPrivate === true || resolveAllowPrivateFromEnv();\n if (!allowPrivate) {\n const rejection = classifyPrivateHost(url.hostname);\n if (rejection !== null) {\n return err(\n new ConfigError(\n `Custom API base URL rejected (SSRF guard, reason=\"${rejection.reason}\"): ${rejection.message}. ` +\n `Set ${CUSTOM_API_ALLOW_PRIVATE_ENV}=1 to bypass if the gateway runs on a trusted internal host.`\n )\n );\n }\n }\n\n return ok(url);\n}\n\nfunction resolveAllowPrivateFromEnv(): boolean {\n // Deliberately strict (case-sensitive, not parseBoolEnv): this disables an\n // SSRF guard, so we don't want extra case variants (e.g. `TRUE`) to loosen\n // the control. Fail-closed on anything but exact `1`/`true` (#3297).\n const v = process.env[CUSTOM_API_ALLOW_PRIVATE_ENV];\n return v === '1' || v === 'true';\n}\n\n/**\n * Returns a rejection reason if the hostname resolves (by string form) to\n * a loopback, link-local, or RFC 1918 private address; `null` otherwise.\n *\n * Note: this is a string-level check. It does NOT perform DNS resolution,\n * so a public DNS name that secretly resolves to a private IP will pass.\n * For that level of defense, callers should additionally run\n * {@link assertCustomApiHostResolvesPublic}, which performs a DNS lookup\n * and classifies each resolved address with the same range tables below.\n */\nfunction classifyPrivateHost(hostname: string): RejectionDetail | null {\n const normalized = normalizeHost(hostname);\n\n // Literal IP addresses — classify directly against the range tables.\n const literal = classifyIpAddress(normalized);\n if (literal !== 'not_an_ip') {\n return literal;\n }\n\n // Hostname literals that resolve to loopback without needing DNS\n if (\n normalized === 'localhost' ||\n normalized.endsWith('.localhost') ||\n normalized.endsWith('.local') // mDNS\n ) {\n return {\n reason: 'loopback',\n message: `hostname \"${hostname}\" resolves to loopback/mDNS`,\n };\n }\n\n return null;\n}\n\n/**\n * Strips IPv6 bracket wrapping (URL.hostname yields \"[::1]\") and lowercases,\n * giving the bare form the `node:net` checks expect. Shared by the sync and\n * async guards so the normalization rule lives in one place.\n */\nfunction normalizeHost(hostname: string): string {\n const stripped =\n hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;\n return stripped.toLowerCase();\n}\n\n/**\n * Classifies a single (already normalized, bracket-stripped) address string.\n * Returns:\n * - a {@link RejectionDetail} if it is a private/loopback/link-local/reserved IP,\n * - `null` if it is a public IP literal (safe),\n * - the sentinel `'not_an_ip'` if the string is not an IP literal at all.\n *\n * This is the shared per-address classifier reused by both the sync\n * string-level guard ({@link classifyPrivateHost}) and the async\n * DNS-resolve-time guard ({@link assertCustomApiHostResolvesPublic}), so the\n * IPv4/IPv6 range tables live in exactly one place.\n */\nfunction classifyIpAddress(normalized: string): RejectionDetail | null | 'not_an_ip' {\n if (isIPv4(normalized)) {\n return classifyIPv4(normalized);\n }\n if (isIPv6(normalized)) {\n return classifyIPv6(normalized);\n }\n return 'not_an_ip';\n}\n\n/** Verdict for a host that is already an IP literal — no DNS required. */\nfunction classifyLiteral(hostname: string, normalized: string): Result<void, ConfigError> {\n const literal = classifyIpAddress(normalized);\n if (literal === null || literal === 'not_an_ip') return ok(undefined);\n return err(\n new ConfigError(\n `Custom API host '${hostname}' is a ${literal.reason} address: ${literal.message}`\n )\n );\n}\n\n/**\n * DNS-resolve-time SSRF guard for the custom-openai gateway.\n *\n * The sync {@link validateCustomApiBaseUrl} only inspects the hostname as a\n * string, so a PUBLIC DNS name that resolves to a PRIVATE/loopback/link-local\n * IP (e.g. an attacker-controlled `gateway.evil.test` pointing at\n * `169.254.169.254`) slips past it. This function closes that gap: it resolves\n * the hostname and runs the EXISTING IP classification against every returned\n * address, failing closed if ANY of them is private.\n *\n * Behaviour:\n * - `allowPrivate` (opt or env) → bypass, returns `ok` (matches the sync guard).\n * - hostname is already an IP literal → no DNS needed; the sync guard already\n * classified literals at construction, so return `ok`.\n * - otherwise `dns.lookup(hostname, { all: true })` and classify each address.\n * If ANY resolves private → `err(ConfigError)`.\n * - on a `dns.lookup` THROW (transient/NXDOMAIN/network error) → log debug and\n * return `ok`. Failing closed here is too aggressive: it would break legit\n * gateways on a flaky resolver, and this is additive defense-in-depth layered\n * on top of the sync string guard that already ran at construction. We only\n * REJECT on a SUCCESSFUL resolution to a private address.\n *\n * Residual risk (TOCTOU): this is RESOLVE-time, not socket-connect-time. A name\n * that passes here could rebind to a private IP before the actual connect\n * (DNS rebinding). A full fix needs a custom `fetch` `lookup` hook validating\n * the peer address at the socket layer — out of scope for this LOW\n * defense-in-depth pass (#3426).\n */\nexport async function assertCustomApiHostResolvesPublic(\n hostname: string,\n opts: { readonly allowPrivate?: boolean } = {}\n): Promise<Result<void, ConfigError>> {\n const allowPrivate = opts.allowPrivate === true || resolveAllowPrivateFromEnv();\n if (allowPrivate) {\n return ok(undefined);\n }\n\n const normalized = normalizeHost(hostname);\n\n // A literal needs no DNS, but it is still classified HERE rather than\n // assumed handled elsewhere. This used to return ok for any literal on the\n // grounds that the sync guard had classified it at construction — but\n // `discoverModels` calls only this guard, never the sync one, so on that\n // path each deferred to the other and neither ran. `http://169.254.169.254/`\n // reached IMDS, from a base URL that can come from a file rather than from\n // operator intent.\n if (isIPv4(normalized) || isIPv6(normalized)) {\n return classifyLiteral(hostname, normalized);\n }\n\n // Only `address` is read; the resolver's `family` is intentionally NOT\n // trusted — `classifyIpAddress` re-derives v4/v6 from the address string, so\n // a mislabeled family in the lookup result cannot let a private IP slip past.\n let addresses: ReadonlyArray<{ readonly address: string }>;\n try {\n addresses = await dnsLookup(hostname, { all: true });\n } catch (error) {\n // Fail OPEN: transient/NXDOMAIN/network errors must not break legit\n // gateways. The sync string guard already ran; this is additive only.\n logger.debug('custom-api SSRF resolve check: DNS lookup failed, skipping (fail-open)', {\n hostname,\n error: error instanceof Error ? error.message : String(error),\n });\n return ok(undefined);\n }\n\n const rejection = firstPrivateAddress(addresses);\n if (rejection !== null) {\n return err(\n new ConfigError(\n `Custom API base URL rejected (SSRF resolve guard, reason=\"${rejection.reason}\"): ` +\n `hostname \"${hostname}\" resolved to ${rejection.message}. ` +\n `Set ${CUSTOM_API_ALLOW_PRIVATE_ENV}=1 to bypass if the gateway runs on a trusted internal host.`\n )\n );\n }\n\n return ok(undefined);\n}\n\n/**\n * Returns the {@link RejectionDetail} for the first resolved address that\n * classifies as private/loopback/link-local/reserved, or `null` if every\n * address is public. Fail-closed: any single private hit trips the guard.\n */\nfunction firstPrivateAddress(\n addresses: ReadonlyArray<{ readonly address: string }>\n): RejectionDetail | null {\n for (const { address } of addresses) {\n const rejection = classifyIpAddress(address.toLowerCase());\n if (rejection !== null && rejection !== 'not_an_ip') {\n return rejection;\n }\n }\n return null;\n}\n\n/**\n * IPv4 ranges the SSRF guard rejects, in order of specificity.\n *\n * \"Not RFC1918\" is not the same as \"public\". The blocks below RFC1918 are\n * here because each is reachable from a host and serves something: 100.64/10\n * carries Alibaba Cloud's metadata endpoint, 192.0.0/24 carries a legacy\n * Oracle Cloud one, and multicast/reserved/broadcast reach the local segment\n * rather than the internet (second pass over #4884).\n */\nconst IPV4_RULES: ReadonlyArray<{\n readonly match: (a: number, b: number, c: number) => boolean;\n readonly reason: BaseUrlRejectionReason;\n readonly label: string;\n}> = [\n { match: (a) => a === 127, reason: 'loopback', label: 'IPv4 loopback' },\n { match: (a) => a === 10, reason: 'private_range', label: 'IPv4 private (10/8)' },\n {\n match: (a, b) => a === 172 && b >= 16 && b <= 31,\n reason: 'private_range',\n label: 'IPv4 private (172.16/12)',\n },\n {\n match: (a, b) => a === 192 && b === 168,\n reason: 'private_range',\n label: 'IPv4 private (192.168/16)',\n },\n {\n match: (a, b) => a === 169 && b === 254,\n reason: 'link_local',\n label: 'IPv4 link-local (169.254/16 — AWS IMDS)',\n },\n { match: (a) => a === 0, reason: 'reserved', label: 'IPv4 reserved (0/8)' },\n {\n match: (a, b) => a === 100 && b >= 64 && b <= 127,\n reason: 'private_range',\n label: 'IPv4 shared address space (100.64/10 — CGNAT, Alibaba metadata)',\n },\n {\n match: (a, b, c) => a === 192 && b === 0 && c === 0,\n reason: 'reserved',\n label: 'IPv4 IETF protocol assignments (192.0.0/24 — legacy Oracle metadata)',\n },\n {\n match: (a, b) => a === 198 && (b === 18 || b === 19),\n reason: 'reserved',\n label: 'IPv4 benchmarking (198.18/15)',\n },\n { match: (a) => a >= 224 && a <= 239, reason: 'reserved', label: 'IPv4 multicast (224/4)' },\n {\n match: (a) => a >= 240,\n reason: 'reserved',\n label: 'IPv4 reserved (240/4, includes 255.255.255.255 broadcast)',\n },\n];\n\nfunction classifyIPv4(ip: string): RejectionDetail | null {\n const parts = ip.split('.').map((p) => Number.parseInt(p, 10));\n if (parts.length !== 4 || parts.some((n) => Number.isNaN(n))) return null;\n const [a, b, c] = parts as [number, number, number, number];\n for (const rule of IPV4_RULES) {\n if (rule.match(a, b, c)) {\n return { reason: rule.reason, message: `${rule.label} (${ip})` };\n }\n }\n return null;\n}\n\n/**\n * Expand an IPv6 literal to its eight 16-bit groups, or null if unparseable.\n *\n * A trailing dotted quad (`::ffff:1.2.3.4`) is folded into two groups first,\n * so callers see one representation regardless of spelling.\n */\nfunction foldTrailingQuad(s: string): string | null {\n const dotted = /(?:^|:)((?:\\d{1,3}\\.){3}\\d{1,3})$/.exec(s);\n if (dotted?.[1] === undefined) return s;\n const q = dotted[1].split('.').map((n) => Number.parseInt(n, 10));\n if (q.length !== 4 || q.some((n) => Number.isNaN(n) || n < 0 || n > 255)) return null;\n const hi = (((q[0] ?? 0) << 8) | (q[1] ?? 0)).toString(16);\n const lo = (((q[2] ?? 0) << 8) | (q[3] ?? 0)).toString(16);\n return `${s.slice(0, s.length - dotted[1].length)}${hi}:${lo}`;\n}\n\nfunction toHextets(ip: string): number[] | null {\n const s = foldTrailingQuad(ip.toLowerCase());\n if (s === null) return null;\n const halves = s.split('::');\n if (halves.length > 2) return null;\n const head = halves[0] === '' || halves[0] === undefined ? [] : halves[0].split(':');\n const tail = halves[1] === '' || halves[1] === undefined ? [] : halves[1].split(':');\n const groups =\n halves.length === 1\n ? head\n : [...head, ...Array<string>(8 - head.length - tail.length).fill('0'), ...tail];\n if (groups.length !== 8) return null;\n const out = groups.map((g) => Number.parseInt(g === '' ? '0' : g, 16));\n return out.some((n) => Number.isNaN(n) || n < 0 || n > 0xffff) ? null : out;\n}\n\n/**\n * The IPv4 address embedded in an IPv6 literal, in dotted form, or null.\n *\n * Covers every prefix that carries a routable IPv4 payload: IPv4-mapped\n * (`::ffff:0:0/96`), IPv4-translated (`::ffff:0:0:0/96`), the NAT64\n * well-known prefix (`64:ff9b::/96`), 6to4 (`2002::/16`), and the deprecated\n * IPv4-compatible form (`::a.b.c.d`). Each is the same destination under a\n * different spelling, and the IPv4 rules must apply to all of them.\n */\nfunction embeddedIPv4(g: readonly number[]): string | null {\n // 6to4 is the odd one out: the payload sits in hextets 1-2, not 6-7, so it\n // cannot share the trailing-quad extraction below.\n if (g[0] === 0x2002) return hextetPairToDotted(g[1] ?? 0, g[2] ?? 0);\n const top = g.slice(0, 6);\n const prefixes: ReadonlyArray<readonly number[]> = [\n [0, 0, 0, 0, 0, 0xffff], // ::ffff:0:0/96 — IPv4-mapped\n [0, 0, 0, 0, 0xffff, 0], // ::ffff:0:0:0/96 — IPv4-translated\n [0x64, 0xff9b, 0, 0, 0, 0], // 64:ff9b::/96 — NAT64 well-known\n [0, 0, 0, 0, 0, 0], // ::a.b.c.d — deprecated IPv4-compatible\n ];\n const matched = prefixes.some((p) => p.every((v, i) => top[i] === v));\n if (!matched) return null;\n const g6 = g[6] ?? 0;\n const g7 = g[7] ?? 0;\n // `::` and `::1` are their own addresses, not embedded IPv4.\n if (top.every((v) => v === 0) && g6 === 0 && g7 <= 1) return null;\n return hextetPairToDotted(g6, g7);\n}\n\n/** The dotted IPv4 address carried by two consecutive 16-bit hextets. */\nfunction hextetPairToDotted(hi: number, lo: number): string {\n return [(hi >> 8) & 255, hi & 255, (lo >> 8) & 255, lo & 255].join('.');\n}\n\n/**\n * Classify an IPv6 address by its first hextet, or null if it is not in a\n * non-global prefix.\n *\n * Numeric rather than a string prefix test. The previous `startsWith('fe80:')`\n * covered 1/64th of fe80::/10 — `febf::1` is equally link-local and was\n * allowed — and site-local fec0::/10 had no check at all (second pass over #4884).\n */\nfunction classifyIPv6Prefix(first: number, ip: string): RejectionDetail | null {\n if (first >= 0xfc00 && first <= 0xfdff) {\n return { reason: 'private_range', message: `IPv6 unique-local (${ip}, fc00::/7)` };\n }\n if (first >= 0xfe80 && first <= 0xfebf) {\n return { reason: 'link_local', message: `IPv6 link-local (${ip}, fe80::/10)` };\n }\n if (first >= 0xfec0 && first <= 0xfeff) {\n return { reason: 'private_range', message: `IPv6 site-local (${ip}, fec0::/10)` };\n }\n return null;\n}\n\nfunction classifyIPv6(ip: string): RejectionDetail | null {\n const lower = ip.toLowerCase();\n if (lower === '::1') return { reason: 'loopback', message: `IPv6 loopback (${ip})` };\n\n const groups = toHextets(lower);\n if (groups === null) return null;\n\n const prefixDetail = classifyIPv6Prefix(groups[0] ?? 0, ip);\n if (prefixDetail !== null) return prefixDetail;\n\n if (groups.every((n) => n === 0)) {\n return { reason: 'reserved', message: `IPv6 unspecified (${ip}) — routes to localhost` };\n }\n\n // An IPv4 address written in IPv6 form is still that IPv4 address, and\n // `isIPv6` is true for it so it never reached the IPv4 rules — IMDS,\n // loopback and RFC1918 were all reachable this way. Note the URL parser\n // normalises the dotted spelling to hex, so the hex form is the one that\n // actually arrives.\n const v4 = embeddedIPv4(groups);\n if (v4 === null) return null;\n const detail = classifyIPv4(v4);\n return detail === null\n ? null\n : { reason: detail.reason, message: `${detail.message} via IPv6 (${ip})` };\n}\n"],"mappings":"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","names":[]}
|