@xdsjs/dossierx-daemon 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +442 -47
- package/package.json +27 -18
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
|
|
4
|
+
import { mkdir as mkdir7, stat as stat4 } from "fs/promises";
|
|
5
5
|
import os3 from "os";
|
|
6
|
-
import
|
|
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(
|
|
34
|
-
const response = await this.fetchImpl(`${this.serverUrl}${
|
|
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.
|
|
823
|
+
var DAEMON_VERSION = "0.1.5";
|
|
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,359 @@ 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/
|
|
1469
|
-
import {
|
|
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
|
+
renderSecHtmlToPdf,
|
|
1510
|
+
runNotebookLmFinancialReportsIngest
|
|
1511
|
+
} from "@xdsjs/dossier-financial-reports";
|
|
1471
1512
|
import {
|
|
1472
1513
|
buildCompanyManifest as buildCompanyManifest2,
|
|
1473
1514
|
companyManifestPath as companyManifestPath2,
|
|
1515
|
+
financialReportsIndexPath,
|
|
1516
|
+
resolveInsideWorkspace as resolveInsideWorkspace4
|
|
1517
|
+
} from "@xdsjs/dossierx-workspace";
|
|
1518
|
+
var FinancialReportsRuntimeError = class extends Error {
|
|
1519
|
+
code;
|
|
1520
|
+
step;
|
|
1521
|
+
constructor(code, message, step) {
|
|
1522
|
+
super(message);
|
|
1523
|
+
this.name = "FinancialReportsRuntimeError";
|
|
1524
|
+
this.code = code;
|
|
1525
|
+
this.step = step;
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
function defaultFetch() {
|
|
1529
|
+
return async (url, init) => fetch(url, init);
|
|
1530
|
+
}
|
|
1531
|
+
function defaultRenderHtmlToPdf() {
|
|
1532
|
+
return renderSecHtmlToPdf;
|
|
1533
|
+
}
|
|
1534
|
+
function requireNotebookLmClient(context) {
|
|
1535
|
+
const client = context.financialReports?.notebookLmClient;
|
|
1536
|
+
if (!client) {
|
|
1537
|
+
throw new FinancialReportsRuntimeError(
|
|
1538
|
+
"RUNTIME_NOT_FOUND",
|
|
1539
|
+
"NotebookLM client is not configured",
|
|
1540
|
+
"financial_reports.notebooklm.resolve"
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
return client;
|
|
1544
|
+
}
|
|
1545
|
+
function marketToAppMarket(market) {
|
|
1546
|
+
if (market === "US") {
|
|
1547
|
+
return "us";
|
|
1548
|
+
}
|
|
1549
|
+
if (market === "H") {
|
|
1550
|
+
return "hk";
|
|
1551
|
+
}
|
|
1552
|
+
return "cn";
|
|
1553
|
+
}
|
|
1554
|
+
function sha256Json(value) {
|
|
1555
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
|
|
1556
|
+
}
|
|
1557
|
+
function writeWorkspaceJson(context, relativePath, value) {
|
|
1558
|
+
return writeWorkspaceBytes(
|
|
1559
|
+
context,
|
|
1560
|
+
relativePath,
|
|
1561
|
+
new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
|
|
1562
|
+
`)
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
async function writeWorkspaceBytes(context, relativePath, bytes) {
|
|
1566
|
+
const absolutePath = resolveInsideWorkspace4(context.workspaceRoot, relativePath);
|
|
1567
|
+
await mkdir3(path6.dirname(absolutePath), { recursive: true });
|
|
1568
|
+
if (!context.dryRun) {
|
|
1569
|
+
await writeFile2(absolutePath, bytes);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
function createWorkspaceStorage(context) {
|
|
1573
|
+
return {
|
|
1574
|
+
async writeFile(relativePath, bytes) {
|
|
1575
|
+
await writeWorkspaceBytes(context, relativePath, bytes);
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
function readWorkspaceJson(context, relativePath) {
|
|
1580
|
+
const absolutePath = resolveInsideWorkspace4(context.workspaceRoot, relativePath);
|
|
1581
|
+
return readFile4(absolutePath, "utf8").then((content) => JSON.parse(content));
|
|
1582
|
+
}
|
|
1583
|
+
function parseReportManifest(payload) {
|
|
1584
|
+
const maybeRecord = typeof payload === "object" && payload !== null ? payload : {};
|
|
1585
|
+
if (maybeRecord.schemaVersion === "financial-reports/manifest/v1") {
|
|
1586
|
+
return FinancialReportManifestSchema.parse(payload);
|
|
1587
|
+
}
|
|
1588
|
+
return FinancialReportManifestSchema.parse(maybeRecord.manifest);
|
|
1589
|
+
}
|
|
1590
|
+
async function readReportManifest(context, manifestPath) {
|
|
1591
|
+
try {
|
|
1592
|
+
return parseReportManifest(await readWorkspaceJson(context, manifestPath));
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
if (error instanceof SyntaxError) {
|
|
1595
|
+
throw new FinancialReportsRuntimeError(
|
|
1596
|
+
"VALIDATION_ERROR",
|
|
1597
|
+
"Financial reports manifest is not valid JSON",
|
|
1598
|
+
"financial_reports.manifest.read"
|
|
1599
|
+
);
|
|
1600
|
+
}
|
|
1601
|
+
throw error;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
function assertManifestIssuerMatchesTaskIssuer(manifestIssuer, taskIssuer) {
|
|
1605
|
+
const mismatches = [];
|
|
1606
|
+
if (manifestIssuer.issuerId !== taskIssuer.issuerId) {
|
|
1607
|
+
mismatches.push("issuerId");
|
|
1608
|
+
}
|
|
1609
|
+
if (manifestIssuer.companyId !== taskIssuer.companyId) {
|
|
1610
|
+
mismatches.push("companyId");
|
|
1611
|
+
}
|
|
1612
|
+
if (primaryTicker(manifestIssuer) !== primaryTicker(taskIssuer)) {
|
|
1613
|
+
mismatches.push("primary ticker");
|
|
1614
|
+
}
|
|
1615
|
+
if (primaryMarket(manifestIssuer) !== primaryMarket(taskIssuer)) {
|
|
1616
|
+
mismatches.push("primary market");
|
|
1617
|
+
}
|
|
1618
|
+
if (mismatches.length > 0) {
|
|
1619
|
+
throw new FinancialReportsRuntimeError(
|
|
1620
|
+
"VALIDATION_ERROR",
|
|
1621
|
+
`Financial reports manifest issuer does not match task payload issuer: ${mismatches.join(", ")}`,
|
|
1622
|
+
"financial_reports.manifest.validate"
|
|
1623
|
+
);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
function financialTaskResult(input) {
|
|
1627
|
+
const ticker = primaryTicker(input.manifest.issuer);
|
|
1628
|
+
const coverage = assessReportCoverage(input.manifest);
|
|
1629
|
+
return {
|
|
1630
|
+
schemaVersion: "financial-reports/task-result/v1",
|
|
1631
|
+
issuerId: input.manifest.issuer.issuerId,
|
|
1632
|
+
companyId: input.manifest.issuer.companyId,
|
|
1633
|
+
ticker,
|
|
1634
|
+
manifestPath: input.manifestPath,
|
|
1635
|
+
manifestHash: input.manifestHash,
|
|
1636
|
+
notebookPath: input.notebookPath,
|
|
1637
|
+
factsBundlePath: input.factsBundlePath,
|
|
1638
|
+
reportCount: input.manifest.reports.length,
|
|
1639
|
+
coverageStatus: coverage.status,
|
|
1640
|
+
blockingReasons: coverage.knownGaps
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
async function runSyncReports(task, context) {
|
|
1644
|
+
const issuer = task.payload.issuer;
|
|
1645
|
+
const market = primaryMarket(issuer);
|
|
1646
|
+
const fetch2 = context.financialReports?.fetch ?? defaultFetch();
|
|
1647
|
+
const storage = createWorkspaceStorage(context);
|
|
1648
|
+
const now = context.financialReports?.now;
|
|
1649
|
+
let result;
|
|
1650
|
+
await context.appendEvent({
|
|
1651
|
+
level: "info",
|
|
1652
|
+
message: "Starting financial reports sync",
|
|
1653
|
+
data: { issuerId: issuer.issuerId, market }
|
|
1654
|
+
});
|
|
1655
|
+
if (market === "US") {
|
|
1656
|
+
result = await downloadSecReports({
|
|
1657
|
+
issuer,
|
|
1658
|
+
fiscalYears: task.payload.fiscalYears,
|
|
1659
|
+
reportTypes: task.payload.reportTypes,
|
|
1660
|
+
fetch: fetch2,
|
|
1661
|
+
storage,
|
|
1662
|
+
renderHtmlToPdf: context.financialReports?.renderHtmlToPdf ?? defaultRenderHtmlToPdf(),
|
|
1663
|
+
now
|
|
1664
|
+
});
|
|
1665
|
+
} else {
|
|
1666
|
+
result = await downloadCninfoReports({
|
|
1667
|
+
issuer,
|
|
1668
|
+
fiscalYears: task.payload.fiscalYears,
|
|
1669
|
+
reportTypes: task.payload.reportTypes,
|
|
1670
|
+
fetch: fetch2,
|
|
1671
|
+
storage,
|
|
1672
|
+
now
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
const ticker = primaryTicker(result.manifest.issuer);
|
|
1676
|
+
const reportIndexPath = financialReportsIndexPath(ticker);
|
|
1677
|
+
const manifestHash = sha256Json(result.manifest);
|
|
1678
|
+
const reportIndex = {
|
|
1679
|
+
schemaVersion: "financial-reports/report-index/v1",
|
|
1680
|
+
generatedAt: result.manifest.generatedAt,
|
|
1681
|
+
issuer: result.manifest.issuer,
|
|
1682
|
+
manifestHash,
|
|
1683
|
+
manifest: result.manifest,
|
|
1684
|
+
coverage: result.coverage,
|
|
1685
|
+
skippedReports: result.skippedReports,
|
|
1686
|
+
missingReports: result.missingReports
|
|
1687
|
+
};
|
|
1688
|
+
await writeWorkspaceJson(context, reportIndexPath, reportIndex);
|
|
1689
|
+
const companyManifest = context.dryRun ? void 0 : await buildCompanyManifest2({
|
|
1690
|
+
workspaceRoot: context.workspaceRoot,
|
|
1691
|
+
ticker,
|
|
1692
|
+
market: marketToAppMarket(market)
|
|
1693
|
+
});
|
|
1694
|
+
await context.appendEvent({
|
|
1695
|
+
level: "info",
|
|
1696
|
+
message: "Financial reports sync completed",
|
|
1697
|
+
data: {
|
|
1698
|
+
issuerId: issuer.issuerId,
|
|
1699
|
+
coverageStatus: result.coverage.status,
|
|
1700
|
+
reportCount: result.manifest.reports.length,
|
|
1701
|
+
manifestPath: reportIndexPath
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
return {
|
|
1705
|
+
generatedFiles: [
|
|
1706
|
+
...result.manifest.reports.map((report) => report.localPath),
|
|
1707
|
+
reportIndexPath,
|
|
1708
|
+
companyManifestPath2(ticker)
|
|
1709
|
+
],
|
|
1710
|
+
manifestPath: companyManifestPath2(ticker),
|
|
1711
|
+
manifest: companyManifest,
|
|
1712
|
+
blockingReasons: [],
|
|
1713
|
+
financialReports: financialTaskResult({
|
|
1714
|
+
manifest: result.manifest,
|
|
1715
|
+
manifestPath: reportIndexPath,
|
|
1716
|
+
manifestHash
|
|
1717
|
+
})
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
async function runNotebookIngest(task, context) {
|
|
1721
|
+
const client = requireNotebookLmClient(context);
|
|
1722
|
+
const manifest = await readReportManifest(context, task.payload.manifestPath);
|
|
1723
|
+
assertManifestIssuerMatchesTaskIssuer(manifest.issuer, task.payload.issuer);
|
|
1724
|
+
const ticker = primaryTicker(manifest.issuer);
|
|
1725
|
+
const manifestHash = sha256Json(manifest);
|
|
1726
|
+
const factsAsOf = task.payload.factsAsOf ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1727
|
+
await context.appendEvent({
|
|
1728
|
+
level: "info",
|
|
1729
|
+
message: "Starting NotebookLM financial reports ingest",
|
|
1730
|
+
data: { issuerId: manifest.issuer.issuerId, manifestPath: task.payload.manifestPath }
|
|
1731
|
+
});
|
|
1732
|
+
const ingest = await runNotebookLmFinancialReportsIngest({
|
|
1733
|
+
manifest,
|
|
1734
|
+
client,
|
|
1735
|
+
manifestHash,
|
|
1736
|
+
factsAsOf,
|
|
1737
|
+
generatedAt: context.financialReports?.now?.(),
|
|
1738
|
+
notebookTitle: task.payload.notebookTitle
|
|
1739
|
+
});
|
|
1740
|
+
const notebookPath = `companies/${ticker}/financial-reports/notebooklm/notebook.json`;
|
|
1741
|
+
const factsBundlePath = `companies/${ticker}/financial-reports/ingest/facts-bundle.json`;
|
|
1742
|
+
await writeWorkspaceJson(context, notebookPath, {
|
|
1743
|
+
schemaVersion: "financial-reports/notebooklm-run/v1",
|
|
1744
|
+
generatedAt: ingest.factsBundle.generatedAt,
|
|
1745
|
+
manifestPath: task.payload.manifestPath,
|
|
1746
|
+
notebooklm: ingest.notebooklm,
|
|
1747
|
+
sourceIds: ingest.sourceIds
|
|
1748
|
+
});
|
|
1749
|
+
await writeWorkspaceJson(context, factsBundlePath, ingest.factsBundle);
|
|
1750
|
+
const companyManifest = context.dryRun ? void 0 : await buildCompanyManifest2({
|
|
1751
|
+
workspaceRoot: context.workspaceRoot,
|
|
1752
|
+
ticker,
|
|
1753
|
+
market: marketToAppMarket(primaryMarket(manifest.issuer))
|
|
1754
|
+
});
|
|
1755
|
+
await context.appendEvent({
|
|
1756
|
+
level: "info",
|
|
1757
|
+
message: "NotebookLM financial reports ingest completed",
|
|
1758
|
+
data: {
|
|
1759
|
+
issuerId: manifest.issuer.issuerId,
|
|
1760
|
+
notebookPath,
|
|
1761
|
+
factsBundlePath
|
|
1762
|
+
}
|
|
1763
|
+
});
|
|
1764
|
+
return {
|
|
1765
|
+
generatedFiles: [notebookPath, factsBundlePath, companyManifestPath2(ticker)],
|
|
1766
|
+
manifestPath: companyManifestPath2(ticker),
|
|
1767
|
+
manifest: companyManifest,
|
|
1768
|
+
blockingReasons: [],
|
|
1769
|
+
financialReports: financialTaskResult({
|
|
1770
|
+
manifest,
|
|
1771
|
+
manifestPath: task.payload.manifestPath,
|
|
1772
|
+
manifestHash,
|
|
1773
|
+
notebookPath,
|
|
1774
|
+
factsBundlePath
|
|
1775
|
+
})
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
async function runRefreshCompany(task, context) {
|
|
1779
|
+
const syncResult = await runSyncReports(
|
|
1780
|
+
{
|
|
1781
|
+
id: task.id,
|
|
1782
|
+
type: "financial_reports.sync_reports",
|
|
1783
|
+
payload: {
|
|
1784
|
+
issuer: task.payload.issuer,
|
|
1785
|
+
fiscalYears: task.payload.fiscalYears,
|
|
1786
|
+
reportTypes: task.payload.reportTypes
|
|
1787
|
+
}
|
|
1788
|
+
},
|
|
1789
|
+
context
|
|
1790
|
+
);
|
|
1791
|
+
if (!context.financialReports?.notebookLmClient) {
|
|
1792
|
+
return syncResult;
|
|
1793
|
+
}
|
|
1794
|
+
const manifestPath = syncResult.financialReports?.manifestPath;
|
|
1795
|
+
if (!manifestPath) {
|
|
1796
|
+
return syncResult;
|
|
1797
|
+
}
|
|
1798
|
+
const ingestResult = await runNotebookIngest(
|
|
1799
|
+
{
|
|
1800
|
+
id: task.id,
|
|
1801
|
+
type: "financial_reports.ingest_notebook",
|
|
1802
|
+
payload: {
|
|
1803
|
+
issuer: task.payload.issuer,
|
|
1804
|
+
manifestPath,
|
|
1805
|
+
factsAsOf: task.payload.factsAsOf,
|
|
1806
|
+
notebookTitle: task.payload.notebookTitle
|
|
1807
|
+
}
|
|
1808
|
+
},
|
|
1809
|
+
context
|
|
1810
|
+
);
|
|
1811
|
+
return {
|
|
1812
|
+
...ingestResult,
|
|
1813
|
+
generatedFiles: [
|
|
1814
|
+
...syncResult.generatedFiles,
|
|
1815
|
+
...ingestResult.generatedFiles
|
|
1816
|
+
]
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
async function runFinancialReportsTask(task, context) {
|
|
1820
|
+
if (task.type === "financial_reports.sync_reports") {
|
|
1821
|
+
return runSyncReports(task, context);
|
|
1822
|
+
}
|
|
1823
|
+
if (task.type === "financial_reports.ingest_notebook" || task.type === "financial_reports.extract_facts") {
|
|
1824
|
+
return runNotebookIngest(task, context);
|
|
1825
|
+
}
|
|
1826
|
+
if (task.type === "financial_reports.refresh_company") {
|
|
1827
|
+
return runRefreshCompany(task, context);
|
|
1828
|
+
}
|
|
1829
|
+
throw new FinancialReportsRuntimeError(
|
|
1830
|
+
"VALIDATION_ERROR",
|
|
1831
|
+
"Unsupported financial reports task type",
|
|
1832
|
+
"financial_reports.dispatch"
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
// src/executors/investWiki.ts
|
|
1837
|
+
import { access as access2, mkdir as mkdir4, readFile as readFile5, stat as stat3, writeFile as writeFile3 } from "fs/promises";
|
|
1838
|
+
import path7 from "path";
|
|
1839
|
+
import {
|
|
1840
|
+
buildCompanyManifest as buildCompanyManifest3,
|
|
1841
|
+
companyManifestPath as companyManifestPath3,
|
|
1474
1842
|
investWikiConfigPath,
|
|
1475
1843
|
investWikiRoot as investWikiRoot2,
|
|
1476
|
-
resolveInsideWorkspace as
|
|
1844
|
+
resolveInsideWorkspace as resolveInsideWorkspace5
|
|
1477
1845
|
} from "@xdsjs/dossierx-workspace";
|
|
1478
1846
|
async function exists(absolutePath) {
|
|
1479
1847
|
try {
|
|
@@ -1498,7 +1866,7 @@ function isWorkspaceGuardError2(error) {
|
|
|
1498
1866
|
}
|
|
1499
1867
|
function workspacePath2(workspaceRoot, relativePath, step) {
|
|
1500
1868
|
try {
|
|
1501
|
-
return
|
|
1869
|
+
return resolveInsideWorkspace5(workspaceRoot, relativePath);
|
|
1502
1870
|
} catch (error) {
|
|
1503
1871
|
if (isWorkspaceGuardError2(error)) {
|
|
1504
1872
|
throw new InvestWikiRuntimeError(
|
|
@@ -1579,11 +1947,11 @@ function isMarket(value) {
|
|
|
1579
1947
|
async function readManifestMarket(workspaceRoot, ticker) {
|
|
1580
1948
|
const manifestPath = workspacePath2(
|
|
1581
1949
|
workspaceRoot,
|
|
1582
|
-
|
|
1950
|
+
companyManifestPath3(ticker),
|
|
1583
1951
|
"invest_wiki.sync"
|
|
1584
1952
|
);
|
|
1585
1953
|
try {
|
|
1586
|
-
const manifest = JSON.parse(await
|
|
1954
|
+
const manifest = JSON.parse(await readFile5(manifestPath, "utf8"));
|
|
1587
1955
|
if (!isMarket(manifest.market)) {
|
|
1588
1956
|
throw new InvestWikiRuntimeError(
|
|
1589
1957
|
"VALIDATION_ERROR",
|
|
@@ -1626,7 +1994,7 @@ async function runInitCompanyVault(task, context) {
|
|
|
1626
1994
|
`${vaultRelativePath}/.llm-wiki-invest/dossier-state.json`,
|
|
1627
1995
|
"invest_wiki.init_company_vault"
|
|
1628
1996
|
);
|
|
1629
|
-
await
|
|
1997
|
+
await mkdir4(vaultRoot, { recursive: true });
|
|
1630
1998
|
await context.appendEvent({
|
|
1631
1999
|
level: "info",
|
|
1632
2000
|
message: "Initializing invest-wiki vault",
|
|
@@ -1645,10 +2013,10 @@ async function runInitCompanyVault(task, context) {
|
|
|
1645
2013
|
const statusOutput = await runner.run(["dossier", "status"], { cwd: vaultRoot });
|
|
1646
2014
|
await appendOutputEvent2(context, "Invest wiki dossier status completed", statusOutput);
|
|
1647
2015
|
if (!await exists(markerPath)) {
|
|
1648
|
-
await
|
|
1649
|
-
await
|
|
2016
|
+
await mkdir4(path7.dirname(markerPath), { recursive: true });
|
|
2017
|
+
await writeFile3(markerPath, "# Created by dossierx-daemon\n");
|
|
1650
2018
|
}
|
|
1651
|
-
const manifest = await
|
|
2019
|
+
const manifest = await buildCompanyManifest3({
|
|
1652
2020
|
workspaceRoot: context.workspaceRoot,
|
|
1653
2021
|
ticker,
|
|
1654
2022
|
market: task.payload.market
|
|
@@ -1662,9 +2030,10 @@ async function runInitCompanyVault(task, context) {
|
|
|
1662
2030
|
generatedFiles: [
|
|
1663
2031
|
vaultRelativePath,
|
|
1664
2032
|
markerRelativePath,
|
|
1665
|
-
|
|
2033
|
+
companyManifestPath3(ticker)
|
|
1666
2034
|
],
|
|
1667
|
-
|
|
2035
|
+
blockingReasons: [],
|
|
2036
|
+
manifestPath: companyManifestPath3(ticker),
|
|
1668
2037
|
manifest
|
|
1669
2038
|
};
|
|
1670
2039
|
}
|
|
@@ -1688,7 +2057,8 @@ async function runStatus(task, context) {
|
|
|
1688
2057
|
);
|
|
1689
2058
|
return {
|
|
1690
2059
|
generatedFiles: [],
|
|
1691
|
-
|
|
2060
|
+
blockingReasons: [],
|
|
2061
|
+
manifestPath: companyManifestPath3(ticker)
|
|
1692
2062
|
};
|
|
1693
2063
|
}
|
|
1694
2064
|
async function runSync(task, context) {
|
|
@@ -1703,14 +2073,15 @@ async function runSync(task, context) {
|
|
|
1703
2073
|
const args = task.payload.dryRun ? ["sync", "--dry-run"] : ["sync"];
|
|
1704
2074
|
const output = await runner.run(args, { cwd: vaultRoot });
|
|
1705
2075
|
await appendOutputEvent2(context, "Invest wiki sync completed", output);
|
|
1706
|
-
const manifest = await
|
|
2076
|
+
const manifest = await buildCompanyManifest3({
|
|
1707
2077
|
workspaceRoot: context.workspaceRoot,
|
|
1708
2078
|
ticker,
|
|
1709
2079
|
market
|
|
1710
2080
|
});
|
|
1711
2081
|
return {
|
|
1712
2082
|
generatedFiles: [],
|
|
1713
|
-
|
|
2083
|
+
blockingReasons: [],
|
|
2084
|
+
manifestPath: companyManifestPath3(ticker),
|
|
1714
2085
|
manifest
|
|
1715
2086
|
};
|
|
1716
2087
|
}
|
|
@@ -1732,13 +2103,13 @@ async function runInvestWikiTask(task, context) {
|
|
|
1732
2103
|
}
|
|
1733
2104
|
|
|
1734
2105
|
// src/executors/mockWriteCompanyReport.ts
|
|
1735
|
-
import { mkdir as
|
|
1736
|
-
import
|
|
2106
|
+
import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
|
|
2107
|
+
import path8 from "path";
|
|
1737
2108
|
import {
|
|
1738
|
-
buildCompanyManifest as
|
|
1739
|
-
companyManifestPath as
|
|
2109
|
+
buildCompanyManifest as buildCompanyManifest4,
|
|
2110
|
+
companyManifestPath as companyManifestPath4,
|
|
1740
2111
|
companyRoot,
|
|
1741
|
-
resolveInsideWorkspace as
|
|
2112
|
+
resolveInsideWorkspace as resolveInsideWorkspace6,
|
|
1742
2113
|
rightBusinessPath,
|
|
1743
2114
|
rightPeoplePath,
|
|
1744
2115
|
rightPricePath
|
|
@@ -1772,10 +2143,10 @@ var rightPrice = (ticker) => `# Right Price - ${ticker}
|
|
|
1772
2143
|
This is a placeholder right-price analysis for ${ticker}.
|
|
1773
2144
|
`;
|
|
1774
2145
|
async function writeWorkspaceFile(context, relativePath, content) {
|
|
1775
|
-
const absolutePath =
|
|
1776
|
-
await
|
|
2146
|
+
const absolutePath = resolveInsideWorkspace6(context.workspaceRoot, relativePath);
|
|
2147
|
+
await mkdir5(path8.dirname(absolutePath), { recursive: true });
|
|
1777
2148
|
if (!context.dryRun) {
|
|
1778
|
-
await
|
|
2149
|
+
await writeFile4(absolutePath, content);
|
|
1779
2150
|
}
|
|
1780
2151
|
}
|
|
1781
2152
|
async function runMockWriteCompanyReport(task, context) {
|
|
@@ -1790,7 +2161,7 @@ async function runMockWriteCompanyReport(task, context) {
|
|
|
1790
2161
|
message: "Creating company directory",
|
|
1791
2162
|
data: { ticker }
|
|
1792
2163
|
});
|
|
1793
|
-
await
|
|
2164
|
+
await mkdir5(resolveInsideWorkspace6(context.workspaceRoot, companyRoot(ticker)), {
|
|
1794
2165
|
recursive: true
|
|
1795
2166
|
});
|
|
1796
2167
|
await context.appendEvent({
|
|
@@ -1814,11 +2185,11 @@ async function runMockWriteCompanyReport(task, context) {
|
|
|
1814
2185
|
await context.appendEvent({
|
|
1815
2186
|
level: "info",
|
|
1816
2187
|
message: "Generating manifest.json",
|
|
1817
|
-
data: { path:
|
|
2188
|
+
data: { path: companyManifestPath4(ticker) }
|
|
1818
2189
|
});
|
|
1819
2190
|
let manifest;
|
|
1820
2191
|
if (!context.dryRun) {
|
|
1821
|
-
manifest = await
|
|
2192
|
+
manifest = await buildCompanyManifest4({
|
|
1822
2193
|
workspaceRoot: context.workspaceRoot,
|
|
1823
2194
|
ticker,
|
|
1824
2195
|
market: task.payload.market
|
|
@@ -1830,8 +2201,9 @@ async function runMockWriteCompanyReport(task, context) {
|
|
|
1830
2201
|
data: { ticker }
|
|
1831
2202
|
});
|
|
1832
2203
|
return {
|
|
1833
|
-
generatedFiles: [...generatedFiles,
|
|
1834
|
-
|
|
2204
|
+
generatedFiles: [...generatedFiles, companyManifestPath4(ticker)],
|
|
2205
|
+
blockingReasons: [],
|
|
2206
|
+
manifestPath: companyManifestPath4(ticker),
|
|
1835
2207
|
manifest
|
|
1836
2208
|
};
|
|
1837
2209
|
}
|
|
@@ -1844,6 +2216,9 @@ function getExecutor(task) {
|
|
|
1844
2216
|
if (task.type.startsWith("invest_wiki.")) {
|
|
1845
2217
|
return runInvestWikiTask;
|
|
1846
2218
|
}
|
|
2219
|
+
if (task.type.startsWith("financial_reports.")) {
|
|
2220
|
+
return runFinancialReportsTask;
|
|
2221
|
+
}
|
|
1847
2222
|
if (task.type === "mock.write_company_report") {
|
|
1848
2223
|
return runMockWriteCompanyReport;
|
|
1849
2224
|
}
|
|
@@ -1912,6 +2287,7 @@ async function runTask(ctx, task, agent) {
|
|
|
1912
2287
|
dryRun: ctx.dryRun,
|
|
1913
2288
|
codex: codexRunnerForTask(ctx, agent),
|
|
1914
2289
|
investWiki: ctx.investWiki,
|
|
2290
|
+
financialReports: ctx.financialReports,
|
|
1915
2291
|
appendEvent: async (event) => {
|
|
1916
2292
|
await appendLocalEvent(ctx, task.id, TaskEventInputSchema.parse(event));
|
|
1917
2293
|
},
|
|
@@ -1979,6 +2355,22 @@ async function runTask(ctx, task, agent) {
|
|
|
1979
2355
|
});
|
|
1980
2356
|
return;
|
|
1981
2357
|
}
|
|
2358
|
+
if (error instanceof FinancialReportsRuntimeError) {
|
|
2359
|
+
await appendLocalEvent(ctx, task.id, {
|
|
2360
|
+
level: "error",
|
|
2361
|
+
message: error.message,
|
|
2362
|
+
data: { code: error.code, step: error.step }
|
|
2363
|
+
});
|
|
2364
|
+
await ctx.api.failTask(task.id, {
|
|
2365
|
+
error: {
|
|
2366
|
+
code: error.code,
|
|
2367
|
+
message: error.message,
|
|
2368
|
+
step: error.step,
|
|
2369
|
+
recoverable: false
|
|
2370
|
+
}
|
|
2371
|
+
});
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
1982
2374
|
if (error instanceof GitMirrorError) {
|
|
1983
2375
|
await appendLocalEvent(ctx, task.id, {
|
|
1984
2376
|
level: "error",
|
|
@@ -2100,6 +2492,9 @@ async function detectCapabilities() {
|
|
|
2100
2492
|
git,
|
|
2101
2493
|
node: true,
|
|
2102
2494
|
python: python3 || python,
|
|
2495
|
+
financialReports: true,
|
|
2496
|
+
financialReportsNotebookLm: false,
|
|
2497
|
+
financialReportsSecHtmlRenderer: true,
|
|
2103
2498
|
investWikiRuntime,
|
|
2104
2499
|
codex,
|
|
2105
2500
|
claude
|
|
@@ -2176,9 +2571,9 @@ async function subscribeToTaskAvailable(options, onEvent, onInvalidEvent) {
|
|
|
2176
2571
|
}
|
|
2177
2572
|
|
|
2178
2573
|
// src/service.ts
|
|
2179
|
-
import { mkdir as
|
|
2574
|
+
import { mkdir as mkdir6, unlink, writeFile as writeFile5 } from "fs/promises";
|
|
2180
2575
|
import os2 from "os";
|
|
2181
|
-
import
|
|
2576
|
+
import path9 from "path";
|
|
2182
2577
|
var LAUNCH_AGENT_LABEL = "com.xdsjs.dossierx-daemon";
|
|
2183
2578
|
function xmlEscape(value) {
|
|
2184
2579
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -2187,10 +2582,10 @@ function shellQuote(value) {
|
|
|
2187
2582
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
2188
2583
|
}
|
|
2189
2584
|
function launchAgentsDir() {
|
|
2190
|
-
return
|
|
2585
|
+
return path9.join(os2.homedir(), "Library", "LaunchAgents");
|
|
2191
2586
|
}
|
|
2192
2587
|
function launchAgentPlistPath(label = LAUNCH_AGENT_LABEL, dir = launchAgentsDir()) {
|
|
2193
|
-
return
|
|
2588
|
+
return path9.join(dir, `${label}.plist`);
|
|
2194
2589
|
}
|
|
2195
2590
|
function resolveDaemonProgramArguments(input) {
|
|
2196
2591
|
if (input.daemonCommand?.trim()) {
|
|
@@ -2201,14 +2596,14 @@ function resolveDaemonProgramArguments(input) {
|
|
|
2201
2596
|
if (!entry) {
|
|
2202
2597
|
throw new Error("Unable to resolve daemon entrypoint for LaunchAgent");
|
|
2203
2598
|
}
|
|
2204
|
-
if (
|
|
2599
|
+
if (path9.extname(entry) === ".ts") {
|
|
2205
2600
|
throw new Error(
|
|
2206
2601
|
"LaunchAgent cannot run a TypeScript daemon entrypoint directly; pass --daemon-command when installing from source"
|
|
2207
2602
|
);
|
|
2208
2603
|
}
|
|
2209
2604
|
return [
|
|
2210
2605
|
input.execPath ?? process.execPath,
|
|
2211
|
-
|
|
2606
|
+
path9.resolve(entry),
|
|
2212
2607
|
"--log-level",
|
|
2213
2608
|
input.logLevel ?? "info"
|
|
2214
2609
|
];
|
|
@@ -2267,7 +2662,7 @@ async function installLaunchAgent(options = {}) {
|
|
|
2267
2662
|
throw new Error("DossierX LaunchAgent service is only supported on macOS");
|
|
2268
2663
|
}
|
|
2269
2664
|
const label = options.label ?? LAUNCH_AGENT_LABEL;
|
|
2270
|
-
const configDir =
|
|
2665
|
+
const configDir = path9.resolve(
|
|
2271
2666
|
expandHomePath(options.configDir ?? getDaemonConfigDir())
|
|
2272
2667
|
);
|
|
2273
2668
|
const config = await readDaemonLocalConfig(configDir);
|
|
@@ -2287,8 +2682,8 @@ async function installLaunchAgent(options = {}) {
|
|
|
2287
2682
|
);
|
|
2288
2683
|
}
|
|
2289
2684
|
const plistPath = launchAgentPlistPath(label, options.launchAgentsDir);
|
|
2290
|
-
const stdoutPath =
|
|
2291
|
-
const stderrPath =
|
|
2685
|
+
const stdoutPath = path9.join(configDir, "daemon.out.log");
|
|
2686
|
+
const stderrPath = path9.join(configDir, "daemon.err.log");
|
|
2292
2687
|
const uid = typeof process.getuid === "function" ? process.getuid() : "<uid>";
|
|
2293
2688
|
const programArguments = resolveDaemonProgramArguments({
|
|
2294
2689
|
daemonCommand: options.daemonCommand,
|
|
@@ -2296,9 +2691,9 @@ async function installLaunchAgent(options = {}) {
|
|
|
2296
2691
|
argv: options.argv,
|
|
2297
2692
|
execPath: options.execPath
|
|
2298
2693
|
});
|
|
2299
|
-
await
|
|
2300
|
-
await
|
|
2301
|
-
await
|
|
2694
|
+
await mkdir6(path9.dirname(plistPath), { recursive: true });
|
|
2695
|
+
await mkdir6(configDir, { recursive: true, mode: 448 });
|
|
2696
|
+
await writeFile5(
|
|
2302
2697
|
plistPath,
|
|
2303
2698
|
buildLaunchAgentPlist({
|
|
2304
2699
|
label,
|
|
@@ -2339,7 +2734,7 @@ async function uninstallLaunchAgent(options = {}) {
|
|
|
2339
2734
|
|
|
2340
2735
|
// src/cli.ts
|
|
2341
2736
|
async function ensureWorkspaceDirectory(workspace) {
|
|
2342
|
-
await
|
|
2737
|
+
await mkdir7(workspace, { recursive: true }).catch(async (error) => {
|
|
2343
2738
|
const stats2 = await stat4(workspace).catch(() => null);
|
|
2344
2739
|
if (!stats2?.isDirectory()) {
|
|
2345
2740
|
throw new Error("Workspace path is not a directory");
|
|
@@ -2437,7 +2832,7 @@ async function runDaemon(options) {
|
|
|
2437
2832
|
"Missing daemon connection config. Run the generated daemon command from DossierX first."
|
|
2438
2833
|
);
|
|
2439
2834
|
}
|
|
2440
|
-
const workspaceRoot =
|
|
2835
|
+
const workspaceRoot = path10.resolve(expandHomePath(workspacePath3));
|
|
2441
2836
|
await ensureWorkspaceDirectory(workspaceRoot);
|
|
2442
2837
|
const capabilities = await detectCapabilities();
|
|
2443
2838
|
const investWiki = createInvestWikiRunnerFromOptions(runtimeOptions);
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xdsjs/dossierx-daemon",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "./
|
|
6
|
-
"types": "./
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
-
"types": "./
|
|
10
|
-
"import": "./
|
|
9
|
+
"types": "./src/index.ts",
|
|
10
|
+
"import": "./src/index.ts"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"bin": {
|
|
@@ -17,20 +17,36 @@
|
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
19
|
"publishConfig": {
|
|
20
|
-
"access": "public"
|
|
20
|
+
"access": "public",
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
21
29
|
},
|
|
22
30
|
"engines": {
|
|
23
31
|
"node": ">=20"
|
|
24
32
|
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "tsx src/index.ts",
|
|
35
|
+
"build": "tsup src/index.ts --format esm --dts --out-dir dist",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"lint": "tsc --noEmit"
|
|
39
|
+
},
|
|
25
40
|
"dependencies": {
|
|
26
41
|
"@supabase/supabase-js": "^2.0.0",
|
|
42
|
+
"@xdsjs/dossier-financial-reports": "workspace:^",
|
|
43
|
+
"@xdsjs/dossierx-git-mirror": "workspace:^",
|
|
44
|
+
"@xdsjs/dossierx-shared": "workspace:^",
|
|
45
|
+
"@xdsjs/dossierx-workspace": "workspace:^",
|
|
27
46
|
"commander": "^14.0.0",
|
|
28
47
|
"execa": "^9.0.0",
|
|
29
48
|
"pino": "^10.0.0",
|
|
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"
|
|
49
|
+
"zod": "^4.0.0"
|
|
34
50
|
},
|
|
35
51
|
"devDependencies": {
|
|
36
52
|
"@types/node": "^24.0.0",
|
|
@@ -38,12 +54,5 @@
|
|
|
38
54
|
"tsup": "^8.0.0",
|
|
39
55
|
"typescript": "^5.0.0",
|
|
40
56
|
"vitest": "^3.0.0"
|
|
41
|
-
},
|
|
42
|
-
"scripts": {
|
|
43
|
-
"dev": "tsx src/index.ts",
|
|
44
|
-
"build": "tsup src/index.ts --format esm --dts --out-dir dist",
|
|
45
|
-
"typecheck": "tsc --noEmit",
|
|
46
|
-
"test": "vitest run",
|
|
47
|
-
"lint": "tsc --noEmit"
|
|
48
57
|
}
|
|
49
|
-
}
|
|
58
|
+
}
|