@xdsjs/dossierx-daemon 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +447 -47
  2. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { mkdir as mkdir6, stat as stat4 } from "fs/promises";
4
+ import { mkdir as mkdir7, stat as stat4 } from "fs/promises";
5
5
  import os3 from "os";
6
- import path9 from "path";
6
+ import path10 from "path";
7
7
  import { Command } from "commander";
8
8
  import { DOSSIERX_DEFAULT_WORKSPACE_PATH } from "@xdsjs/dossierx-workspace";
9
9
 
@@ -30,8 +30,8 @@ var ApiClient = class {
30
30
  this.machineKey = options.machineKey;
31
31
  this.fetchImpl = options.fetchImpl ?? fetch;
32
32
  }
33
- async post(path10, body, schema) {
34
- const response = await this.fetchImpl(`${this.serverUrl}${path10}`, {
33
+ async post(path11, body, schema) {
34
+ const response = await this.fetchImpl(`${this.serverUrl}${path11}`, {
35
35
  method: "POST",
36
36
  headers: {
37
37
  authorization: `Bearer ${this.machineKey}`,
@@ -738,6 +738,7 @@ import { readFile as readFile3, realpath, stat } from "fs/promises";
738
738
  import path5 from "path";
739
739
  import { execa as execa3 } from "execa";
740
740
  import {
741
+ readCoverageContractBundle,
741
742
  resolveInsideWorkspace as resolveInsideWorkspace2,
742
743
  scanCompanyManifest
743
744
  } from "@xdsjs/dossierx-workspace";
@@ -819,7 +820,7 @@ function createTaskArchive(options) {
819
820
 
820
821
  // src/version.ts
821
822
  var DAEMON_PACKAGE_NAME = "@xdsjs/dossierx-daemon";
822
- var DAEMON_VERSION = "0.1.2";
823
+ var DAEMON_VERSION = "0.1.4";
823
824
 
824
825
  // src/local-api/server.ts
825
826
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
@@ -1191,6 +1192,34 @@ async function startWorkspaceReadServer(options) {
1191
1192
  }
1192
1193
  return;
1193
1194
  }
1195
+ if (request.method === "GET" && url.pathname === "/coverage/bundle") {
1196
+ const bundlePath = url.searchParams.get("path");
1197
+ if (!bundlePath) {
1198
+ json(response, 400, { error: "Missing coverage bundle path" }, origin);
1199
+ return;
1200
+ }
1201
+ try {
1202
+ json(
1203
+ response,
1204
+ 200,
1205
+ await readCoverageContractBundle({
1206
+ workspaceRoot: options.workspaceRoot,
1207
+ bundlePath
1208
+ }),
1209
+ origin
1210
+ );
1211
+ } catch (error) {
1212
+ json(
1213
+ response,
1214
+ 400,
1215
+ {
1216
+ error: error instanceof Error ? error.message : "Coverage bundle read failed"
1217
+ },
1218
+ origin
1219
+ );
1220
+ }
1221
+ return;
1222
+ }
1194
1223
  if (request.method !== "GET" || url.pathname !== "/workspace/read") {
1195
1224
  json(response, 404, { error: "Not found" }, origin);
1196
1225
  return;
@@ -1460,20 +1489,364 @@ async function runCodexTask(task, context) {
1460
1489
  });
1461
1490
  return {
1462
1491
  generatedFiles: [],
1492
+ blockingReasons: [],
1463
1493
  manifestPath: companyManifestPath(ticker),
1464
1494
  manifest
1465
1495
  };
1466
1496
  }
1467
1497
 
1468
- // src/executors/investWiki.ts
1469
- import { access as access2, mkdir as mkdir3, readFile as readFile4, stat as stat3, writeFile as writeFile2 } from "fs/promises";
1498
+ // src/executors/financialReports.ts
1499
+ import { createHash } from "crypto";
1500
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
1470
1501
  import path6 from "path";
1502
+ import {
1503
+ FinancialReportManifestSchema,
1504
+ assessReportCoverage,
1505
+ downloadCninfoReports,
1506
+ downloadSecReports,
1507
+ primaryMarket,
1508
+ primaryTicker,
1509
+ runNotebookLmFinancialReportsIngest
1510
+ } from "@xdsjs/dossier-financial-reports";
1471
1511
  import {
1472
1512
  buildCompanyManifest as buildCompanyManifest2,
1473
1513
  companyManifestPath as companyManifestPath2,
1514
+ financialReportsIndexPath,
1515
+ resolveInsideWorkspace as resolveInsideWorkspace4
1516
+ } from "@xdsjs/dossierx-workspace";
1517
+ var FinancialReportsRuntimeError = class extends Error {
1518
+ code;
1519
+ step;
1520
+ constructor(code, message, step) {
1521
+ super(message);
1522
+ this.name = "FinancialReportsRuntimeError";
1523
+ this.code = code;
1524
+ this.step = step;
1525
+ }
1526
+ };
1527
+ function defaultFetch() {
1528
+ return async (url, init) => fetch(url, init);
1529
+ }
1530
+ function defaultRenderHtmlToPdf() {
1531
+ return async () => {
1532
+ throw new FinancialReportsRuntimeError(
1533
+ "RUNTIME_NOT_FOUND",
1534
+ "SEC HTML-to-PDF renderer is not configured",
1535
+ "financial_reports.render_html_to_pdf"
1536
+ );
1537
+ };
1538
+ }
1539
+ function requireNotebookLmClient(context) {
1540
+ const client = context.financialReports?.notebookLmClient;
1541
+ if (!client) {
1542
+ throw new FinancialReportsRuntimeError(
1543
+ "RUNTIME_NOT_FOUND",
1544
+ "NotebookLM client is not configured",
1545
+ "financial_reports.notebooklm.resolve"
1546
+ );
1547
+ }
1548
+ return client;
1549
+ }
1550
+ function marketToAppMarket(market) {
1551
+ if (market === "US") {
1552
+ return "us";
1553
+ }
1554
+ if (market === "H") {
1555
+ return "hk";
1556
+ }
1557
+ return "cn";
1558
+ }
1559
+ function sha256Json(value) {
1560
+ return `sha256:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
1561
+ }
1562
+ function writeWorkspaceJson(context, relativePath, value) {
1563
+ return writeWorkspaceBytes(
1564
+ context,
1565
+ relativePath,
1566
+ new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
1567
+ `)
1568
+ );
1569
+ }
1570
+ async function writeWorkspaceBytes(context, relativePath, bytes) {
1571
+ const absolutePath = resolveInsideWorkspace4(context.workspaceRoot, relativePath);
1572
+ await mkdir3(path6.dirname(absolutePath), { recursive: true });
1573
+ if (!context.dryRun) {
1574
+ await writeFile2(absolutePath, bytes);
1575
+ }
1576
+ }
1577
+ function createWorkspaceStorage(context) {
1578
+ return {
1579
+ async writeFile(relativePath, bytes) {
1580
+ await writeWorkspaceBytes(context, relativePath, bytes);
1581
+ }
1582
+ };
1583
+ }
1584
+ function readWorkspaceJson(context, relativePath) {
1585
+ const absolutePath = resolveInsideWorkspace4(context.workspaceRoot, relativePath);
1586
+ return readFile4(absolutePath, "utf8").then((content) => JSON.parse(content));
1587
+ }
1588
+ function parseReportManifest(payload) {
1589
+ const maybeRecord = typeof payload === "object" && payload !== null ? payload : {};
1590
+ if (maybeRecord.schemaVersion === "financial-reports/manifest/v1") {
1591
+ return FinancialReportManifestSchema.parse(payload);
1592
+ }
1593
+ return FinancialReportManifestSchema.parse(maybeRecord.manifest);
1594
+ }
1595
+ async function readReportManifest(context, manifestPath) {
1596
+ try {
1597
+ return parseReportManifest(await readWorkspaceJson(context, manifestPath));
1598
+ } catch (error) {
1599
+ if (error instanceof SyntaxError) {
1600
+ throw new FinancialReportsRuntimeError(
1601
+ "VALIDATION_ERROR",
1602
+ "Financial reports manifest is not valid JSON",
1603
+ "financial_reports.manifest.read"
1604
+ );
1605
+ }
1606
+ throw error;
1607
+ }
1608
+ }
1609
+ function assertManifestIssuerMatchesTaskIssuer(manifestIssuer, taskIssuer) {
1610
+ const mismatches = [];
1611
+ if (manifestIssuer.issuerId !== taskIssuer.issuerId) {
1612
+ mismatches.push("issuerId");
1613
+ }
1614
+ if (manifestIssuer.companyId !== taskIssuer.companyId) {
1615
+ mismatches.push("companyId");
1616
+ }
1617
+ if (primaryTicker(manifestIssuer) !== primaryTicker(taskIssuer)) {
1618
+ mismatches.push("primary ticker");
1619
+ }
1620
+ if (primaryMarket(manifestIssuer) !== primaryMarket(taskIssuer)) {
1621
+ mismatches.push("primary market");
1622
+ }
1623
+ if (mismatches.length > 0) {
1624
+ throw new FinancialReportsRuntimeError(
1625
+ "VALIDATION_ERROR",
1626
+ `Financial reports manifest issuer does not match task payload issuer: ${mismatches.join(", ")}`,
1627
+ "financial_reports.manifest.validate"
1628
+ );
1629
+ }
1630
+ }
1631
+ function financialTaskResult(input) {
1632
+ const ticker = primaryTicker(input.manifest.issuer);
1633
+ const coverage = assessReportCoverage(input.manifest);
1634
+ return {
1635
+ schemaVersion: "financial-reports/task-result/v1",
1636
+ issuerId: input.manifest.issuer.issuerId,
1637
+ companyId: input.manifest.issuer.companyId,
1638
+ ticker,
1639
+ manifestPath: input.manifestPath,
1640
+ manifestHash: input.manifestHash,
1641
+ notebookPath: input.notebookPath,
1642
+ factsBundlePath: input.factsBundlePath,
1643
+ reportCount: input.manifest.reports.length,
1644
+ coverageStatus: coverage.status,
1645
+ blockingReasons: coverage.knownGaps
1646
+ };
1647
+ }
1648
+ async function runSyncReports(task, context) {
1649
+ const issuer = task.payload.issuer;
1650
+ const market = primaryMarket(issuer);
1651
+ const fetch2 = context.financialReports?.fetch ?? defaultFetch();
1652
+ const storage = createWorkspaceStorage(context);
1653
+ const now = context.financialReports?.now;
1654
+ let result;
1655
+ await context.appendEvent({
1656
+ level: "info",
1657
+ message: "Starting financial reports sync",
1658
+ data: { issuerId: issuer.issuerId, market }
1659
+ });
1660
+ if (market === "US") {
1661
+ result = await downloadSecReports({
1662
+ issuer,
1663
+ fiscalYears: task.payload.fiscalYears,
1664
+ reportTypes: task.payload.reportTypes,
1665
+ fetch: fetch2,
1666
+ storage,
1667
+ renderHtmlToPdf: context.financialReports?.renderHtmlToPdf ?? defaultRenderHtmlToPdf(),
1668
+ now
1669
+ });
1670
+ } else {
1671
+ result = await downloadCninfoReports({
1672
+ issuer,
1673
+ fiscalYears: task.payload.fiscalYears,
1674
+ reportTypes: task.payload.reportTypes,
1675
+ fetch: fetch2,
1676
+ storage,
1677
+ now
1678
+ });
1679
+ }
1680
+ const ticker = primaryTicker(result.manifest.issuer);
1681
+ const reportIndexPath = financialReportsIndexPath(ticker);
1682
+ const manifestHash = sha256Json(result.manifest);
1683
+ const reportIndex = {
1684
+ schemaVersion: "financial-reports/report-index/v1",
1685
+ generatedAt: result.manifest.generatedAt,
1686
+ issuer: result.manifest.issuer,
1687
+ manifestHash,
1688
+ manifest: result.manifest,
1689
+ coverage: result.coverage,
1690
+ skippedReports: result.skippedReports,
1691
+ missingReports: result.missingReports
1692
+ };
1693
+ await writeWorkspaceJson(context, reportIndexPath, reportIndex);
1694
+ const companyManifest = context.dryRun ? void 0 : await buildCompanyManifest2({
1695
+ workspaceRoot: context.workspaceRoot,
1696
+ ticker,
1697
+ market: marketToAppMarket(market)
1698
+ });
1699
+ await context.appendEvent({
1700
+ level: "info",
1701
+ message: "Financial reports sync completed",
1702
+ data: {
1703
+ issuerId: issuer.issuerId,
1704
+ coverageStatus: result.coverage.status,
1705
+ reportCount: result.manifest.reports.length,
1706
+ manifestPath: reportIndexPath
1707
+ }
1708
+ });
1709
+ return {
1710
+ generatedFiles: [
1711
+ ...result.manifest.reports.map((report) => report.localPath),
1712
+ reportIndexPath,
1713
+ companyManifestPath2(ticker)
1714
+ ],
1715
+ manifestPath: companyManifestPath2(ticker),
1716
+ manifest: companyManifest,
1717
+ blockingReasons: [],
1718
+ financialReports: financialTaskResult({
1719
+ manifest: result.manifest,
1720
+ manifestPath: reportIndexPath,
1721
+ manifestHash
1722
+ })
1723
+ };
1724
+ }
1725
+ async function runNotebookIngest(task, context) {
1726
+ const client = requireNotebookLmClient(context);
1727
+ const manifest = await readReportManifest(context, task.payload.manifestPath);
1728
+ assertManifestIssuerMatchesTaskIssuer(manifest.issuer, task.payload.issuer);
1729
+ const ticker = primaryTicker(manifest.issuer);
1730
+ const manifestHash = sha256Json(manifest);
1731
+ const factsAsOf = task.payload.factsAsOf ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1732
+ await context.appendEvent({
1733
+ level: "info",
1734
+ message: "Starting NotebookLM financial reports ingest",
1735
+ data: { issuerId: manifest.issuer.issuerId, manifestPath: task.payload.manifestPath }
1736
+ });
1737
+ const ingest = await runNotebookLmFinancialReportsIngest({
1738
+ manifest,
1739
+ client,
1740
+ manifestHash,
1741
+ factsAsOf,
1742
+ generatedAt: context.financialReports?.now?.(),
1743
+ notebookTitle: task.payload.notebookTitle
1744
+ });
1745
+ const notebookPath = `companies/${ticker}/financial-reports/notebooklm/notebook.json`;
1746
+ const factsBundlePath = `companies/${ticker}/financial-reports/ingest/facts-bundle.json`;
1747
+ await writeWorkspaceJson(context, notebookPath, {
1748
+ schemaVersion: "financial-reports/notebooklm-run/v1",
1749
+ generatedAt: ingest.factsBundle.generatedAt,
1750
+ manifestPath: task.payload.manifestPath,
1751
+ notebooklm: ingest.notebooklm,
1752
+ sourceIds: ingest.sourceIds
1753
+ });
1754
+ await writeWorkspaceJson(context, factsBundlePath, ingest.factsBundle);
1755
+ const companyManifest = context.dryRun ? void 0 : await buildCompanyManifest2({
1756
+ workspaceRoot: context.workspaceRoot,
1757
+ ticker,
1758
+ market: marketToAppMarket(primaryMarket(manifest.issuer))
1759
+ });
1760
+ await context.appendEvent({
1761
+ level: "info",
1762
+ message: "NotebookLM financial reports ingest completed",
1763
+ data: {
1764
+ issuerId: manifest.issuer.issuerId,
1765
+ notebookPath,
1766
+ factsBundlePath
1767
+ }
1768
+ });
1769
+ return {
1770
+ generatedFiles: [notebookPath, factsBundlePath, companyManifestPath2(ticker)],
1771
+ manifestPath: companyManifestPath2(ticker),
1772
+ manifest: companyManifest,
1773
+ blockingReasons: [],
1774
+ financialReports: financialTaskResult({
1775
+ manifest,
1776
+ manifestPath: task.payload.manifestPath,
1777
+ manifestHash,
1778
+ notebookPath,
1779
+ factsBundlePath
1780
+ })
1781
+ };
1782
+ }
1783
+ async function runRefreshCompany(task, context) {
1784
+ const syncResult = await runSyncReports(
1785
+ {
1786
+ id: task.id,
1787
+ type: "financial_reports.sync_reports",
1788
+ payload: {
1789
+ issuer: task.payload.issuer,
1790
+ fiscalYears: task.payload.fiscalYears,
1791
+ reportTypes: task.payload.reportTypes
1792
+ }
1793
+ },
1794
+ context
1795
+ );
1796
+ if (!context.financialReports?.notebookLmClient) {
1797
+ return syncResult;
1798
+ }
1799
+ const manifestPath = syncResult.financialReports?.manifestPath;
1800
+ if (!manifestPath) {
1801
+ return syncResult;
1802
+ }
1803
+ const ingestResult = await runNotebookIngest(
1804
+ {
1805
+ id: task.id,
1806
+ type: "financial_reports.ingest_notebook",
1807
+ payload: {
1808
+ issuer: task.payload.issuer,
1809
+ manifestPath,
1810
+ factsAsOf: task.payload.factsAsOf,
1811
+ notebookTitle: task.payload.notebookTitle
1812
+ }
1813
+ },
1814
+ context
1815
+ );
1816
+ return {
1817
+ ...ingestResult,
1818
+ generatedFiles: [
1819
+ ...syncResult.generatedFiles,
1820
+ ...ingestResult.generatedFiles
1821
+ ]
1822
+ };
1823
+ }
1824
+ async function runFinancialReportsTask(task, context) {
1825
+ if (task.type === "financial_reports.sync_reports") {
1826
+ return runSyncReports(task, context);
1827
+ }
1828
+ if (task.type === "financial_reports.ingest_notebook" || task.type === "financial_reports.extract_facts") {
1829
+ return runNotebookIngest(task, context);
1830
+ }
1831
+ if (task.type === "financial_reports.refresh_company") {
1832
+ return runRefreshCompany(task, context);
1833
+ }
1834
+ throw new FinancialReportsRuntimeError(
1835
+ "VALIDATION_ERROR",
1836
+ "Unsupported financial reports task type",
1837
+ "financial_reports.dispatch"
1838
+ );
1839
+ }
1840
+
1841
+ // src/executors/investWiki.ts
1842
+ import { access as access2, mkdir as mkdir4, readFile as readFile5, stat as stat3, writeFile as writeFile3 } from "fs/promises";
1843
+ import path7 from "path";
1844
+ import {
1845
+ buildCompanyManifest as buildCompanyManifest3,
1846
+ companyManifestPath as companyManifestPath3,
1474
1847
  investWikiConfigPath,
1475
1848
  investWikiRoot as investWikiRoot2,
1476
- resolveInsideWorkspace as resolveInsideWorkspace4
1849
+ resolveInsideWorkspace as resolveInsideWorkspace5
1477
1850
  } from "@xdsjs/dossierx-workspace";
1478
1851
  async function exists(absolutePath) {
1479
1852
  try {
@@ -1498,7 +1871,7 @@ function isWorkspaceGuardError2(error) {
1498
1871
  }
1499
1872
  function workspacePath2(workspaceRoot, relativePath, step) {
1500
1873
  try {
1501
- return resolveInsideWorkspace4(workspaceRoot, relativePath);
1874
+ return resolveInsideWorkspace5(workspaceRoot, relativePath);
1502
1875
  } catch (error) {
1503
1876
  if (isWorkspaceGuardError2(error)) {
1504
1877
  throw new InvestWikiRuntimeError(
@@ -1579,11 +1952,11 @@ function isMarket(value) {
1579
1952
  async function readManifestMarket(workspaceRoot, ticker) {
1580
1953
  const manifestPath = workspacePath2(
1581
1954
  workspaceRoot,
1582
- companyManifestPath2(ticker),
1955
+ companyManifestPath3(ticker),
1583
1956
  "invest_wiki.sync"
1584
1957
  );
1585
1958
  try {
1586
- const manifest = JSON.parse(await readFile4(manifestPath, "utf8"));
1959
+ const manifest = JSON.parse(await readFile5(manifestPath, "utf8"));
1587
1960
  if (!isMarket(manifest.market)) {
1588
1961
  throw new InvestWikiRuntimeError(
1589
1962
  "VALIDATION_ERROR",
@@ -1626,7 +1999,7 @@ async function runInitCompanyVault(task, context) {
1626
1999
  `${vaultRelativePath}/.llm-wiki-invest/dossier-state.json`,
1627
2000
  "invest_wiki.init_company_vault"
1628
2001
  );
1629
- await mkdir3(vaultRoot, { recursive: true });
2002
+ await mkdir4(vaultRoot, { recursive: true });
1630
2003
  await context.appendEvent({
1631
2004
  level: "info",
1632
2005
  message: "Initializing invest-wiki vault",
@@ -1645,10 +2018,10 @@ async function runInitCompanyVault(task, context) {
1645
2018
  const statusOutput = await runner.run(["dossier", "status"], { cwd: vaultRoot });
1646
2019
  await appendOutputEvent2(context, "Invest wiki dossier status completed", statusOutput);
1647
2020
  if (!await exists(markerPath)) {
1648
- await mkdir3(path6.dirname(markerPath), { recursive: true });
1649
- await writeFile2(markerPath, "# Created by dossierx-daemon\n");
2021
+ await mkdir4(path7.dirname(markerPath), { recursive: true });
2022
+ await writeFile3(markerPath, "# Created by dossierx-daemon\n");
1650
2023
  }
1651
- const manifest = await buildCompanyManifest2({
2024
+ const manifest = await buildCompanyManifest3({
1652
2025
  workspaceRoot: context.workspaceRoot,
1653
2026
  ticker,
1654
2027
  market: task.payload.market
@@ -1662,9 +2035,10 @@ async function runInitCompanyVault(task, context) {
1662
2035
  generatedFiles: [
1663
2036
  vaultRelativePath,
1664
2037
  markerRelativePath,
1665
- companyManifestPath2(ticker)
2038
+ companyManifestPath3(ticker)
1666
2039
  ],
1667
- manifestPath: companyManifestPath2(ticker),
2040
+ blockingReasons: [],
2041
+ manifestPath: companyManifestPath3(ticker),
1668
2042
  manifest
1669
2043
  };
1670
2044
  }
@@ -1688,7 +2062,8 @@ async function runStatus(task, context) {
1688
2062
  );
1689
2063
  return {
1690
2064
  generatedFiles: [],
1691
- manifestPath: companyManifestPath2(ticker)
2065
+ blockingReasons: [],
2066
+ manifestPath: companyManifestPath3(ticker)
1692
2067
  };
1693
2068
  }
1694
2069
  async function runSync(task, context) {
@@ -1703,14 +2078,15 @@ async function runSync(task, context) {
1703
2078
  const args = task.payload.dryRun ? ["sync", "--dry-run"] : ["sync"];
1704
2079
  const output = await runner.run(args, { cwd: vaultRoot });
1705
2080
  await appendOutputEvent2(context, "Invest wiki sync completed", output);
1706
- const manifest = await buildCompanyManifest2({
2081
+ const manifest = await buildCompanyManifest3({
1707
2082
  workspaceRoot: context.workspaceRoot,
1708
2083
  ticker,
1709
2084
  market
1710
2085
  });
1711
2086
  return {
1712
2087
  generatedFiles: [],
1713
- manifestPath: companyManifestPath2(ticker),
2088
+ blockingReasons: [],
2089
+ manifestPath: companyManifestPath3(ticker),
1714
2090
  manifest
1715
2091
  };
1716
2092
  }
@@ -1732,13 +2108,13 @@ async function runInvestWikiTask(task, context) {
1732
2108
  }
1733
2109
 
1734
2110
  // src/executors/mockWriteCompanyReport.ts
1735
- import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
1736
- import path7 from "path";
2111
+ import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
2112
+ import path8 from "path";
1737
2113
  import {
1738
- buildCompanyManifest as buildCompanyManifest3,
1739
- companyManifestPath as companyManifestPath3,
2114
+ buildCompanyManifest as buildCompanyManifest4,
2115
+ companyManifestPath as companyManifestPath4,
1740
2116
  companyRoot,
1741
- resolveInsideWorkspace as resolveInsideWorkspace5,
2117
+ resolveInsideWorkspace as resolveInsideWorkspace6,
1742
2118
  rightBusinessPath,
1743
2119
  rightPeoplePath,
1744
2120
  rightPricePath
@@ -1772,10 +2148,10 @@ var rightPrice = (ticker) => `# Right Price - ${ticker}
1772
2148
  This is a placeholder right-price analysis for ${ticker}.
1773
2149
  `;
1774
2150
  async function writeWorkspaceFile(context, relativePath, content) {
1775
- const absolutePath = resolveInsideWorkspace5(context.workspaceRoot, relativePath);
1776
- await mkdir4(path7.dirname(absolutePath), { recursive: true });
2151
+ const absolutePath = resolveInsideWorkspace6(context.workspaceRoot, relativePath);
2152
+ await mkdir5(path8.dirname(absolutePath), { recursive: true });
1777
2153
  if (!context.dryRun) {
1778
- await writeFile3(absolutePath, content);
2154
+ await writeFile4(absolutePath, content);
1779
2155
  }
1780
2156
  }
1781
2157
  async function runMockWriteCompanyReport(task, context) {
@@ -1790,7 +2166,7 @@ async function runMockWriteCompanyReport(task, context) {
1790
2166
  message: "Creating company directory",
1791
2167
  data: { ticker }
1792
2168
  });
1793
- await mkdir4(resolveInsideWorkspace5(context.workspaceRoot, companyRoot(ticker)), {
2169
+ await mkdir5(resolveInsideWorkspace6(context.workspaceRoot, companyRoot(ticker)), {
1794
2170
  recursive: true
1795
2171
  });
1796
2172
  await context.appendEvent({
@@ -1814,11 +2190,11 @@ async function runMockWriteCompanyReport(task, context) {
1814
2190
  await context.appendEvent({
1815
2191
  level: "info",
1816
2192
  message: "Generating manifest.json",
1817
- data: { path: companyManifestPath3(ticker) }
2193
+ data: { path: companyManifestPath4(ticker) }
1818
2194
  });
1819
2195
  let manifest;
1820
2196
  if (!context.dryRun) {
1821
- manifest = await buildCompanyManifest3({
2197
+ manifest = await buildCompanyManifest4({
1822
2198
  workspaceRoot: context.workspaceRoot,
1823
2199
  ticker,
1824
2200
  market: task.payload.market
@@ -1830,8 +2206,9 @@ async function runMockWriteCompanyReport(task, context) {
1830
2206
  data: { ticker }
1831
2207
  });
1832
2208
  return {
1833
- generatedFiles: [...generatedFiles, companyManifestPath3(ticker)],
1834
- manifestPath: companyManifestPath3(ticker),
2209
+ generatedFiles: [...generatedFiles, companyManifestPath4(ticker)],
2210
+ blockingReasons: [],
2211
+ manifestPath: companyManifestPath4(ticker),
1835
2212
  manifest
1836
2213
  };
1837
2214
  }
@@ -1844,6 +2221,9 @@ function getExecutor(task) {
1844
2221
  if (task.type.startsWith("invest_wiki.")) {
1845
2222
  return runInvestWikiTask;
1846
2223
  }
2224
+ if (task.type.startsWith("financial_reports.")) {
2225
+ return runFinancialReportsTask;
2226
+ }
1847
2227
  if (task.type === "mock.write_company_report") {
1848
2228
  return runMockWriteCompanyReport;
1849
2229
  }
@@ -1912,6 +2292,7 @@ async function runTask(ctx, task, agent) {
1912
2292
  dryRun: ctx.dryRun,
1913
2293
  codex: codexRunnerForTask(ctx, agent),
1914
2294
  investWiki: ctx.investWiki,
2295
+ financialReports: ctx.financialReports,
1915
2296
  appendEvent: async (event) => {
1916
2297
  await appendLocalEvent(ctx, task.id, TaskEventInputSchema.parse(event));
1917
2298
  },
@@ -1979,6 +2360,22 @@ async function runTask(ctx, task, agent) {
1979
2360
  });
1980
2361
  return;
1981
2362
  }
2363
+ if (error instanceof FinancialReportsRuntimeError) {
2364
+ await appendLocalEvent(ctx, task.id, {
2365
+ level: "error",
2366
+ message: error.message,
2367
+ data: { code: error.code, step: error.step }
2368
+ });
2369
+ await ctx.api.failTask(task.id, {
2370
+ error: {
2371
+ code: error.code,
2372
+ message: error.message,
2373
+ step: error.step,
2374
+ recoverable: false
2375
+ }
2376
+ });
2377
+ return;
2378
+ }
1982
2379
  if (error instanceof GitMirrorError) {
1983
2380
  await appendLocalEvent(ctx, task.id, {
1984
2381
  level: "error",
@@ -2100,6 +2497,9 @@ async function detectCapabilities() {
2100
2497
  git,
2101
2498
  node: true,
2102
2499
  python: python3 || python,
2500
+ financialReports: true,
2501
+ financialReportsNotebookLm: false,
2502
+ financialReportsSecHtmlRenderer: false,
2103
2503
  investWikiRuntime,
2104
2504
  codex,
2105
2505
  claude
@@ -2176,9 +2576,9 @@ async function subscribeToTaskAvailable(options, onEvent, onInvalidEvent) {
2176
2576
  }
2177
2577
 
2178
2578
  // src/service.ts
2179
- import { mkdir as mkdir5, unlink, writeFile as writeFile4 } from "fs/promises";
2579
+ import { mkdir as mkdir6, unlink, writeFile as writeFile5 } from "fs/promises";
2180
2580
  import os2 from "os";
2181
- import path8 from "path";
2581
+ import path9 from "path";
2182
2582
  var LAUNCH_AGENT_LABEL = "com.xdsjs.dossierx-daemon";
2183
2583
  function xmlEscape(value) {
2184
2584
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
@@ -2187,10 +2587,10 @@ function shellQuote(value) {
2187
2587
  return `'${value.replaceAll("'", "'\\''")}'`;
2188
2588
  }
2189
2589
  function launchAgentsDir() {
2190
- return path8.join(os2.homedir(), "Library", "LaunchAgents");
2590
+ return path9.join(os2.homedir(), "Library", "LaunchAgents");
2191
2591
  }
2192
2592
  function launchAgentPlistPath(label = LAUNCH_AGENT_LABEL, dir = launchAgentsDir()) {
2193
- return path8.join(dir, `${label}.plist`);
2593
+ return path9.join(dir, `${label}.plist`);
2194
2594
  }
2195
2595
  function resolveDaemonProgramArguments(input) {
2196
2596
  if (input.daemonCommand?.trim()) {
@@ -2201,14 +2601,14 @@ function resolveDaemonProgramArguments(input) {
2201
2601
  if (!entry) {
2202
2602
  throw new Error("Unable to resolve daemon entrypoint for LaunchAgent");
2203
2603
  }
2204
- if (path8.extname(entry) === ".ts") {
2604
+ if (path9.extname(entry) === ".ts") {
2205
2605
  throw new Error(
2206
2606
  "LaunchAgent cannot run a TypeScript daemon entrypoint directly; pass --daemon-command when installing from source"
2207
2607
  );
2208
2608
  }
2209
2609
  return [
2210
2610
  input.execPath ?? process.execPath,
2211
- path8.resolve(entry),
2611
+ path9.resolve(entry),
2212
2612
  "--log-level",
2213
2613
  input.logLevel ?? "info"
2214
2614
  ];
@@ -2267,7 +2667,7 @@ async function installLaunchAgent(options = {}) {
2267
2667
  throw new Error("DossierX LaunchAgent service is only supported on macOS");
2268
2668
  }
2269
2669
  const label = options.label ?? LAUNCH_AGENT_LABEL;
2270
- const configDir = path8.resolve(
2670
+ const configDir = path9.resolve(
2271
2671
  expandHomePath(options.configDir ?? getDaemonConfigDir())
2272
2672
  );
2273
2673
  const config = await readDaemonLocalConfig(configDir);
@@ -2287,8 +2687,8 @@ async function installLaunchAgent(options = {}) {
2287
2687
  );
2288
2688
  }
2289
2689
  const plistPath = launchAgentPlistPath(label, options.launchAgentsDir);
2290
- const stdoutPath = path8.join(configDir, "daemon.out.log");
2291
- const stderrPath = path8.join(configDir, "daemon.err.log");
2690
+ const stdoutPath = path9.join(configDir, "daemon.out.log");
2691
+ const stderrPath = path9.join(configDir, "daemon.err.log");
2292
2692
  const uid = typeof process.getuid === "function" ? process.getuid() : "<uid>";
2293
2693
  const programArguments = resolveDaemonProgramArguments({
2294
2694
  daemonCommand: options.daemonCommand,
@@ -2296,9 +2696,9 @@ async function installLaunchAgent(options = {}) {
2296
2696
  argv: options.argv,
2297
2697
  execPath: options.execPath
2298
2698
  });
2299
- await mkdir5(path8.dirname(plistPath), { recursive: true });
2300
- await mkdir5(configDir, { recursive: true, mode: 448 });
2301
- await writeFile4(
2699
+ await mkdir6(path9.dirname(plistPath), { recursive: true });
2700
+ await mkdir6(configDir, { recursive: true, mode: 448 });
2701
+ await writeFile5(
2302
2702
  plistPath,
2303
2703
  buildLaunchAgentPlist({
2304
2704
  label,
@@ -2339,7 +2739,7 @@ async function uninstallLaunchAgent(options = {}) {
2339
2739
 
2340
2740
  // src/cli.ts
2341
2741
  async function ensureWorkspaceDirectory(workspace) {
2342
- await mkdir6(workspace, { recursive: true }).catch(async (error) => {
2742
+ await mkdir7(workspace, { recursive: true }).catch(async (error) => {
2343
2743
  const stats2 = await stat4(workspace).catch(() => null);
2344
2744
  if (!stats2?.isDirectory()) {
2345
2745
  throw new Error("Workspace path is not a directory");
@@ -2437,7 +2837,7 @@ async function runDaemon(options) {
2437
2837
  "Missing daemon connection config. Run the generated daemon command from DossierX first."
2438
2838
  );
2439
2839
  }
2440
- const workspaceRoot = path9.resolve(expandHomePath(workspacePath3));
2840
+ const workspaceRoot = path10.resolve(expandHomePath(workspacePath3));
2441
2841
  await ensureWorkspaceDirectory(workspaceRoot);
2442
2842
  const capabilities = await detectCapabilities();
2443
2843
  const investWiki = createInvestWikiRunnerFromOptions(runtimeOptions);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdsjs/dossierx-daemon",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -28,9 +28,10 @@
28
28
  "execa": "^9.0.0",
29
29
  "pino": "^10.0.0",
30
30
  "zod": "^4.0.0",
31
- "@xdsjs/dossierx-git-mirror": "^0.1.0",
32
- "@xdsjs/dossierx-workspace": "^0.1.0",
33
- "@xdsjs/dossierx-shared": "^0.1.2"
31
+ "@xdsjs/dossier-financial-reports": "^0.1.0",
32
+ "@xdsjs/dossierx-git-mirror": "^0.1.2",
33
+ "@xdsjs/dossierx-shared": "^0.1.3",
34
+ "@xdsjs/dossierx-workspace": "^0.1.1"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/node": "^24.0.0",