company-dossier 0.2.1 → 0.3.1
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.md +59 -0
- package/dist/cli.js +388 -4
- package/dist/index.d.ts +33 -1
- package/dist/index.js +288 -4
- package/dist/mcp-http.js +1 -1
- package/dist/mcp-server.js +1 -1
- package/dist/mcp.js +1 -1
- package/dist/web.js +1542 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -140,10 +140,69 @@ Tool input (same as the stdio server):
|
|
|
140
140
|
{ "target": "acme.com", "sections": ["overview", "tech", "risk"] }
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
+
## Docs-site output
|
|
144
|
+
|
|
145
|
+
`company-dossier` can turn a generated dossier into a themed, **static
|
|
146
|
+
[Astro Starlight](https://starlight.astro.build) docs site** — one page per
|
|
147
|
+
section, an autogenerated sidebar, and a "case file" (ink-on-paper) theme.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# scaffold a Starlight site under "<Company> DOSSIER/site/"
|
|
151
|
+
company-dossier acme.com --out ./out --format site
|
|
152
|
+
|
|
153
|
+
# then build it (Node-only):
|
|
154
|
+
cd "./out/Acme DOSSIER/site" && npm install && npm run build # → site/dist
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Site-related flags:
|
|
158
|
+
|
|
159
|
+
| Flag | Default | Meaning |
|
|
160
|
+
|------|---------|---------|
|
|
161
|
+
| `--format <folder\|site>` | `folder` | `site` scaffolds the Starlight project. |
|
|
162
|
+
| `--deploy <none\|gh-pages>` | `none` | `gh-pages` installs, builds, and publishes `site/dist` via the [`gh-pages`](https://www.npmjs.com/package/gh-pages) package (an optional dep of the *generated* site). |
|
|
163
|
+
| `--subdomain <host>` | — | Writes `public/CNAME` for a custom host. |
|
|
164
|
+
| `--no-noindex` | (noindex on) | Opt out of the unlisted/noindex policy below. |
|
|
165
|
+
|
|
166
|
+
### Unlisted by default (policy)
|
|
167
|
+
|
|
168
|
+
Per-company dossier sites are **UNLISTED + NOINDEX by default**. Every generated
|
|
169
|
+
page carries:
|
|
170
|
+
|
|
171
|
+
- `<meta name="robots" content="noindex,nofollow">` (injected via Starlight `head`),
|
|
172
|
+
- a visible disclaimer — *"Auto-generated from public sources — may be
|
|
173
|
+
inaccurate; not affiliated with the company"* — plus a takedown/contact line, and
|
|
174
|
+
- a `public/robots.txt` with `Disallow: /` (and `public/.nojekyll`).
|
|
175
|
+
|
|
176
|
+
Pass `--no-noindex` to allow indexing (robots meta is dropped and robots.txt
|
|
177
|
+
becomes `Allow: /`).
|
|
178
|
+
|
|
179
|
+
The scaffold (`<Company> DOSSIER/site/`) contains `package.json`
|
|
180
|
+
(`astro` + `@astrojs/starlight`, optional `gh-pages`), `astro.config.mjs`,
|
|
181
|
+
`src/content/config.ts` (loose docs schema), `src/content/docs/*.md`
|
|
182
|
+
(one per section + an `index.md` hero from the overview),
|
|
183
|
+
`src/styles/case-file.css`, and `public/` assets.
|
|
184
|
+
|
|
185
|
+
### Library usage
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { buildDossier, writeDossier, generateSite } from 'company-dossier';
|
|
189
|
+
|
|
190
|
+
const result = await buildDossier('acme.com');
|
|
191
|
+
const folder = writeDossier(result, './out'); // "<Company> DOSSIER/"
|
|
192
|
+
const site = await generateSite(result, folder, { // → folder + "/site/"
|
|
193
|
+
noindex: true, // default; set false to allow indexing
|
|
194
|
+
subdomain: 'acme.example.com', // optional → public/CNAME
|
|
195
|
+
title: 'Acme', // optional override (defaults to company name)
|
|
196
|
+
});
|
|
197
|
+
console.log(site.siteDir, site.files.length);
|
|
198
|
+
```
|
|
199
|
+
|
|
143
200
|
## Output
|
|
144
201
|
|
|
145
202
|
- A `<Company> DOSSIER/` folder with `README.md`, nine numbered markdown
|
|
146
203
|
sections, and `dossier.json`.
|
|
204
|
+
- With `--format site`, additionally a `<Company> DOSSIER/site/` Astro
|
|
205
|
+
Starlight project (build it with `npm install && npm run build`).
|
|
147
206
|
- With `--json`, the structured dossier is printed to stdout instead.
|
|
148
207
|
|
|
149
208
|
## Public sources only
|
package/dist/cli.js
CHANGED
|
@@ -20,6 +20,9 @@ function writeFileSafe(filePath, content) {
|
|
|
20
20
|
function todayISO() {
|
|
21
21
|
return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
22
22
|
}
|
|
23
|
+
function slugify(name) {
|
|
24
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/_+$/, "").replace(/^_+/, "");
|
|
25
|
+
}
|
|
23
26
|
function titleCase(str) {
|
|
24
27
|
return str.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
25
28
|
}
|
|
@@ -913,7 +916,7 @@ var SECTIONS = [
|
|
|
913
916
|
"relationships",
|
|
914
917
|
"risk"
|
|
915
918
|
];
|
|
916
|
-
var GENERATOR = "company-dossier v0.
|
|
919
|
+
var GENERATOR = "company-dossier v0.3.0";
|
|
917
920
|
var HOMEPAGE = "https://companydossier.lol";
|
|
918
921
|
var GAP = "_Gap: no public data found \u2014 requires manual research._";
|
|
919
922
|
function deriveCompanyName(target, website) {
|
|
@@ -1405,19 +1408,304 @@ async function buildDossier(target, opts = {}) {
|
|
|
1405
1408
|
}
|
|
1406
1409
|
|
|
1407
1410
|
// src/index.ts
|
|
1411
|
+
import * as path3 from "path";
|
|
1412
|
+
|
|
1413
|
+
// src/generators/site.ts
|
|
1408
1414
|
import * as path2 from "path";
|
|
1415
|
+
var ASTRO_VERSION = "^5.7.0";
|
|
1416
|
+
var STARLIGHT_VERSION = "^0.34.0";
|
|
1417
|
+
var GH_PAGES_VERSION = "^6.3.0";
|
|
1418
|
+
var DISCLAIMER = "Auto-generated from public sources \u2014 may be inaccurate; not affiliated with the company.";
|
|
1419
|
+
var TAKEDOWN = "Takedown / contact: open an issue at https://github.com/ever-just/company-dossier/issues";
|
|
1420
|
+
function stripFrontmatter(content) {
|
|
1421
|
+
if (content.startsWith("---\n")) {
|
|
1422
|
+
const end = content.indexOf("\n---", 4);
|
|
1423
|
+
if (end !== -1) {
|
|
1424
|
+
const after = content.indexOf("\n", end + 1);
|
|
1425
|
+
return after !== -1 ? content.slice(after + 1) : "";
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
return content;
|
|
1429
|
+
}
|
|
1430
|
+
function extractTitle2(content, fallback) {
|
|
1431
|
+
const m = content.match(/^#\s+(.+?)\s*$/m);
|
|
1432
|
+
if (!m) return fallback;
|
|
1433
|
+
let t = m[1].trim();
|
|
1434
|
+
t = t.replace(/^\d+\.\s*/, "");
|
|
1435
|
+
t = t.replace(/\s*[—–-]\s*[^—–-]+$/, "");
|
|
1436
|
+
return t.trim() || fallback;
|
|
1437
|
+
}
|
|
1438
|
+
function orderFromFilename(filePath) {
|
|
1439
|
+
const m = path2.basename(filePath).match(/^(\d+)/);
|
|
1440
|
+
return m ? parseInt(m[1], 10) : void 0;
|
|
1441
|
+
}
|
|
1442
|
+
function yamlString(value) {
|
|
1443
|
+
return '"' + value.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
|
|
1444
|
+
}
|
|
1445
|
+
function isOverview(filePath) {
|
|
1446
|
+
const b = path2.basename(filePath).toLowerCase();
|
|
1447
|
+
return b === "readme.md" || /^0*1[_.]/.test(b) || b.includes("overview");
|
|
1448
|
+
}
|
|
1449
|
+
function buildPackageJson(name, deploy) {
|
|
1450
|
+
const pkg = {
|
|
1451
|
+
name: `${slugify(name) || "company"}-dossier-site`,
|
|
1452
|
+
private: true,
|
|
1453
|
+
version: "0.0.1",
|
|
1454
|
+
type: "module",
|
|
1455
|
+
scripts: {
|
|
1456
|
+
dev: "astro dev",
|
|
1457
|
+
build: "astro build",
|
|
1458
|
+
preview: "astro preview",
|
|
1459
|
+
...deploy ? { deploy: "gh-pages -d dist -t true" } : {}
|
|
1460
|
+
},
|
|
1461
|
+
dependencies: {
|
|
1462
|
+
astro: ASTRO_VERSION,
|
|
1463
|
+
"@astrojs/starlight": STARLIGHT_VERSION
|
|
1464
|
+
},
|
|
1465
|
+
// gh-pages is an optional dep of the GENERATED site, not the main package.
|
|
1466
|
+
optionalDependencies: {
|
|
1467
|
+
"gh-pages": GH_PAGES_VERSION
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
return JSON.stringify(pkg, null, 2) + "\n";
|
|
1471
|
+
}
|
|
1472
|
+
function buildAstroConfig(title, noindex) {
|
|
1473
|
+
const headEntry = noindex ? `
|
|
1474
|
+
head: [
|
|
1475
|
+
{
|
|
1476
|
+
tag: 'meta',
|
|
1477
|
+
attrs: { name: 'robots', content: 'noindex,nofollow' },
|
|
1478
|
+
},
|
|
1479
|
+
],` : "";
|
|
1480
|
+
return `import { defineConfig } from 'astro/config';
|
|
1481
|
+
import starlight from '@astrojs/starlight';
|
|
1482
|
+
|
|
1483
|
+
// Generated by company-dossier (--format site).
|
|
1484
|
+
// Per policy, per-company dossier sites are UNLISTED + NOINDEX by default.
|
|
1485
|
+
export default defineConfig({
|
|
1486
|
+
integrations: [
|
|
1487
|
+
starlight({
|
|
1488
|
+
title: ${yamlString(title)},
|
|
1489
|
+
description: ${yamlString(DISCLAIMER)},
|
|
1490
|
+
tableOfContents: true,
|
|
1491
|
+
customCss: ['./src/styles/case-file.css'],${headEntry}
|
|
1492
|
+
// Sidebar autogenerated from the docs tree.
|
|
1493
|
+
sidebar: [
|
|
1494
|
+
{
|
|
1495
|
+
label: 'Dossier',
|
|
1496
|
+
autogenerate: { directory: '.' },
|
|
1497
|
+
},
|
|
1498
|
+
],
|
|
1499
|
+
// Footer disclaimer + takedown line on every page.
|
|
1500
|
+
credits: false,
|
|
1501
|
+
lastUpdated: false,
|
|
1502
|
+
pagination: false,
|
|
1503
|
+
}),
|
|
1504
|
+
],
|
|
1505
|
+
});
|
|
1506
|
+
`;
|
|
1507
|
+
}
|
|
1508
|
+
function buildContentConfig() {
|
|
1509
|
+
return `import { defineCollection } from 'astro:content';
|
|
1510
|
+
import { docsLoader } from '@astrojs/starlight/loaders';
|
|
1511
|
+
import { docsSchema } from '@astrojs/starlight/schema';
|
|
1512
|
+
import { z } from 'astro:content';
|
|
1513
|
+
|
|
1514
|
+
export const collections = {
|
|
1515
|
+
docs: defineCollection({
|
|
1516
|
+
loader: docsLoader(),
|
|
1517
|
+
schema: docsSchema({
|
|
1518
|
+
// Tolerant extra frontmatter keys for generated content.
|
|
1519
|
+
extend: z.object({
|
|
1520
|
+
order: z.number().optional(),
|
|
1521
|
+
section: z.string().optional(),
|
|
1522
|
+
sourceFile: z.string().optional(),
|
|
1523
|
+
generatedAt: z.string().optional(),
|
|
1524
|
+
company: z.string().optional(),
|
|
1525
|
+
domain: z.string().optional(),
|
|
1526
|
+
customNote: z.string().optional(),
|
|
1527
|
+
}),
|
|
1528
|
+
}),
|
|
1529
|
+
}),
|
|
1530
|
+
};
|
|
1531
|
+
`;
|
|
1532
|
+
}
|
|
1533
|
+
function buildCaseFileCss() {
|
|
1534
|
+
return `/* company-dossier "case file" theme.
|
|
1535
|
+
Ink-on-paper light scheme expressed through Starlight design tokens. */
|
|
1536
|
+
|
|
1537
|
+
:root {
|
|
1538
|
+
/* Manila / paper accent (amber-brown ink stamp). */
|
|
1539
|
+
--sl-color-accent-low: #efe6d2;
|
|
1540
|
+
--sl-color-accent: #8a5a1f;
|
|
1541
|
+
--sl-color-accent-high: #4d3210;
|
|
1542
|
+
|
|
1543
|
+
/* Ink on aged paper. */
|
|
1544
|
+
--sl-color-white: #1c1a16;
|
|
1545
|
+
--sl-color-gray-1: #2a2620;
|
|
1546
|
+
--sl-color-gray-2: #423c31;
|
|
1547
|
+
--sl-color-gray-3: #6b6253;
|
|
1548
|
+
--sl-color-gray-4: #8c8270;
|
|
1549
|
+
--sl-color-gray-5: #b9ad95;
|
|
1550
|
+
--sl-color-gray-6: #e6dcc6;
|
|
1551
|
+
--sl-color-gray-7: #f1e9d6;
|
|
1552
|
+
--sl-color-black: #faf4e4;
|
|
1553
|
+
|
|
1554
|
+
--sl-color-bg: #faf4e4;
|
|
1555
|
+
--sl-color-bg-nav: #f1e9d6;
|
|
1556
|
+
--sl-color-bg-sidebar: #f4ecda;
|
|
1557
|
+
--sl-color-text: #1c1a16;
|
|
1558
|
+
--sl-color-text-accent: #8a5a1f;
|
|
1559
|
+
--sl-color-hairline: #d8cbac;
|
|
1560
|
+
--sl-color-hairline-light: #e6dcc6;
|
|
1561
|
+
|
|
1562
|
+
--sl-font: ui-serif, Georgia, 'Times New Roman', serif;
|
|
1563
|
+
--sl-font-mono: ui-monospace, 'SFMono-Regular', 'Courier New', monospace;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/* Keep a dark variant readable too (folder-at-night). */
|
|
1567
|
+
:root[data-theme='dark'] {
|
|
1568
|
+
--sl-color-accent-low: #2a2012;
|
|
1569
|
+
--sl-color-accent: #d9a441;
|
|
1570
|
+
--sl-color-accent-high: #f3d79a;
|
|
1571
|
+
--sl-color-bg: #16140f;
|
|
1572
|
+
--sl-color-bg-nav: #1d1a13;
|
|
1573
|
+
--sl-color-bg-sidebar: #1b1812;
|
|
1574
|
+
--sl-color-text: #ece2cc;
|
|
1575
|
+
--sl-color-hairline: #36301f;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
/* Markdown content reads like a typed report. */
|
|
1579
|
+
.sl-markdown-content {
|
|
1580
|
+
line-height: 1.6;
|
|
1581
|
+
}
|
|
1582
|
+
.sl-markdown-content table {
|
|
1583
|
+
font-family: var(--sl-font-mono);
|
|
1584
|
+
font-size: 0.85rem;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/* CONFIDENTIAL / disclaimer banner injected at the top of every page. */
|
|
1588
|
+
.dossier-banner {
|
|
1589
|
+
border: 1px dashed var(--sl-color-accent);
|
|
1590
|
+
background: var(--sl-color-accent-low);
|
|
1591
|
+
color: var(--sl-color-accent-high);
|
|
1592
|
+
padding: 0.6rem 0.9rem;
|
|
1593
|
+
margin: 0 0 1.25rem 0;
|
|
1594
|
+
border-radius: 4px;
|
|
1595
|
+
font-family: var(--sl-font-mono);
|
|
1596
|
+
font-size: 0.8rem;
|
|
1597
|
+
line-height: 1.45;
|
|
1598
|
+
}
|
|
1599
|
+
.dossier-banner strong {
|
|
1600
|
+
display: inline-block;
|
|
1601
|
+
letter-spacing: 0.18em;
|
|
1602
|
+
text-transform: uppercase;
|
|
1603
|
+
margin-right: 0.5rem;
|
|
1604
|
+
}
|
|
1605
|
+
.dossier-banner .dossier-takedown {
|
|
1606
|
+
display: block;
|
|
1607
|
+
margin-top: 0.35rem;
|
|
1608
|
+
opacity: 0.85;
|
|
1609
|
+
}
|
|
1610
|
+
`;
|
|
1611
|
+
}
|
|
1612
|
+
function buildRobotsTxt(noindex) {
|
|
1613
|
+
if (noindex) {
|
|
1614
|
+
return "User-agent: *\nDisallow: /\n";
|
|
1615
|
+
}
|
|
1616
|
+
return "User-agent: *\nAllow: /\n";
|
|
1617
|
+
}
|
|
1618
|
+
function disclaimerMarkdown() {
|
|
1619
|
+
return `:::caution[Disclaimer]
|
|
1620
|
+
**CONFIDENTIAL \u2014 UNVERIFIED.** ${DISCLAIMER}
|
|
1621
|
+
|
|
1622
|
+
${TAKEDOWN}
|
|
1623
|
+
:::
|
|
1624
|
+
|
|
1625
|
+
`;
|
|
1626
|
+
}
|
|
1627
|
+
function buildIndexDoc(result, files, title) {
|
|
1628
|
+
const overviewFile = files.find((f) => path2.basename(f.path).toLowerCase() === "readme.md") || files.find((f) => isOverview(f.path));
|
|
1629
|
+
const body = overviewFile ? stripFrontmatter(overviewFile.content) : "";
|
|
1630
|
+
const bodyNoH1 = body.replace(/^#\s+.+?\r?\n/, "");
|
|
1631
|
+
const frontmatter = [
|
|
1632
|
+
"---",
|
|
1633
|
+
`title: ${yamlString(title)}`,
|
|
1634
|
+
"template: splash",
|
|
1635
|
+
"hero:",
|
|
1636
|
+
` title: ${yamlString(title)}`,
|
|
1637
|
+
` tagline: ${yamlString("Intelligence dossier compiled from public sources.")}`,
|
|
1638
|
+
" actions:",
|
|
1639
|
+
` - text: Read the dossier`,
|
|
1640
|
+
` link: ./overview-identity/`,
|
|
1641
|
+
` icon: right-arrow`,
|
|
1642
|
+
"---",
|
|
1643
|
+
""
|
|
1644
|
+
].join("\n");
|
|
1645
|
+
return frontmatter + disclaimerMarkdown() + bodyNoH1.trim() + "\n";
|
|
1646
|
+
}
|
|
1647
|
+
function buildSectionDoc(file, title, order) {
|
|
1648
|
+
const body = stripFrontmatter(file.content).replace(/^#\s+.+?\r?\n/, "");
|
|
1649
|
+
const fm = ["---", `title: ${yamlString(title)}`];
|
|
1650
|
+
if (order !== void 0) {
|
|
1651
|
+
fm.push("sidebar:", ` order: ${order}`);
|
|
1652
|
+
}
|
|
1653
|
+
fm.push("---", "");
|
|
1654
|
+
return fm.join("\n") + disclaimerMarkdown() + body.trim() + "\n";
|
|
1655
|
+
}
|
|
1656
|
+
async function generateSite(result, outDir, opts = {}) {
|
|
1657
|
+
const noindex = opts.noindex !== false;
|
|
1658
|
+
const title = (opts.title || result.meta.companyName || "Company Dossier").trim();
|
|
1659
|
+
const siteDir = path2.join(outDir, "site");
|
|
1660
|
+
const written = [];
|
|
1661
|
+
const write = (rel, content) => {
|
|
1662
|
+
const abs = path2.join(siteDir, rel);
|
|
1663
|
+
writeFileSafe(abs, content);
|
|
1664
|
+
written.push(abs);
|
|
1665
|
+
};
|
|
1666
|
+
const files = result.files;
|
|
1667
|
+
write("package.json", buildPackageJson(title, true));
|
|
1668
|
+
write("astro.config.mjs", buildAstroConfig(title, noindex));
|
|
1669
|
+
write("src/content/config.ts", buildContentConfig());
|
|
1670
|
+
write("src/styles/case-file.css", buildCaseFileCss());
|
|
1671
|
+
write("tsconfig.json", JSON.stringify({ extends: "astro/tsconfigs/strict" }, null, 2) + "\n");
|
|
1672
|
+
write("src/content/docs/index.md", buildIndexDoc(result, files, title));
|
|
1673
|
+
const usedSlugs = /* @__PURE__ */ new Set(["index"]);
|
|
1674
|
+
for (const file of files) {
|
|
1675
|
+
const base = path2.basename(file.path).toLowerCase();
|
|
1676
|
+
if (base === "dossier.json") continue;
|
|
1677
|
+
if (base === "readme.md") continue;
|
|
1678
|
+
if (!base.endsWith(".md")) continue;
|
|
1679
|
+
const docTitle = extractTitle2(file.content, path2.basename(file.path, ".md"));
|
|
1680
|
+
let slug = slugify(docTitle).replace(/_/g, "-") || slugify(base.replace(/\.md$/, ""));
|
|
1681
|
+
let unique = slug;
|
|
1682
|
+
let n = 2;
|
|
1683
|
+
while (usedSlugs.has(unique)) unique = `${slug}-${n++}`;
|
|
1684
|
+
usedSlugs.add(unique);
|
|
1685
|
+
const order = orderFromFilename(file.path);
|
|
1686
|
+
write(`src/content/docs/${unique}.md`, buildSectionDoc(file, docTitle, order));
|
|
1687
|
+
}
|
|
1688
|
+
write("public/.nojekyll", "");
|
|
1689
|
+
write("public/robots.txt", buildRobotsTxt(noindex));
|
|
1690
|
+
if (opts.subdomain && opts.subdomain.trim()) {
|
|
1691
|
+
write("public/CNAME", opts.subdomain.trim() + "\n");
|
|
1692
|
+
}
|
|
1693
|
+
return { siteDir, files: written, noindex };
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
// src/index.ts
|
|
1409
1697
|
function writeDossier(result, outDir) {
|
|
1410
1698
|
const folderName = `${result.meta.companyName} DOSSIER`.trim();
|
|
1411
1699
|
const safeFolder = folderName.replace(/[/\\]/g, "_");
|
|
1412
|
-
const target =
|
|
1700
|
+
const target = path3.join(outDir, safeFolder);
|
|
1413
1701
|
for (const file of result.files) {
|
|
1414
|
-
writeFileSafe(
|
|
1702
|
+
writeFileSafe(path3.join(target, file.path), file.content);
|
|
1415
1703
|
}
|
|
1416
1704
|
return target;
|
|
1417
1705
|
}
|
|
1418
1706
|
|
|
1419
1707
|
// src/cli.ts
|
|
1420
|
-
var VERSION = "0.
|
|
1708
|
+
var VERSION = "0.3.0";
|
|
1421
1709
|
var HELP = `company-dossier v${VERSION}
|
|
1422
1710
|
Build a complete, sourced intelligence dossier on any company from public data.
|
|
1423
1711
|
|
|
@@ -1436,6 +1724,15 @@ OPTIONS
|
|
|
1436
1724
|
Available: ${SECTIONS.join(", ")}
|
|
1437
1725
|
--max-pages <n> Max internal pages to crawl (default: 25).
|
|
1438
1726
|
--no-social-probe Skip slow HEAD-probing of social platforms.
|
|
1727
|
+
--format <fmt> Output format: folder (default) or site.
|
|
1728
|
+
"site" scaffolds a themed, static Astro Starlight
|
|
1729
|
+
docs site under "<Company> DOSSIER/site/".
|
|
1730
|
+
--deploy <target> Deploy target for --format site: none (default) or
|
|
1731
|
+
gh-pages (builds site/dist and publishes via the
|
|
1732
|
+
gh-pages npm package).
|
|
1733
|
+
--subdomain <host> Custom host for the site; writes public/CNAME.
|
|
1734
|
+
--no-noindex Allow indexing. By POLICY, dossier sites are
|
|
1735
|
+
UNLISTED + NOINDEX by default; this opts out.
|
|
1439
1736
|
--quiet Suppress progress output.
|
|
1440
1737
|
-h, --help Show this help.
|
|
1441
1738
|
-v, --version Show version.
|
|
@@ -1450,6 +1747,12 @@ EXAMPLES
|
|
|
1450
1747
|
company-dossier "Acme Corporation"
|
|
1451
1748
|
company-dossier acme.com --json > acme.json
|
|
1452
1749
|
company-dossier acme.com --sections overview,tech,risk
|
|
1750
|
+
company-dossier acme.com --out ./out --format site
|
|
1751
|
+
company-dossier acme.com --format site --deploy gh-pages --subdomain acme.example.com
|
|
1752
|
+
|
|
1753
|
+
Generated dossier sites are UNLISTED + NOINDEX by default (noindex meta,
|
|
1754
|
+
robots.txt Disallow, and a visible "auto-generated / not affiliated"
|
|
1755
|
+
disclaimer on every page). Use --no-noindex to opt out.
|
|
1453
1756
|
|
|
1454
1757
|
Public sources only \u2014 no private databases, no API keys required.
|
|
1455
1758
|
Learn more: https://companydossier.lol
|
|
@@ -1460,6 +1763,9 @@ function parseArgs(argv) {
|
|
|
1460
1763
|
json: false,
|
|
1461
1764
|
quiet: false,
|
|
1462
1765
|
skipSocialProbe: false,
|
|
1766
|
+
format: "folder",
|
|
1767
|
+
deploy: "none",
|
|
1768
|
+
noindex: true,
|
|
1463
1769
|
help: false,
|
|
1464
1770
|
version: false
|
|
1465
1771
|
};
|
|
@@ -1483,6 +1789,30 @@ function parseArgs(argv) {
|
|
|
1483
1789
|
case "--no-social-probe":
|
|
1484
1790
|
out.skipSocialProbe = true;
|
|
1485
1791
|
break;
|
|
1792
|
+
case "--no-noindex":
|
|
1793
|
+
out.noindex = false;
|
|
1794
|
+
break;
|
|
1795
|
+
case "--format": {
|
|
1796
|
+
const v = (argv[++i] ?? "").trim().toLowerCase();
|
|
1797
|
+
if (v === "folder" || v === "site") {
|
|
1798
|
+
out.format = v;
|
|
1799
|
+
} else {
|
|
1800
|
+
out.error = `Invalid --format: ${v || "(missing)"}. Use folder or site.`;
|
|
1801
|
+
}
|
|
1802
|
+
break;
|
|
1803
|
+
}
|
|
1804
|
+
case "--deploy": {
|
|
1805
|
+
const v = (argv[++i] ?? "").trim().toLowerCase();
|
|
1806
|
+
if (v === "none" || v === "gh-pages") {
|
|
1807
|
+
out.deploy = v;
|
|
1808
|
+
} else {
|
|
1809
|
+
out.error = `Invalid --deploy: ${v || "(missing)"}. Use none or gh-pages.`;
|
|
1810
|
+
}
|
|
1811
|
+
break;
|
|
1812
|
+
}
|
|
1813
|
+
case "--subdomain":
|
|
1814
|
+
out.subdomain = (argv[++i] ?? "").trim() || void 0;
|
|
1815
|
+
break;
|
|
1486
1816
|
case "--out":
|
|
1487
1817
|
out.out = argv[++i] ?? out.out;
|
|
1488
1818
|
break;
|
|
@@ -1565,11 +1895,65 @@ Run "company-dossier --help".
|
|
|
1565
1895
|
log(`
|
|
1566
1896
|
Dossier written to: ${folder}`);
|
|
1567
1897
|
log(`Files: ${result.files.map((f) => f.path).join(", ")}`);
|
|
1898
|
+
if (args.format === "site") {
|
|
1899
|
+
log("\nScaffolding Astro Starlight docs site...");
|
|
1900
|
+
const site = await generateSite(result, folder, {
|
|
1901
|
+
subdomain: args.subdomain,
|
|
1902
|
+
noindex: args.noindex
|
|
1903
|
+
});
|
|
1904
|
+
log(`Site scaffolded at: ${site.siteDir}`);
|
|
1905
|
+
log(
|
|
1906
|
+
` ${site.files.length} files written` + (site.noindex ? " (UNLISTED + NOINDEX: robots meta + Disallow-all)" : " (indexable)")
|
|
1907
|
+
);
|
|
1908
|
+
log(` Build it: cd "${site.siteDir}" && npm install && npm run build`);
|
|
1909
|
+
if (args.deploy === "gh-pages") {
|
|
1910
|
+
const code = await deployGhPages(site.siteDir, log);
|
|
1911
|
+
if (code !== 0) return code;
|
|
1912
|
+
} else {
|
|
1913
|
+
log(" Deploy: skipped (--deploy none).");
|
|
1914
|
+
}
|
|
1915
|
+
if (!args.quiet) {
|
|
1916
|
+
process.stdout.write(site.siteDir + "\n");
|
|
1917
|
+
}
|
|
1918
|
+
return 0;
|
|
1919
|
+
}
|
|
1568
1920
|
if (!args.quiet) {
|
|
1569
1921
|
process.stdout.write(folder + "\n");
|
|
1570
1922
|
}
|
|
1571
1923
|
return 0;
|
|
1572
1924
|
}
|
|
1925
|
+
async function deployGhPages(siteDir, log) {
|
|
1926
|
+
const { spawn } = await import("child_process");
|
|
1927
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
1928
|
+
const run = (cmd, cmdArgs) => new Promise((resolve) => {
|
|
1929
|
+
log(` $ ${cmd} ${cmdArgs.join(" ")}`);
|
|
1930
|
+
const child = spawn(cmd, cmdArgs, { cwd: siteDir, stdio: "inherit" });
|
|
1931
|
+
child.on("error", (err) => {
|
|
1932
|
+
process.stderr.write(`Deploy error: ${err.message}
|
|
1933
|
+
`);
|
|
1934
|
+
resolve(1);
|
|
1935
|
+
});
|
|
1936
|
+
child.on("close", (c) => resolve(c ?? 0));
|
|
1937
|
+
});
|
|
1938
|
+
log("\nDeploying to GitHub Pages via gh-pages...");
|
|
1939
|
+
let code = await run(npmCmd, ["install"]);
|
|
1940
|
+
if (code !== 0) {
|
|
1941
|
+
process.stderr.write("Deploy aborted: npm install failed in site/.\n");
|
|
1942
|
+
return code;
|
|
1943
|
+
}
|
|
1944
|
+
code = await run(npmCmd, ["run", "build"]);
|
|
1945
|
+
if (code !== 0) {
|
|
1946
|
+
process.stderr.write("Deploy aborted: astro build failed.\n");
|
|
1947
|
+
return code;
|
|
1948
|
+
}
|
|
1949
|
+
code = await run(npmCmd, ["run", "deploy"]);
|
|
1950
|
+
if (code !== 0) {
|
|
1951
|
+
process.stderr.write("Deploy aborted: gh-pages publish failed.\n");
|
|
1952
|
+
return code;
|
|
1953
|
+
}
|
|
1954
|
+
log("Deployed site/dist to gh-pages branch.");
|
|
1955
|
+
return 0;
|
|
1956
|
+
}
|
|
1573
1957
|
main().then((code) => process.exit(code)).catch((err) => {
|
|
1574
1958
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
|
|
1575
1959
|
`);
|
package/dist/index.d.ts
CHANGED
|
@@ -200,10 +200,42 @@ interface DossierResult {
|
|
|
200
200
|
*/
|
|
201
201
|
declare function buildDossier(target: string, opts?: BuildOptions): Promise<DossierResult>;
|
|
202
202
|
|
|
203
|
+
interface GenerateSiteOptions {
|
|
204
|
+
/** Custom subdomain/host. When set, writes a public/CNAME file. */
|
|
205
|
+
subdomain?: string;
|
|
206
|
+
/**
|
|
207
|
+
* Inject `<meta name="robots" content="noindex,nofollow">` on every page
|
|
208
|
+
* and a Disallow-all robots.txt. Defaults to TRUE (unlisted by policy).
|
|
209
|
+
*/
|
|
210
|
+
noindex?: boolean;
|
|
211
|
+
/** Override the site title. Defaults to the dossier company name. */
|
|
212
|
+
title?: string;
|
|
213
|
+
}
|
|
214
|
+
interface GenerateSiteResult {
|
|
215
|
+
/** Absolute path to the generated `site/` directory. */
|
|
216
|
+
siteDir: string;
|
|
217
|
+
/** Absolute paths of every file written, relative to nothing — fully qualified. */
|
|
218
|
+
files: string[];
|
|
219
|
+
/** Whether noindex/unlisted policy was applied. */
|
|
220
|
+
noindex: boolean;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Turn a generated dossier into a themed, static Astro Starlight docs site.
|
|
224
|
+
*
|
|
225
|
+
* Scaffolds an Astro Starlight project under `<outDir>/site/`. Per policy,
|
|
226
|
+
* the site is UNLISTED + NOINDEX by default: every page carries a
|
|
227
|
+
* `noindex,nofollow` robots meta, a visible disclaimer, and a takedown line,
|
|
228
|
+
* and robots.txt disallows all crawling.
|
|
229
|
+
*
|
|
230
|
+
* Build it with `cd site && npm install && npm run build` (output in
|
|
231
|
+
* `site/dist`). Deploy is pluggable; pass a subdomain to emit a CNAME.
|
|
232
|
+
*/
|
|
233
|
+
declare function generateSite(result: DossierResult, outDir: string, opts?: GenerateSiteOptions): Promise<GenerateSiteResult>;
|
|
234
|
+
|
|
203
235
|
/**
|
|
204
236
|
* Write a built dossier to disk. Files are placed inside a
|
|
205
237
|
* "<Company> DOSSIER/" folder under `outDir`. Returns the folder path.
|
|
206
238
|
*/
|
|
207
239
|
declare function writeDossier(result: DossierResult, outDir: string): string;
|
|
208
240
|
|
|
209
|
-
export { type BuildOptions, type DnsData, type DossierData, type DossierFile, type DossierMeta, type DossierResult, type PageData, type ProgressCallback, SECTIONS, type SearchData, type SectionId, type SocialProfile, type TechStackData, type USASpendingContract, type WaybackData, type WebsiteData, buildDossier, collectDns, collectSearch, collectWayback, collectWebsite, extractTechStack, slugify, writeDossier };
|
|
241
|
+
export { type BuildOptions, type DnsData, type DossierData, type DossierFile, type DossierMeta, type DossierResult, type GenerateSiteOptions, type GenerateSiteResult, type PageData, type ProgressCallback, SECTIONS, type SearchData, type SectionId, type SocialProfile, type TechStackData, type USASpendingContract, type WaybackData, type WebsiteData, buildDossier, collectDns, collectSearch, collectWayback, collectWebsite, extractTechStack, generateSite, slugify, writeDossier };
|