@cliwant/mcp-sam-gov 1.8.0 → 1.10.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/README.ja.md +5 -5
- package/README.ko.md +5 -5
- package/README.md +8 -7
- package/dist/arcgis-feature.d.ts +68 -0
- package/dist/arcgis-feature.d.ts.map +1 -0
- package/dist/arcgis-feature.js +206 -0
- package/dist/arcgis-feature.js.map +1 -0
- package/dist/arcgis-hub.d.ts +90 -0
- package/dist/arcgis-hub.d.ts.map +1 -0
- package/dist/arcgis-hub.js +210 -0
- package/dist/arcgis-hub.js.map +1 -0
- package/dist/bonfire.d.ts +69 -0
- package/dist/bonfire.d.ts.map +1 -0
- package/dist/bonfire.js +208 -0
- package/dist/bonfire.js.map +1 -0
- package/dist/ecfr.d.ts +14 -0
- package/dist/ecfr.d.ts.map +1 -1
- package/dist/ecfr.js +127 -1
- package/dist/ecfr.js.map +1 -1
- package/dist/opengov.d.ts +96 -0
- package/dist/opengov.d.ts.map +1 -0
- package/dist/opengov.js +244 -0
- package/dist/opengov.js.map +1 -0
- package/dist/pricing.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +189 -3
- package/dist/server.js.map +1 -1
- package/dist/socrata.d.ts +27 -16
- package/dist/socrata.d.ts.map +1 -1
- package/dist/socrata.js +128 -18
- package/dist/socrata.js.map +1 -1
- package/package.json +2 -2
- package/src/arcgis-feature.ts +232 -0
- package/src/arcgis-hub.ts +270 -0
- package/src/bonfire.ts +245 -0
- package/src/ecfr.ts +154 -1
- package/src/opengov.ts +309 -0
- package/src/server.ts +203 -3
- package/src/socrata.ts +132 -18
package/dist/ecfr.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Both keyless. Documented at https://www.ecfr.gov/developers/.
|
|
14
14
|
*/
|
|
15
|
-
import { fetchWithRetry } from "./errors.js";
|
|
15
|
+
import { fetchWithRetry, ToolErrorCarrier } from "./errors.js";
|
|
16
16
|
import { driftError } from "./datasource.js";
|
|
17
17
|
import { memoize } from "./cache.js";
|
|
18
18
|
import { withMeta } from "./meta.js";
|
|
@@ -59,6 +59,132 @@ export async function listTitles() {
|
|
|
59
59
|
});
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
+
// ─── ecfr_get_section — full in-force text of one CFR section ──────────────
|
|
63
|
+
// The eval-found gap: ecfr_search returns ranked SNIPPETS; there was no way to pull
|
|
64
|
+
// a clause's COMPLETE text. This fetches one section from the versioner `full`
|
|
65
|
+
// endpoint (a per-section XML, ~5KB), de-XMLed to plain text. A bespoke individual
|
|
66
|
+
// source (not the search API) — mode-2 discovery.
|
|
67
|
+
const ECFR_SECTION_RE = /^[0-9]{1,3}\.[0-9]{1,4}(-[0-9]{1,4})?$/;
|
|
68
|
+
const ECFR_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
69
|
+
async function fetchXmlText(url) {
|
|
70
|
+
const r = await fetchWithRetry(url, { headers: { Accept: "application/xml" }, signal: AbortSignal.timeout(20_000) }, `ecfr:${url.split("/api/")[1] ?? url}`);
|
|
71
|
+
return await r.text();
|
|
72
|
+
}
|
|
73
|
+
function decodeXmlEntities(s) {
|
|
74
|
+
return s
|
|
75
|
+
.replace(/"/g, '"')
|
|
76
|
+
.replace(/</g, "<")
|
|
77
|
+
.replace(/>/g, ">")
|
|
78
|
+
.replace(/'/g, "'")
|
|
79
|
+
.replace(/&#(\d+);/g, (_, n) => String.fromCharCode(Number(n)))
|
|
80
|
+
.replace(/&/g, "&");
|
|
81
|
+
}
|
|
82
|
+
// De-XML a section body to readable plain text: block tags → newlines, strip the
|
|
83
|
+
// rest, decode entities, drop empty lines. No fabrication — the eCFR's own text.
|
|
84
|
+
function xmlToPlainText(xml) {
|
|
85
|
+
return decodeXmlEntities(xml
|
|
86
|
+
.replace(/<\/(P|HD1|HD2|HD3|HD4|HEAD|FP|GID|GPOTABLE|ROW)>/gi, "\n")
|
|
87
|
+
.replace(/<[^>]+>/g, ""))
|
|
88
|
+
.split("\n")
|
|
89
|
+
.map((l) => l.trim())
|
|
90
|
+
.filter((l) => l.length > 0)
|
|
91
|
+
.join("\n");
|
|
92
|
+
}
|
|
93
|
+
export async function getSection(args) {
|
|
94
|
+
// SSRF/injection: `section` (+ derived `part`) interpolate into the URL query, so
|
|
95
|
+
// the charclass is the load-bearing guard. `titleNumber` is numeric (schema).
|
|
96
|
+
// `\s` reject is load-bearing: JS `$` also matches BEFORE a trailing "\n", so the
|
|
97
|
+
// charclass alone would admit "52.204-21\n". No valid section carries whitespace.
|
|
98
|
+
if (!ECFR_SECTION_RE.test(args.section) || /\s/.test(args.section)) {
|
|
99
|
+
throw new ToolErrorCarrier({
|
|
100
|
+
kind: "invalid_input",
|
|
101
|
+
retryable: false,
|
|
102
|
+
message: `Invalid section ${JSON.stringify(args.section)} — expected a CFR section like "52.204-21" or "12.301" (^[0-9]{1,3}\\.[0-9]{1,4}(-[0-9]{1,4})?$, no whitespace).`,
|
|
103
|
+
upstreamEndpoint: "ecfr:versioner/full",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
let issueDate = args.date;
|
|
107
|
+
if (issueDate !== undefined && !ECFR_DATE_RE.test(issueDate)) {
|
|
108
|
+
throw new ToolErrorCarrier({
|
|
109
|
+
kind: "invalid_input",
|
|
110
|
+
retryable: false,
|
|
111
|
+
message: `Invalid date ${JSON.stringify(args.date)} — expected an eCFR issue date YYYY-MM-DD (omit to use the title's latest).`,
|
|
112
|
+
upstreamEndpoint: "ecfr:versioner/full",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (!issueDate) {
|
|
116
|
+
// Resolve the title's latest issue date (memoized titles list) — DISCLOSED below.
|
|
117
|
+
const t = (await listTitles()).data.titles.find((x) => x.number === args.titleNumber);
|
|
118
|
+
issueDate = t?.latestIssueDate;
|
|
119
|
+
if (!issueDate) {
|
|
120
|
+
throw new ToolErrorCarrier({
|
|
121
|
+
kind: "invalid_input",
|
|
122
|
+
retryable: false,
|
|
123
|
+
message: `Title ${args.titleNumber} is not a known CFR title (or has no issue date). Verify it via ecfr_list_titles.`,
|
|
124
|
+
upstreamEndpoint: "ecfr:versioner/full",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const part = args.section.split(".")[0];
|
|
129
|
+
const url = `${ECFR}/versioner/v1/full/${issueDate}/title-${args.titleNumber}.xml?part=${encodeURIComponent(part)}§ion=${encodeURIComponent(args.section)}`;
|
|
130
|
+
const xml = await fetchXmlText(url);
|
|
131
|
+
// Locate the SECTION DIV8 for EXACTLY this section (the endpoint may return the
|
|
132
|
+
// enclosing part). Absent ⇒ honest not_found, NEVER a fabricated empty/wrong text.
|
|
133
|
+
const escaped = args.section.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
134
|
+
const div = xml.match(new RegExp(`<DIV8 N="${escaped}"[^>]*TYPE="SECTION"[^>]*>([\\s\\S]*?)</DIV8>`, "i"));
|
|
135
|
+
if (!div) {
|
|
136
|
+
throw new ToolErrorCarrier({
|
|
137
|
+
kind: "not_found",
|
|
138
|
+
retryable: false,
|
|
139
|
+
message: `Section ${args.section} not found in CFR title ${args.titleNumber} as of the ${issueDate} issue. Verify the citation (e.g. via ecfr_search); the part number (${part}) must actually contain the section.`,
|
|
140
|
+
upstreamEndpoint: "ecfr:versioner/full",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const body = div[1];
|
|
144
|
+
const headMatch = body.match(/<HEAD>([\s\S]*?)<\/HEAD>/i);
|
|
145
|
+
const heading = headMatch ? xmlToPlainText(headMatch[1]).replace(/\n+/g, " ").trim() : null;
|
|
146
|
+
let citation = null;
|
|
147
|
+
let alternateReference = null;
|
|
148
|
+
const meta = xml.match(/hierarchy_metadata="([^"]*)"/i);
|
|
149
|
+
if (meta) {
|
|
150
|
+
try {
|
|
151
|
+
const hm = JSON.parse(decodeXmlEntities(meta[1]));
|
|
152
|
+
citation = hm.citation ?? null;
|
|
153
|
+
alternateReference = hm.alternate_reference ?? null;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
/* metadata is advisory — its absence never blocks the text */
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const fullText = xmlToPlainText(body);
|
|
160
|
+
if (fullText.length === 0) {
|
|
161
|
+
// A 200 with a section shell but no readable text ⇒ schema drift, not a fake empty.
|
|
162
|
+
throw driftError("ecfr:versioner/full", `The section ${args.section} DIV8 carried no extractable text — eCFR XML shape drift, never a fabricated empty section.`);
|
|
163
|
+
}
|
|
164
|
+
const ecfrUrl = `https://www.ecfr.gov/current/title-${args.titleNumber}/section-${args.section}`;
|
|
165
|
+
return withMeta({
|
|
166
|
+
titleNumber: args.titleNumber,
|
|
167
|
+
section: args.section,
|
|
168
|
+
citation,
|
|
169
|
+
alternateReference,
|
|
170
|
+
heading,
|
|
171
|
+
fullText,
|
|
172
|
+
issueDate,
|
|
173
|
+
ecfrUrl,
|
|
174
|
+
}, {
|
|
175
|
+
source: "ecfr.gov/api (versioner/v1/full)",
|
|
176
|
+
keylessMode: true,
|
|
177
|
+
returned: 1,
|
|
178
|
+
totalAvailable: 1,
|
|
179
|
+
truncated: false,
|
|
180
|
+
filtersApplied: ["titleNumber", "section"],
|
|
181
|
+
filtersDropped: [],
|
|
182
|
+
fieldsUnavailable: [],
|
|
183
|
+
notes: [
|
|
184
|
+
`Full in-force text of ${citation ?? `${args.titleNumber} CFR ${args.section}`} as of the ${issueDate} eCFR issue (the title's LATEST unless a date was supplied — a moving target). De-XMLed to plain text; no content fabricated — open ecfrUrl for the authoritative rendering.`,
|
|
185
|
+
],
|
|
186
|
+
});
|
|
187
|
+
}
|
|
62
188
|
export async function search(args) {
|
|
63
189
|
const url = new URL(`${ECFR}/search/v1/results`);
|
|
64
190
|
url.searchParams.set("query", args.query);
|
package/dist/ecfr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ecfr.js","sourceRoot":"","sources":["../src/ecfr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ecfr.js","sourceRoot":"","sources":["../src/ecfr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,IAAI,GAAG,0BAA0B,CAAC;AAExC,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,mEAAmE;AACnE,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAEnC,KAAK,UAAU,SAAS,CAAI,GAAW;IACrC,MAAM,CAAC,GAAG,MAAM,cAAc,CAC5B,GAAG,EACH;QACE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;QACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;KACpC,EACD,QAAQ,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CACvC,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAM,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,sEAAsE;IACtE,OAAO,OAAO,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;QAWvC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAO,GAAG,IAAI,2BAA2B,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7C,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC;YACrB,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE;YAClB,eAAe,EAAE,CAAC,CAAC,iBAAiB;YACpC,eAAe,EAAE,CAAC,CAAC,iBAAiB;YACpC,YAAY,EAAE,CAAC,CAAC,gBAAgB;YAChC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ;SACvB,CAAC,CAAC,CAAC;QACJ,+EAA+E;QAC/E,2EAA2E;QAC3E,6EAA6E;QAC7E,+EAA+E;QAC/E,OAAO,QAAQ,CACb,EAAE,MAAM,EAAE,EACV;YACE,MAAM,EAAE,oCAAoC;YAC5C,WAAW,EAAE,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,MAAM;YACvB,cAAc,EAAE,MAAM,CAAC,MAAM;YAC7B,SAAS,EAAE,KAAK;YAChB,cAAc,EAAE,EAAE;YAClB,cAAc,EAAE,EAAE;YAClB,KAAK,EAAE;gBACL,iIAAiI;aAClI;SACF,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,oFAAoF;AACpF,+EAA+E;AAC/E,mFAAmF;AACnF,kDAAkD;AAClD,MAAM,eAAe,GAAG,wCAAwC,CAAC;AACjE,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAE3C,KAAK,UAAU,YAAY,CAAC,GAAW;IACrC,MAAM,CAAC,GAAG,MAAM,cAAc,CAC5B,GAAG,EACH,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAC/E,QAAQ,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CACvC,CAAC;IACF,OAAO,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAS;IAClC,OAAO,CAAC;SACL,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;SAC9D,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,iFAAiF;AACjF,iFAAiF;AACjF,SAAS,cAAc,CAAC,GAAW;IACjC,OAAO,iBAAiB,CACtB,GAAG;SACA,OAAO,CAAC,oDAAoD,EAAE,IAAI,CAAC;SACnE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAC3B;SACE,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAIhC;IACC,kFAAkF;IAClF,8EAA8E;IAC9E,kFAAkF;IAClF,kFAAkF;IAClF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,eAAe;YACrB,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,mBAAmB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,kHAAkH;YAC1K,gBAAgB,EAAE,qBAAqB;SACxC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC;IAC1B,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,eAAe;YACrB,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,gBAAgB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,6EAA6E;YAC/H,gBAAgB,EAAE,qBAAqB;SACxC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,kFAAkF;QAClF,MAAM,CAAC,GAAG,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAC7C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,WAAW,CACrC,CAAC;QACF,SAAS,GAAG,CAAC,EAAE,eAAe,CAAC;QAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,gBAAgB,CAAC;gBACzB,IAAI,EAAE,eAAe;gBACrB,SAAS,EAAE,KAAK;gBAChB,OAAO,EAAE,SAAS,IAAI,CAAC,WAAW,mFAAmF;gBACrH,gBAAgB,EAAE,qBAAqB;aACxC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACzC,MAAM,GAAG,GAAG,GAAG,IAAI,sBAAsB,SAAS,UAAU,IAAI,CAAC,WAAW,aAAa,kBAAkB,CAAC,IAAI,CAAC,YAAY,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;IAChK,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IACpC,gFAAgF;IAChF,mFAAmF;IACnF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CACnB,IAAI,MAAM,CAAC,YAAY,OAAO,+CAA+C,EAAE,GAAG,CAAC,CACpF,CAAC;IACF,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,WAAW,IAAI,CAAC,OAAO,2BAA2B,IAAI,CAAC,WAAW,cAAc,SAAS,wEAAwE,IAAI,sCAAsC;YACpN,gBAAgB,EAAE,qBAAqB;SACxC,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC;IACrB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7F,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,kBAAkB,GAAkB,IAAI,CAAC;IAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACxD,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAGhD,CAAC;YACF,QAAQ,GAAG,EAAE,CAAC,QAAQ,IAAI,IAAI,CAAC;YAC/B,kBAAkB,GAAG,EAAE,CAAC,mBAAmB,IAAI,IAAI,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,8DAA8D;QAChE,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,oFAAoF;QACpF,MAAM,UAAU,CACd,qBAAqB,EACrB,eAAe,IAAI,CAAC,OAAO,6FAA6F,CACzH,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,sCAAsC,IAAI,CAAC,WAAW,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC;IACjG,OAAO,QAAQ,CACb;QACE,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ;QACR,kBAAkB;QAClB,OAAO;QACP,QAAQ;QACR,SAAS;QACT,OAAO;KACR,EACD;QACE,MAAM,EAAE,kCAAkC;QAC1C,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE,CAAC;QACX,cAAc,EAAE,CAAC;QACjB,SAAS,EAAE,KAAK;QAChB,cAAc,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC;QAC1C,cAAc,EAAE,EAAE;QAClB,iBAAiB,EAAE,EAAE;QACrB,KAAK,EAAE;YACL,yBAAyB,QAAQ,IAAI,GAAG,IAAI,CAAC,WAAW,QAAQ,IAAI,CAAC,OAAO,EAAE,cAAc,SAAS,8KAA8K;SACpR;KACF,CACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAK5B;IACC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,oBAAoB,CAAC,CAAC;IACjD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,oEAAoE;QACpE,uDAAuD;QACvD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,kBAAkB,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,8EAA8E;IAC9E,8EAA8E;IAC9E,uEAAuE;IACvE,+EAA+E;IAC/E,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC/B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACnE,CAAC;IA4BD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnD,+EAA+E;IAC/E,gFAAgF;IAChF,6EAA6E;IAC7E,kFAAkF;IAClF,8EAA8E;IAC9E,iFAAiF;IACjF,wEAAwE;IACxE,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/D,MAAM,UAAU,CACd,UAAU,EACV,qHAAqH,CACtH,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC1D,MAAM,UAAU,CACd,UAAU,EACV,+JAA+J,CAChK,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG;QACX,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxC,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE;YAClB,KAAK,EAAE,CAAC,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;YAC/B,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO;YAC7B,IAAI,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI;YACvB,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO;YAC7B,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO;YAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,kBAAkB,IAAI,EAAE,CAAC;iBACnD,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,KAAK,CAAC;YACd,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,iBAAiB,IAAI,EAAE,CAAC;YAC7C,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;YACnB,iDAAiD;YACjD,OAAO,EAAE,CAAC,CAAC,SAAS;gBAClB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC3B,CAAC,CAAC,EAAE;YACN,WAAW,EAAE,CAAC,CAAC,SAAS,IAAI,EAAE;YAC9B,2EAA2E;YAC3E,0EAA0E;YAC1E,4EAA4E;YAC5E,2EAA2E;YAC3E,MAAM,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI;SAC1B,CAAC,CAAC;KACJ,CAAC;IAEF,qEAAqE;IACrE,kEAAkE;IAClE,yEAAyE;IACzE,uEAAuE;IACvE,2EAA2E;IAC3E,2DAA2D;IAC3D,wEAAwE;IACxE,2EAA2E;IAC3E,6EAA6E;IAC7E,6EAA6E;IAC7E,iFAAiF;IACjF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IACrC,MAAM,cAAc,GAClB,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,yEAAyE;IACzE,yBAAyB;IACzB,MAAM,iBAAiB,GACrB,cAAc,KAAK,IAAI,IAAI,cAAc,IAAI,oBAAoB,CAAC;IACpE,MAAM,SAAS,GACb,IAAI,CAAC,WAAW,KAAK,SAAS;QAC5B,CAAC,CAAC,sBAAsB,IAAI,CAAC,WAAW,GACpC,IAAI,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,yCAAyC,CAAC,CAAC,CAAC,EACxE,OAAO;QACT,CAAC,CAAC,mDAAmD,CAAC;IAC1D,MAAM,KAAK,GAAG,CAAC,SAAS,CAAC,CAAC;IAC1B,kFAAkF;IAClF,kFAAkF;IAClF,uEAAuE;IACvE,KAAK,CAAC,IAAI,CACR,2TAA2T,CAC5T,CAAC;IACF,IAAI,iBAAiB,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CACR,4BAA4B,oBAAoB,wMAAwM,CACzP,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE;QACpB,MAAM,EAAE,0BAA0B;QAClC,WAAW,EAAE,IAAI;QACjB,QAAQ;QACR,cAAc;QACd,wEAAwE;QACxE,0EAA0E;QAC1E,gEAAgE;QAChE,iBAAiB;QACjB,SAAS,EACP,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS;QACjE,cAAc,EAAE,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE;QACrE,cAAc,EAAE,EAAE;QAClB,iBAAiB,EAAE,EAAE;QACrB,KAAK;KACN,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC1B,OAAO,CAAC;SACL,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,YAAY,CAAC,CAKrB;IACC,MAAM,IAAI,GAAG,sCAAsC,CAAC,CAAC,KAAK,EAAE,CAAC;IAC7D,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,GAAG,IAAI,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC;IACrD,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,GAAG,IAAI,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,GAAG,IAAI,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opengov.ts — OpenGov Procurement (formerly ProcureNow) public solicitations,
|
|
3
|
+
* a keyless-first SLED bid source (loop cycle — SLED bid campaign, 2026-07-19).
|
|
4
|
+
*
|
|
5
|
+
* WHAT IT ADDS: OpenGov Procurement hosts the live open-solicitation portals of
|
|
6
|
+
* **525+ US state/local governments** (cities, counties, school & special
|
|
7
|
+
* districts across 42 states + DC). The public portal SPA
|
|
8
|
+
* (`procurement.opengov.com/portal/{code}`) is served by an UNAUTHENTICATED
|
|
9
|
+
* backend REST API — the official key-gated `api-key` API is NOT used; this
|
|
10
|
+
* consumes ONLY the anonymous endpoints the public portal itself calls, so it is
|
|
11
|
+
* genuinely keyless (live-verified 2026-07-19: bare anonymous request, no api-key /
|
|
12
|
+
* auth header / cookie). It is the OpenGov sibling of the Socrata/CKAN/ArcGIS
|
|
13
|
+
* discovery tools and one of the two highest-reach keyless SLED bid feeds (with
|
|
14
|
+
* Bonfire RSS).
|
|
15
|
+
*
|
|
16
|
+
* The module writes ZERO fetch/coercion/error/meta code — it REUSES `getJson`
|
|
17
|
+
* (GET + POST/JSON via ADR-0014, redirect:"error") / `driftError` / `num`·`str`
|
|
18
|
+
* (coerce.ts, null-never-0 / null-never-empty) / `withMeta`·`buildMeta`,
|
|
19
|
+
* mirroring datagov-catalog.ts. KEYLESS: no key seam.
|
|
20
|
+
*
|
|
21
|
+
* ★ SSRF: the host is a compile-time literal (`OPENGOV_HOST`). All inputs ride in
|
|
22
|
+
* a MODULE-BUILT JSON body / URLSearchParams from validated typed args — NO
|
|
23
|
+
* raw-host/raw-path passthrough (this is a FIXED-host API). A post-construction
|
|
24
|
+
* hostname/protocol assertion + `redirect:"error"` lock every call.
|
|
25
|
+
*
|
|
26
|
+
* ★ TWO endpoints (both anonymous, live-verified):
|
|
27
|
+
* - `GET /api/v1/government` → a flat JSON array of ~560 org records (the whole
|
|
28
|
+
* directory in one call; `government.code` = the portal slug). We keep the
|
|
29
|
+
* ACTIVE, non-internal ones (~525 real public portals).
|
|
30
|
+
* - `POST /api/v1/project/list` body `{governmentCode, publicView:true, page,
|
|
31
|
+
* limit}` → `{projects:[…], count}`. `publicView:true` is REQUIRED (the public
|
|
32
|
+
* gate). `count` is the TRUE total of public projects for that org.
|
|
33
|
+
*
|
|
34
|
+
* ★ HONESTY PILLARS (captured live 2026-07-19):
|
|
35
|
+
* P1: totalAvailable = the real total — the filtered directory length (govt
|
|
36
|
+
* list) or the API's `count` (solicitations) — NEVER the page length.
|
|
37
|
+
* P2: getJson→fetchWithRetry THROWS on 429 / 5xx / timeout — NEVER a fake empty.
|
|
38
|
+
* A genuine no-match (projects:[], count:0) ⇒ honest empty.
|
|
39
|
+
* P3: every scalar via `str` (null-never-empty) / `num` (null-never-0). The
|
|
40
|
+
* `status` enum is surfaced VERBATIM (open = currently accepting; the response
|
|
41
|
+
* also carries pending/evaluation/closed — the CONSUMER filters; we do not
|
|
42
|
+
* hide non-open rows or fabricate an "open count").
|
|
43
|
+
* P4: `/government` non-array or `/project/list` `.projects` non-array ⇒
|
|
44
|
+
* driftError; a 200 non-JSON body ⇒ schema_drift via the catch-ladder
|
|
45
|
+
* (ToolErrorCarrier rethrow FIRST so a 429/5xx keeps its taxonomy).
|
|
46
|
+
*/
|
|
47
|
+
import { num } from "./coerce.js";
|
|
48
|
+
import { type MetaBundle } from "./meta.js";
|
|
49
|
+
export { num };
|
|
50
|
+
export declare const OPENGOV_HOST = "api.procurement.opengov.com";
|
|
51
|
+
export declare const OPENGOV_CODE_RE: RegExp;
|
|
52
|
+
export type OpengovGovernment = {
|
|
53
|
+
code: string | null;
|
|
54
|
+
name: string | null;
|
|
55
|
+
city: string | null;
|
|
56
|
+
state: string | null;
|
|
57
|
+
website: string | null;
|
|
58
|
+
};
|
|
59
|
+
export type OpengovSolicitation = {
|
|
60
|
+
id: number | null;
|
|
61
|
+
title: string | null;
|
|
62
|
+
solicitationNumber: string | null;
|
|
63
|
+
status: string | null;
|
|
64
|
+
type: string | null;
|
|
65
|
+
department: string | null;
|
|
66
|
+
releaseDate: string | null;
|
|
67
|
+
proposalDeadline: string | null;
|
|
68
|
+
contactName: string | null;
|
|
69
|
+
link: string | null;
|
|
70
|
+
};
|
|
71
|
+
export type OpengovListGovernmentsArgs = {
|
|
72
|
+
state?: string;
|
|
73
|
+
query?: string;
|
|
74
|
+
limit?: number;
|
|
75
|
+
offset?: number;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* List the OpenGov Procurement government portals (the whole directory arrives in
|
|
79
|
+
* ONE keyless GET). Client-side filters: `state` (2-letter), `query` (name
|
|
80
|
+
* substring). Only ACTIVE, non-internal portals are kept. Feeds `code` to
|
|
81
|
+
* opengov_search_solicitations. totalAvailable = the filtered directory size (P1).
|
|
82
|
+
*/
|
|
83
|
+
export declare function listGovernments(args: OpengovListGovernmentsArgs): Promise<MetaBundle>;
|
|
84
|
+
export type OpengovSearchSolicitationsArgs = {
|
|
85
|
+
governmentCode: string;
|
|
86
|
+
limit?: number;
|
|
87
|
+
offset?: number;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* List an OpenGov government's public solicitations (POST /project/list with the
|
|
91
|
+
* REQUIRED publicView gate). Returns all public projects with `status` surfaced
|
|
92
|
+
* (filter status==='open' for live bids). totalAvailable = the API's `count` (the
|
|
93
|
+
* org's total public-project count — P1, never the page length).
|
|
94
|
+
*/
|
|
95
|
+
export declare function searchSolicitations(args: OpengovSearchSolicitationsArgs): Promise<MetaBundle>;
|
|
96
|
+
//# sourceMappingURL=opengov.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opengov.d.ts","sourceRoot":"","sources":["../src/opengov.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,OAAO,EAAE,GAAG,EAAO,MAAM,aAAa,CAAC;AACvC,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAGzE,OAAO,EAAE,GAAG,EAAE,CAAC;AAGf,eAAO,MAAM,YAAY,gCAAgC,CAAC;AAW1D,eAAO,MAAM,eAAe,QAAsB,CAAC;AAMnD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AA6BF,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,UAAU,CAAC,CAuErB;AAGD,MAAM,MAAM,8BAA8B,GAAG;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC,UAAU,CAAC,CAoFrB"}
|
package/dist/opengov.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opengov.ts — OpenGov Procurement (formerly ProcureNow) public solicitations,
|
|
3
|
+
* a keyless-first SLED bid source (loop cycle — SLED bid campaign, 2026-07-19).
|
|
4
|
+
*
|
|
5
|
+
* WHAT IT ADDS: OpenGov Procurement hosts the live open-solicitation portals of
|
|
6
|
+
* **525+ US state/local governments** (cities, counties, school & special
|
|
7
|
+
* districts across 42 states + DC). The public portal SPA
|
|
8
|
+
* (`procurement.opengov.com/portal/{code}`) is served by an UNAUTHENTICATED
|
|
9
|
+
* backend REST API — the official key-gated `api-key` API is NOT used; this
|
|
10
|
+
* consumes ONLY the anonymous endpoints the public portal itself calls, so it is
|
|
11
|
+
* genuinely keyless (live-verified 2026-07-19: bare anonymous request, no api-key /
|
|
12
|
+
* auth header / cookie). It is the OpenGov sibling of the Socrata/CKAN/ArcGIS
|
|
13
|
+
* discovery tools and one of the two highest-reach keyless SLED bid feeds (with
|
|
14
|
+
* Bonfire RSS).
|
|
15
|
+
*
|
|
16
|
+
* The module writes ZERO fetch/coercion/error/meta code — it REUSES `getJson`
|
|
17
|
+
* (GET + POST/JSON via ADR-0014, redirect:"error") / `driftError` / `num`·`str`
|
|
18
|
+
* (coerce.ts, null-never-0 / null-never-empty) / `withMeta`·`buildMeta`,
|
|
19
|
+
* mirroring datagov-catalog.ts. KEYLESS: no key seam.
|
|
20
|
+
*
|
|
21
|
+
* ★ SSRF: the host is a compile-time literal (`OPENGOV_HOST`). All inputs ride in
|
|
22
|
+
* a MODULE-BUILT JSON body / URLSearchParams from validated typed args — NO
|
|
23
|
+
* raw-host/raw-path passthrough (this is a FIXED-host API). A post-construction
|
|
24
|
+
* hostname/protocol assertion + `redirect:"error"` lock every call.
|
|
25
|
+
*
|
|
26
|
+
* ★ TWO endpoints (both anonymous, live-verified):
|
|
27
|
+
* - `GET /api/v1/government` → a flat JSON array of ~560 org records (the whole
|
|
28
|
+
* directory in one call; `government.code` = the portal slug). We keep the
|
|
29
|
+
* ACTIVE, non-internal ones (~525 real public portals).
|
|
30
|
+
* - `POST /api/v1/project/list` body `{governmentCode, publicView:true, page,
|
|
31
|
+
* limit}` → `{projects:[…], count}`. `publicView:true` is REQUIRED (the public
|
|
32
|
+
* gate). `count` is the TRUE total of public projects for that org.
|
|
33
|
+
*
|
|
34
|
+
* ★ HONESTY PILLARS (captured live 2026-07-19):
|
|
35
|
+
* P1: totalAvailable = the real total — the filtered directory length (govt
|
|
36
|
+
* list) or the API's `count` (solicitations) — NEVER the page length.
|
|
37
|
+
* P2: getJson→fetchWithRetry THROWS on 429 / 5xx / timeout — NEVER a fake empty.
|
|
38
|
+
* A genuine no-match (projects:[], count:0) ⇒ honest empty.
|
|
39
|
+
* P3: every scalar via `str` (null-never-empty) / `num` (null-never-0). The
|
|
40
|
+
* `status` enum is surfaced VERBATIM (open = currently accepting; the response
|
|
41
|
+
* also carries pending/evaluation/closed — the CONSUMER filters; we do not
|
|
42
|
+
* hide non-open rows or fabricate an "open count").
|
|
43
|
+
* P4: `/government` non-array or `/project/list` `.projects` non-array ⇒
|
|
44
|
+
* driftError; a 200 non-JSON body ⇒ schema_drift via the catch-ladder
|
|
45
|
+
* (ToolErrorCarrier rethrow FIRST so a 429/5xx keeps its taxonomy).
|
|
46
|
+
*/
|
|
47
|
+
import { ToolErrorCarrier } from "./errors.js";
|
|
48
|
+
import { getJson, driftError } from "./datasource.js";
|
|
49
|
+
import { num, str } from "./coerce.js";
|
|
50
|
+
import { withMeta } from "./meta.js";
|
|
51
|
+
// Re-export the shared honesty coercion (num-parity guard).
|
|
52
|
+
export { num };
|
|
53
|
+
// ─── Fixed endpoint (SSRF core — compile-time CONSTANTS) ──────────
|
|
54
|
+
export const OPENGOV_HOST = "api.procurement.opengov.com";
|
|
55
|
+
const OPENGOV_GOVERNMENT_PATH = "/api/v1/government";
|
|
56
|
+
const OPENGOV_PROJECT_LIST_PATH = "/api/v1/project/list";
|
|
57
|
+
const OPENGOV_GOV_LABEL = "opengov:/api/v1/government";
|
|
58
|
+
const OPENGOV_PROJ_LABEL = "opengov:/api/v1/project/list";
|
|
59
|
+
const OPENGOV_SOURCE = "procurement.opengov.com via OpenGov Procurement public API (keyless)";
|
|
60
|
+
const OPENGOV_PORTAL = "https://procurement.opengov.com/portal";
|
|
61
|
+
// The org slug grammar (SSRF + injection guard). Real slugs are lowercase
|
|
62
|
+
// alnum + hyphen (e.g. "santacruzca", "u-46", "twc-texas-gov"). Validated
|
|
63
|
+
// BEFORE it rides the JSON body — a "../"/space/uppercase ⇒ invalid_input, 0 fetch.
|
|
64
|
+
export const OPENGOV_CODE_RE = /^[a-z0-9-]{1,64}$/;
|
|
65
|
+
const OPENGOV_STATUS_NOTE = "`status` is surfaced verbatim: open = currently ACCEPTING responses; pending/evaluation/closed are also returned (publicView shows all public projects). Filter status==='open' for live bids. totalAvailable is the org's TOTAL public-project count (all statuses), not an open-only count.";
|
|
66
|
+
/** null-preserving boolean (a real true/false survives; else null). */
|
|
67
|
+
function boolOrNull(x) {
|
|
68
|
+
return typeof x === "boolean" ? x : null;
|
|
69
|
+
}
|
|
70
|
+
// ─── SSRF-guarded fetch (fixed host + hostname assertion + redirect) ──
|
|
71
|
+
async function getOpengov(path, label, init = {}) {
|
|
72
|
+
const url = `https://${OPENGOV_HOST}${path}`;
|
|
73
|
+
const built = new URL(url);
|
|
74
|
+
if (built.hostname !== OPENGOV_HOST || built.protocol !== "https:") {
|
|
75
|
+
throw new ToolErrorCarrier({
|
|
76
|
+
kind: "invalid_input",
|
|
77
|
+
message: `Constructed OpenGov URL host ${JSON.stringify(built.hostname)} (${built.protocol}) does not match the fixed host ${JSON.stringify(OPENGOV_HOST)} over https — refusing to fetch (SSRF safety).`,
|
|
78
|
+
retryable: false,
|
|
79
|
+
upstreamEndpoint: label,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const headers = init.method === "POST" ? { "Content-Type": "application/json" } : {};
|
|
83
|
+
return getJson(url, { label, headers, redirect: "error", ...init });
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* List the OpenGov Procurement government portals (the whole directory arrives in
|
|
87
|
+
* ONE keyless GET). Client-side filters: `state` (2-letter), `query` (name
|
|
88
|
+
* substring). Only ACTIVE, non-internal portals are kept. Feeds `code` to
|
|
89
|
+
* opengov_search_solicitations. totalAvailable = the filtered directory size (P1).
|
|
90
|
+
*/
|
|
91
|
+
export async function listGovernments(args) {
|
|
92
|
+
const limit = args.limit ?? 50;
|
|
93
|
+
const offset = args.offset ?? 0;
|
|
94
|
+
let body;
|
|
95
|
+
try {
|
|
96
|
+
body = await getOpengov(OPENGOV_GOVERNMENT_PATH, OPENGOV_GOV_LABEL);
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
if (e instanceof ToolErrorCarrier)
|
|
100
|
+
throw e;
|
|
101
|
+
if (e instanceof SyntaxError)
|
|
102
|
+
throw driftError(OPENGOV_GOV_LABEL, "OpenGov /government returned a non-JSON body at HTTP 200 — schema drift.");
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
// P4: the response MUST be an array (a missing/object body is drift).
|
|
106
|
+
if (!Array.isArray(body)) {
|
|
107
|
+
throw driftError(OPENGOV_GOV_LABEL, "OpenGov /government shape drift — response must be a JSON array of governments.");
|
|
108
|
+
}
|
|
109
|
+
const stateFilter = args.state ? args.state.trim().toUpperCase() : null;
|
|
110
|
+
const queryFilter = args.query ? args.query.trim().toLowerCase() : null;
|
|
111
|
+
const filtersApplied = [];
|
|
112
|
+
if (stateFilter)
|
|
113
|
+
filtersApplied.push("state");
|
|
114
|
+
if (queryFilter)
|
|
115
|
+
filtersApplied.push("query");
|
|
116
|
+
const all = [];
|
|
117
|
+
for (const row of body) {
|
|
118
|
+
const r = (row ?? {});
|
|
119
|
+
// Keep only active, non-internal, non-vendor real public portals.
|
|
120
|
+
if (boolOrNull(r.isActive) === false)
|
|
121
|
+
continue;
|
|
122
|
+
if (boolOrNull(r.isInternal) === true)
|
|
123
|
+
continue;
|
|
124
|
+
const gov = (r.government ?? {});
|
|
125
|
+
const g = {
|
|
126
|
+
code: str(gov.code),
|
|
127
|
+
name: str(r.name),
|
|
128
|
+
city: str(r.city),
|
|
129
|
+
state: str(r.state),
|
|
130
|
+
website: str(r.website),
|
|
131
|
+
};
|
|
132
|
+
if (g.code === null)
|
|
133
|
+
continue; // no portal slug ⇒ not queryable
|
|
134
|
+
if (stateFilter && (g.state ?? "").toUpperCase() !== stateFilter)
|
|
135
|
+
continue;
|
|
136
|
+
if (queryFilter && !(g.name ?? "").toLowerCase().includes(queryFilter))
|
|
137
|
+
continue;
|
|
138
|
+
all.push(g);
|
|
139
|
+
}
|
|
140
|
+
const totalAvailable = all.length;
|
|
141
|
+
const page = all.slice(offset, offset + limit);
|
|
142
|
+
const returned = page.length;
|
|
143
|
+
const hasMore = offset + returned < totalAvailable;
|
|
144
|
+
const nextOffset = hasMore ? offset + returned : null;
|
|
145
|
+
const notes = [
|
|
146
|
+
"The full OpenGov Procurement directory is returned in one keyless call and filtered client-side; totalAvailable is the exact filtered portal count. Feed a result's `code` to opengov_search_solicitations.",
|
|
147
|
+
];
|
|
148
|
+
if (filtersApplied.length === 0) {
|
|
149
|
+
notes.push("No filters applied — this lists ALL active OpenGov portals. Add `state` and/or `query` to scope.");
|
|
150
|
+
}
|
|
151
|
+
return withMeta({ governments: page }, {
|
|
152
|
+
source: OPENGOV_SOURCE,
|
|
153
|
+
keylessMode: true,
|
|
154
|
+
returned,
|
|
155
|
+
totalAvailable,
|
|
156
|
+
filtersApplied,
|
|
157
|
+
filtersDropped: [],
|
|
158
|
+
fieldsUnavailable: [],
|
|
159
|
+
pagination: { offset, limit, hasMore, nextOffset },
|
|
160
|
+
notes,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* List an OpenGov government's public solicitations (POST /project/list with the
|
|
165
|
+
* REQUIRED publicView gate). Returns all public projects with `status` surfaced
|
|
166
|
+
* (filter status==='open' for live bids). totalAvailable = the API's `count` (the
|
|
167
|
+
* org's total public-project count — P1, never the page length).
|
|
168
|
+
*/
|
|
169
|
+
export async function searchSolicitations(args) {
|
|
170
|
+
const limit = args.limit ?? 50;
|
|
171
|
+
const offset = args.offset ?? 0;
|
|
172
|
+
const code = args.governmentCode ?? "";
|
|
173
|
+
// Belt-and-suspenders slug grammar (behind the server's Zod). A bad slug would
|
|
174
|
+
// ride the JSON body and either error or mis-scope — reject pre-fetch (0 fetch).
|
|
175
|
+
if (!OPENGOV_CODE_RE.test(code)) {
|
|
176
|
+
throw new ToolErrorCarrier({
|
|
177
|
+
kind: "invalid_input",
|
|
178
|
+
message: `Invalid OpenGov governmentCode ${JSON.stringify(code)} — expected a lowercase-alnum/hyphen portal slug (≤64 chars) from opengov_list_governments (e.g. 'santacruzca', 'u-46').`,
|
|
179
|
+
retryable: false,
|
|
180
|
+
upstreamEndpoint: OPENGOV_PROJ_LABEL,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
// The API paginates by 0-based `page` + `limit`. Map our offset → page.
|
|
184
|
+
const page = Math.floor(offset / limit);
|
|
185
|
+
const servedOffset = page * limit;
|
|
186
|
+
const payload = JSON.stringify({ governmentCode: code, publicView: true, page, limit });
|
|
187
|
+
let body;
|
|
188
|
+
try {
|
|
189
|
+
body = await getOpengov(OPENGOV_PROJECT_LIST_PATH, OPENGOV_PROJ_LABEL, { method: "POST", body: payload });
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
if (e instanceof ToolErrorCarrier)
|
|
193
|
+
throw e;
|
|
194
|
+
if (e instanceof SyntaxError)
|
|
195
|
+
throw driftError(OPENGOV_PROJ_LABEL, "OpenGov /project/list returned a non-JSON body at HTTP 200 — schema drift.");
|
|
196
|
+
throw e;
|
|
197
|
+
}
|
|
198
|
+
const b = (body ?? {});
|
|
199
|
+
// P4: `projects` MUST be an array (a missing/object projects is drift).
|
|
200
|
+
if (!Array.isArray(b.projects)) {
|
|
201
|
+
throw driftError(OPENGOV_PROJ_LABEL, "OpenGov /project/list shape drift — response.projects must be an array.");
|
|
202
|
+
}
|
|
203
|
+
const solicitations = b.projects.map((row) => {
|
|
204
|
+
const r = (row ?? {});
|
|
205
|
+
const id = num(r.id);
|
|
206
|
+
const codeForLink = code;
|
|
207
|
+
return {
|
|
208
|
+
id,
|
|
209
|
+
title: str(r.title),
|
|
210
|
+
solicitationNumber: str(r.financialId),
|
|
211
|
+
status: str(r.status),
|
|
212
|
+
type: str(r.type),
|
|
213
|
+
department: str(r.departmentName),
|
|
214
|
+
releaseDate: str(r.releaseProjectDate ?? r.postedAt),
|
|
215
|
+
proposalDeadline: str(r.proposalDeadline),
|
|
216
|
+
contactName: [str(r.contactFirstName), str(r.contactLastName)].filter((v) => v !== null).join(" ") || null,
|
|
217
|
+
link: id !== null ? `${OPENGOV_PORTAL}/${codeForLink}/projects/${id}` : null,
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
const returned = solicitations.length;
|
|
221
|
+
// P1: totalAvailable = the API `count` (total public projects), NEVER page length.
|
|
222
|
+
const totalAvailable = num(b.count);
|
|
223
|
+
const hasMore = totalAvailable !== null ? servedOffset + returned < totalAvailable : returned >= limit;
|
|
224
|
+
const nextOffset = hasMore ? servedOffset + returned : null;
|
|
225
|
+
const notes = [OPENGOV_STATUS_NOTE];
|
|
226
|
+
if (servedOffset !== offset) {
|
|
227
|
+
notes.push(`offset was snapped to the page boundary ${servedOffset} (OpenGov paginates by fixed pages of ${limit}).`);
|
|
228
|
+
}
|
|
229
|
+
if (totalAvailable === null) {
|
|
230
|
+
notes.push("totalAvailable is unknown (the API omitted `count`); completeness inferred from page fullness.");
|
|
231
|
+
}
|
|
232
|
+
return withMeta({ governmentCode: code, solicitations }, {
|
|
233
|
+
source: OPENGOV_SOURCE,
|
|
234
|
+
keylessMode: true,
|
|
235
|
+
returned,
|
|
236
|
+
totalAvailable,
|
|
237
|
+
filtersApplied: ["governmentCode", "publicView"],
|
|
238
|
+
filtersDropped: [],
|
|
239
|
+
fieldsUnavailable: [],
|
|
240
|
+
pagination: { offset: servedOffset, limit, hasMore, nextOffset },
|
|
241
|
+
notes,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=opengov.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opengov.js","sourceRoot":"","sources":["../src/opengov.ts"],"names":[],"mappings":"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"}
|
package/dist/pricing.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ export declare function benchmarkLaborRates(args: {
|
|
|
73
73
|
maxSamplePages?: number;
|
|
74
74
|
}): Promise<import("./meta.js").MetaBundle<{
|
|
75
75
|
laborCategory: string;
|
|
76
|
-
matcher: "
|
|
76
|
+
matcher: "q" | "search";
|
|
77
77
|
fuzzy: boolean;
|
|
78
78
|
matchCount: number;
|
|
79
79
|
matchCountSaturated: boolean;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,YAAY,EAKb,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,YAAY,EAKb,MAAM,oBAAoB,CAAC;AA4iJ5B,KAAK,OAAO,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC,UAAU,CAAC;IAO1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE;QAAE,GAAG,EAAE,YAAY,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxE,CAAC;AAoBF,eAAO,MAAM,KAAK,EAAE,OAAO,EAs0D1B,CAAC;AAkLF,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,OAAO,CAAC,CAkClB"}
|