@xdsjs/dossierx-daemon 0.1.2 → 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 +611 -122
  2. package/package.json +5 -3
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 path8 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(path9, body, schema) {
34
- const response = await this.fetchImpl(`${this.serverUrl}${path9}`, {
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}`,
@@ -495,9 +495,103 @@ function createCodexRunnerFromOptions(options, env = process.env) {
495
495
  });
496
496
  }
497
497
 
498
+ // src/git-mirror.ts
499
+ import path2 from "path";
500
+ import {
501
+ publishCompanySnapshot
502
+ } from "@xdsjs/dossierx-git-mirror";
503
+
504
+ // src/local-config.ts
505
+ import { chmod, mkdir, readFile, writeFile } from "fs/promises";
506
+ import os from "os";
507
+ import path from "path";
508
+ import { z } from "zod";
509
+ import { DOSSIERX_CONFIG_DIR } from "@xdsjs/dossierx-workspace";
510
+ var DaemonLocalConfigSchema = z.object({
511
+ machineId: z.string().uuid().optional(),
512
+ machineKey: z.string().startsWith("dx_machine_"),
513
+ serverUrl: z.string().min(1),
514
+ supabaseUrl: z.string().min(1),
515
+ supabaseAnonKey: z.string().min(1),
516
+ workspacePath: z.string().min(1),
517
+ investWikiMode: z.string().optional(),
518
+ investWikiLocalRepo: z.string().optional(),
519
+ investWikiCommand: z.string().optional(),
520
+ codexCommand: z.string().optional(),
521
+ codexModel: z.string().optional(),
522
+ codexSandbox: z.string().optional(),
523
+ gitMirrorRoot: z.string().optional(),
524
+ gitMirrorRemote: z.string().optional(),
525
+ gitMirrorBranch: z.string().optional(),
526
+ gitCommand: z.string().optional(),
527
+ localApiPort: z.number().int().positive().optional()
528
+ });
529
+ function expandHomePath(input) {
530
+ if (input === "~") {
531
+ return os.homedir();
532
+ }
533
+ if (input.startsWith("~/")) {
534
+ return path.join(os.homedir(), input.slice(2));
535
+ }
536
+ return input;
537
+ }
538
+ function getDaemonConfigDir(env = process.env) {
539
+ return path.resolve(
540
+ expandHomePath(env.DOSSIERX_CONFIG_DIR ?? DOSSIERX_CONFIG_DIR)
541
+ );
542
+ }
543
+ function daemonConfigPath(configDir = getDaemonConfigDir()) {
544
+ return path.join(configDir, "config.json");
545
+ }
546
+ async function readDaemonLocalConfig(configDir = getDaemonConfigDir()) {
547
+ try {
548
+ return DaemonLocalConfigSchema.parse(
549
+ JSON.parse(await readFile(daemonConfigPath(configDir), "utf8"))
550
+ );
551
+ } catch (error) {
552
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
553
+ return null;
554
+ }
555
+ throw error;
556
+ }
557
+ }
558
+ async function writeDaemonLocalConfig(config, configDir = getDaemonConfigDir()) {
559
+ const parsed = DaemonLocalConfigSchema.parse(config);
560
+ await mkdir(configDir, { recursive: true, mode: 448 });
561
+ const target = daemonConfigPath(configDir);
562
+ await writeFile(target, `${JSON.stringify(parsed, null, 2)}
563
+ `, {
564
+ mode: 384
565
+ });
566
+ await chmod(target, 384);
567
+ }
568
+
569
+ // src/git-mirror.ts
570
+ function createGitMirrorFromOptions(options) {
571
+ if (!options.gitMirrorRoot && !options.gitMirrorRemote) {
572
+ return null;
573
+ }
574
+ const mirrorRoot = path2.resolve(
575
+ expandHomePath(
576
+ options.gitMirrorRoot ?? path2.join(getDaemonConfigDir(), "git-mirror")
577
+ )
578
+ );
579
+ return {
580
+ publishCompanySnapshot(input) {
581
+ return publishCompanySnapshot({
582
+ ...input,
583
+ mirrorRoot,
584
+ remoteUrl: options.gitMirrorRemote,
585
+ branch: options.gitMirrorBranch,
586
+ gitCommand: options.gitCommand
587
+ });
588
+ }
589
+ };
590
+ }
591
+
498
592
  // src/invest-wiki/runner.ts
499
593
  import { access } from "fs/promises";
500
- import path from "path";
594
+ import path3 from "path";
501
595
  import { execa as execa2 } from "execa";
502
596
 
503
597
  // src/invest-wiki/config.ts
@@ -591,7 +685,7 @@ function createLocalRepoRunner(config) {
591
685
  return {
592
686
  kind: "local-repo",
593
687
  async run(args, options) {
594
- const cliPath = path.join(config.localRepo, "dist", "cli.js");
688
+ const cliPath = path3.join(config.localRepo, "dist", "cli.js");
595
689
  await assertFileExists(cliPath);
596
690
  const result = await runInvestWikiCommand(
597
691
  process.execPath,
@@ -638,73 +732,13 @@ function createInvestWikiRunnerFromOptions(options, env = process.env) {
638
732
  return createInvestWikiRunner(resolveInvestWikiConfig(options, env));
639
733
  }
640
734
 
641
- // src/local-config.ts
642
- import { chmod, mkdir, readFile, writeFile } from "fs/promises";
643
- import os from "os";
644
- import path2 from "path";
645
- import { z } from "zod";
646
- import { DOSSIERX_CONFIG_DIR } from "@xdsjs/dossierx-workspace";
647
- var DaemonLocalConfigSchema = z.object({
648
- machineId: z.string().uuid().optional(),
649
- machineKey: z.string().startsWith("dx_machine_"),
650
- serverUrl: z.string().min(1),
651
- supabaseUrl: z.string().min(1),
652
- supabaseAnonKey: z.string().min(1),
653
- workspacePath: z.string().min(1),
654
- investWikiMode: z.string().optional(),
655
- investWikiLocalRepo: z.string().optional(),
656
- investWikiCommand: z.string().optional(),
657
- codexCommand: z.string().optional(),
658
- codexModel: z.string().optional(),
659
- codexSandbox: z.string().optional(),
660
- localApiPort: z.number().int().positive().optional()
661
- });
662
- function expandHomePath(input) {
663
- if (input === "~") {
664
- return os.homedir();
665
- }
666
- if (input.startsWith("~/")) {
667
- return path2.join(os.homedir(), input.slice(2));
668
- }
669
- return input;
670
- }
671
- function getDaemonConfigDir(env = process.env) {
672
- return path2.resolve(
673
- expandHomePath(env.DOSSIERX_CONFIG_DIR ?? DOSSIERX_CONFIG_DIR)
674
- );
675
- }
676
- function daemonConfigPath(configDir = getDaemonConfigDir()) {
677
- return path2.join(configDir, "config.json");
678
- }
679
- async function readDaemonLocalConfig(configDir = getDaemonConfigDir()) {
680
- try {
681
- return DaemonLocalConfigSchema.parse(
682
- JSON.parse(await readFile(daemonConfigPath(configDir), "utf8"))
683
- );
684
- } catch (error) {
685
- if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
686
- return null;
687
- }
688
- throw error;
689
- }
690
- }
691
- async function writeDaemonLocalConfig(config, configDir = getDaemonConfigDir()) {
692
- const parsed = DaemonLocalConfigSchema.parse(config);
693
- await mkdir(configDir, { recursive: true, mode: 448 });
694
- const target = daemonConfigPath(configDir);
695
- await writeFile(target, `${JSON.stringify(parsed, null, 2)}
696
- `, {
697
- mode: 384
698
- });
699
- await chmod(target, 384);
700
- }
701
-
702
735
  // src/local-api/server.ts
703
736
  import { createServer } from "http";
704
737
  import { readFile as readFile3, realpath, stat } from "fs/promises";
705
- import path4 from "path";
738
+ import path5 from "path";
706
739
  import { execa as execa3 } from "execa";
707
740
  import {
741
+ readCoverageContractBundle,
708
742
  resolveInsideWorkspace as resolveInsideWorkspace2,
709
743
  scanCompanyManifest
710
744
  } from "@xdsjs/dossierx-workspace";
@@ -712,7 +746,7 @@ import {
712
746
  // src/task-archive.ts
713
747
  import { randomUUID } from "crypto";
714
748
  import { appendFile, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
715
- import path3 from "path";
749
+ import path4 from "path";
716
750
  import { resolveInsideWorkspace } from "@xdsjs/dossierx-workspace";
717
751
  var TASK_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
718
752
  function assertTaskId(taskId) {
@@ -724,7 +758,7 @@ function taskDirectory(workspaceRoot, taskId) {
724
758
  assertTaskId(taskId);
725
759
  return resolveInsideWorkspace(
726
760
  workspaceRoot,
727
- path3.posix.join(".dossierx", "tasks", taskId)
761
+ path4.posix.join(".dossierx", "tasks", taskId)
728
762
  );
729
763
  }
730
764
  async function ensureTaskDirectory(workspaceRoot, taskId) {
@@ -757,7 +791,7 @@ function createTaskArchive(options) {
757
791
  created_at: now().toISOString()
758
792
  };
759
793
  await appendFile(
760
- path3.join(directory, "events.jsonl"),
794
+ path4.join(directory, "events.jsonl"),
761
795
  `${JSON.stringify(archivedEvent)}
762
796
  `,
763
797
  "utf8"
@@ -766,13 +800,13 @@ function createTaskArchive(options) {
766
800
  },
767
801
  async appendRawOutput(taskId, stream, chunk) {
768
802
  const directory = await ensureTaskDirectory(options.workspaceRoot, taskId);
769
- await appendFile(path3.join(directory, `${stream}.log`), chunk, "utf8");
803
+ await appendFile(path4.join(directory, `${stream}.log`), chunk, "utf8");
770
804
  },
771
805
  async listEvents(taskId) {
772
806
  const directory = taskDirectory(options.workspaceRoot, taskId);
773
807
  let content = "";
774
808
  try {
775
- content = await readFile2(path3.join(directory, "events.jsonl"), "utf8");
809
+ content = await readFile2(path4.join(directory, "events.jsonl"), "utf8");
776
810
  } catch (error) {
777
811
  if (error.code === "ENOENT") {
778
812
  return [];
@@ -786,7 +820,7 @@ function createTaskArchive(options) {
786
820
 
787
821
  // src/version.ts
788
822
  var DAEMON_PACKAGE_NAME = "@xdsjs/dossierx-daemon";
789
- var DAEMON_VERSION = "0.1.2";
823
+ var DAEMON_VERSION = "0.1.4";
790
824
 
791
825
  // src/local-api/server.ts
792
826
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
@@ -813,7 +847,7 @@ function requestUrl(request) {
813
847
  return new URL(request.url ?? "/", "http://127.0.0.1");
814
848
  }
815
849
  async function readWorkspaceFile(options) {
816
- const extension = path4.extname(options.relativePath).toLowerCase();
850
+ const extension = path5.extname(options.relativePath).toLowerCase();
817
851
  if (!ALLOWED_EXTENSIONS.has(extension)) {
818
852
  throw new Error("Only markdown, text, and json files can be previewed");
819
853
  }
@@ -865,7 +899,7 @@ function uniqueProbes(probes) {
865
899
  }
866
900
  async function resolveCommand(command) {
867
901
  const normalizedCommand = command.trim();
868
- if (path4.isAbsolute(normalizedCommand)) {
902
+ if (path5.isAbsolute(normalizedCommand)) {
869
903
  return realpath(normalizedCommand).catch(() => normalizedCommand);
870
904
  }
871
905
  const result = await execa3("which", [normalizedCommand], {
@@ -1158,6 +1192,34 @@ async function startWorkspaceReadServer(options) {
1158
1192
  }
1159
1193
  return;
1160
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
+ }
1161
1223
  if (request.method !== "GET" || url.pathname !== "/workspace/read") {
1162
1224
  json(response, 404, { error: "Not found" }, origin);
1163
1225
  return;
@@ -1213,6 +1275,7 @@ import {
1213
1275
  TaskEventInputSchema,
1214
1276
  TaskSchema
1215
1277
  } from "@xdsjs/dossierx-shared";
1278
+ import { GitMirrorError } from "@xdsjs/dossierx-git-mirror";
1216
1279
 
1217
1280
  // src/errors.ts
1218
1281
  function failTaskError(error, code = "UNKNOWN", step) {
@@ -1426,20 +1489,364 @@ async function runCodexTask(task, context) {
1426
1489
  });
1427
1490
  return {
1428
1491
  generatedFiles: [],
1492
+ blockingReasons: [],
1429
1493
  manifestPath: companyManifestPath(ticker),
1430
1494
  manifest
1431
1495
  };
1432
1496
  }
1433
1497
 
1434
- // src/executors/investWiki.ts
1435
- import { access as access2, mkdir as mkdir3, readFile as readFile4, stat as stat3, writeFile as writeFile2 } from "fs/promises";
1436
- import path5 from "path";
1498
+ // src/executors/financialReports.ts
1499
+ import { createHash } from "crypto";
1500
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
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";
1437
1511
  import {
1438
1512
  buildCompanyManifest as buildCompanyManifest2,
1439
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,
1440
1847
  investWikiConfigPath,
1441
1848
  investWikiRoot as investWikiRoot2,
1442
- resolveInsideWorkspace as resolveInsideWorkspace4
1849
+ resolveInsideWorkspace as resolveInsideWorkspace5
1443
1850
  } from "@xdsjs/dossierx-workspace";
1444
1851
  async function exists(absolutePath) {
1445
1852
  try {
@@ -1464,7 +1871,7 @@ function isWorkspaceGuardError2(error) {
1464
1871
  }
1465
1872
  function workspacePath2(workspaceRoot, relativePath, step) {
1466
1873
  try {
1467
- return resolveInsideWorkspace4(workspaceRoot, relativePath);
1874
+ return resolveInsideWorkspace5(workspaceRoot, relativePath);
1468
1875
  } catch (error) {
1469
1876
  if (isWorkspaceGuardError2(error)) {
1470
1877
  throw new InvestWikiRuntimeError(
@@ -1545,11 +1952,11 @@ function isMarket(value) {
1545
1952
  async function readManifestMarket(workspaceRoot, ticker) {
1546
1953
  const manifestPath = workspacePath2(
1547
1954
  workspaceRoot,
1548
- companyManifestPath2(ticker),
1955
+ companyManifestPath3(ticker),
1549
1956
  "invest_wiki.sync"
1550
1957
  );
1551
1958
  try {
1552
- const manifest = JSON.parse(await readFile4(manifestPath, "utf8"));
1959
+ const manifest = JSON.parse(await readFile5(manifestPath, "utf8"));
1553
1960
  if (!isMarket(manifest.market)) {
1554
1961
  throw new InvestWikiRuntimeError(
1555
1962
  "VALIDATION_ERROR",
@@ -1592,7 +1999,7 @@ async function runInitCompanyVault(task, context) {
1592
1999
  `${vaultRelativePath}/.llm-wiki-invest/dossier-state.json`,
1593
2000
  "invest_wiki.init_company_vault"
1594
2001
  );
1595
- await mkdir3(vaultRoot, { recursive: true });
2002
+ await mkdir4(vaultRoot, { recursive: true });
1596
2003
  await context.appendEvent({
1597
2004
  level: "info",
1598
2005
  message: "Initializing invest-wiki vault",
@@ -1611,10 +2018,10 @@ async function runInitCompanyVault(task, context) {
1611
2018
  const statusOutput = await runner.run(["dossier", "status"], { cwd: vaultRoot });
1612
2019
  await appendOutputEvent2(context, "Invest wiki dossier status completed", statusOutput);
1613
2020
  if (!await exists(markerPath)) {
1614
- await mkdir3(path5.dirname(markerPath), { recursive: true });
1615
- 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");
1616
2023
  }
1617
- const manifest = await buildCompanyManifest2({
2024
+ const manifest = await buildCompanyManifest3({
1618
2025
  workspaceRoot: context.workspaceRoot,
1619
2026
  ticker,
1620
2027
  market: task.payload.market
@@ -1628,9 +2035,10 @@ async function runInitCompanyVault(task, context) {
1628
2035
  generatedFiles: [
1629
2036
  vaultRelativePath,
1630
2037
  markerRelativePath,
1631
- companyManifestPath2(ticker)
2038
+ companyManifestPath3(ticker)
1632
2039
  ],
1633
- manifestPath: companyManifestPath2(ticker),
2040
+ blockingReasons: [],
2041
+ manifestPath: companyManifestPath3(ticker),
1634
2042
  manifest
1635
2043
  };
1636
2044
  }
@@ -1654,7 +2062,8 @@ async function runStatus(task, context) {
1654
2062
  );
1655
2063
  return {
1656
2064
  generatedFiles: [],
1657
- manifestPath: companyManifestPath2(ticker)
2065
+ blockingReasons: [],
2066
+ manifestPath: companyManifestPath3(ticker)
1658
2067
  };
1659
2068
  }
1660
2069
  async function runSync(task, context) {
@@ -1669,14 +2078,15 @@ async function runSync(task, context) {
1669
2078
  const args = task.payload.dryRun ? ["sync", "--dry-run"] : ["sync"];
1670
2079
  const output = await runner.run(args, { cwd: vaultRoot });
1671
2080
  await appendOutputEvent2(context, "Invest wiki sync completed", output);
1672
- const manifest = await buildCompanyManifest2({
2081
+ const manifest = await buildCompanyManifest3({
1673
2082
  workspaceRoot: context.workspaceRoot,
1674
2083
  ticker,
1675
2084
  market
1676
2085
  });
1677
2086
  return {
1678
2087
  generatedFiles: [],
1679
- manifestPath: companyManifestPath2(ticker),
2088
+ blockingReasons: [],
2089
+ manifestPath: companyManifestPath3(ticker),
1680
2090
  manifest
1681
2091
  };
1682
2092
  }
@@ -1698,13 +2108,13 @@ async function runInvestWikiTask(task, context) {
1698
2108
  }
1699
2109
 
1700
2110
  // src/executors/mockWriteCompanyReport.ts
1701
- import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
1702
- import path6 from "path";
2111
+ import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
2112
+ import path8 from "path";
1703
2113
  import {
1704
- buildCompanyManifest as buildCompanyManifest3,
1705
- companyManifestPath as companyManifestPath3,
2114
+ buildCompanyManifest as buildCompanyManifest4,
2115
+ companyManifestPath as companyManifestPath4,
1706
2116
  companyRoot,
1707
- resolveInsideWorkspace as resolveInsideWorkspace5,
2117
+ resolveInsideWorkspace as resolveInsideWorkspace6,
1708
2118
  rightBusinessPath,
1709
2119
  rightPeoplePath,
1710
2120
  rightPricePath
@@ -1738,10 +2148,10 @@ var rightPrice = (ticker) => `# Right Price - ${ticker}
1738
2148
  This is a placeholder right-price analysis for ${ticker}.
1739
2149
  `;
1740
2150
  async function writeWorkspaceFile(context, relativePath, content) {
1741
- const absolutePath = resolveInsideWorkspace5(context.workspaceRoot, relativePath);
1742
- await mkdir4(path6.dirname(absolutePath), { recursive: true });
2151
+ const absolutePath = resolveInsideWorkspace6(context.workspaceRoot, relativePath);
2152
+ await mkdir5(path8.dirname(absolutePath), { recursive: true });
1743
2153
  if (!context.dryRun) {
1744
- await writeFile3(absolutePath, content);
2154
+ await writeFile4(absolutePath, content);
1745
2155
  }
1746
2156
  }
1747
2157
  async function runMockWriteCompanyReport(task, context) {
@@ -1756,7 +2166,7 @@ async function runMockWriteCompanyReport(task, context) {
1756
2166
  message: "Creating company directory",
1757
2167
  data: { ticker }
1758
2168
  });
1759
- await mkdir4(resolveInsideWorkspace5(context.workspaceRoot, companyRoot(ticker)), {
2169
+ await mkdir5(resolveInsideWorkspace6(context.workspaceRoot, companyRoot(ticker)), {
1760
2170
  recursive: true
1761
2171
  });
1762
2172
  await context.appendEvent({
@@ -1780,11 +2190,11 @@ async function runMockWriteCompanyReport(task, context) {
1780
2190
  await context.appendEvent({
1781
2191
  level: "info",
1782
2192
  message: "Generating manifest.json",
1783
- data: { path: companyManifestPath3(ticker) }
2193
+ data: { path: companyManifestPath4(ticker) }
1784
2194
  });
1785
2195
  let manifest;
1786
2196
  if (!context.dryRun) {
1787
- manifest = await buildCompanyManifest3({
2197
+ manifest = await buildCompanyManifest4({
1788
2198
  workspaceRoot: context.workspaceRoot,
1789
2199
  ticker,
1790
2200
  market: task.payload.market
@@ -1796,8 +2206,9 @@ async function runMockWriteCompanyReport(task, context) {
1796
2206
  data: { ticker }
1797
2207
  });
1798
2208
  return {
1799
- generatedFiles: [...generatedFiles, companyManifestPath3(ticker)],
1800
- manifestPath: companyManifestPath3(ticker),
2209
+ generatedFiles: [...generatedFiles, companyManifestPath4(ticker)],
2210
+ blockingReasons: [],
2211
+ manifestPath: companyManifestPath4(ticker),
1801
2212
  manifest
1802
2213
  };
1803
2214
  }
@@ -1810,6 +2221,9 @@ function getExecutor(task) {
1810
2221
  if (task.type.startsWith("invest_wiki.")) {
1811
2222
  return runInvestWikiTask;
1812
2223
  }
2224
+ if (task.type.startsWith("financial_reports.")) {
2225
+ return runFinancialReportsTask;
2226
+ }
1813
2227
  if (task.type === "mock.write_company_report") {
1814
2228
  return runMockWriteCompanyReport;
1815
2229
  }
@@ -1873,11 +2287,12 @@ async function runTask(ctx, task, agent) {
1873
2287
  }
1874
2288
  await appendEvent(ctx, task.id, "Task claimed");
1875
2289
  try {
1876
- const result = await executor(task, {
2290
+ let result = await executor(task, {
1877
2291
  workspaceRoot: ctx.workspaceRoot,
1878
2292
  dryRun: ctx.dryRun,
1879
2293
  codex: codexRunnerForTask(ctx, agent),
1880
2294
  investWiki: ctx.investWiki,
2295
+ financialReports: ctx.financialReports,
1881
2296
  appendEvent: async (event) => {
1882
2297
  await appendLocalEvent(ctx, task.id, TaskEventInputSchema.parse(event));
1883
2298
  },
@@ -1885,6 +2300,31 @@ async function runTask(ctx, task, agent) {
1885
2300
  await appendLocalRawOutput(ctx, task.id, stream, chunk);
1886
2301
  }
1887
2302
  });
2303
+ if (ctx.gitMirror && result.manifest) {
2304
+ const gitSnapshot = await ctx.gitMirror.publishCompanySnapshot({
2305
+ workspaceRoot: ctx.workspaceRoot,
2306
+ company: {
2307
+ ticker: result.manifest.ticker,
2308
+ market: result.manifest.market
2309
+ },
2310
+ taskId: task.id
2311
+ });
2312
+ await appendLocalEvent(ctx, task.id, {
2313
+ level: "info",
2314
+ message: gitSnapshot.status === "published" ? "Git mirror snapshot published" : "Git mirror snapshot unchanged",
2315
+ data: {
2316
+ commitSha: gitSnapshot.commitSha,
2317
+ treeHash: gitSnapshot.treeHash,
2318
+ branch: gitSnapshot.branch,
2319
+ pushed: gitSnapshot.pushed
2320
+ }
2321
+ });
2322
+ result = {
2323
+ ...result,
2324
+ commitSha: gitSnapshot.commitSha ?? result.commitSha,
2325
+ gitSnapshot
2326
+ };
2327
+ }
1888
2328
  await appendEvent(ctx, task.id, "Task completed");
1889
2329
  await ctx.api.completeTask(task.id, { result });
1890
2330
  } catch (error) {
@@ -1920,6 +2360,38 @@ async function runTask(ctx, task, agent) {
1920
2360
  });
1921
2361
  return;
1922
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
+ }
2379
+ if (error instanceof GitMirrorError) {
2380
+ await appendLocalEvent(ctx, task.id, {
2381
+ level: "error",
2382
+ message: error.message,
2383
+ data: { code: error.code, step: error.step }
2384
+ });
2385
+ await ctx.api.failTask(task.id, {
2386
+ error: {
2387
+ code: error.code,
2388
+ message: error.message,
2389
+ step: error.step,
2390
+ recoverable: false
2391
+ }
2392
+ });
2393
+ return;
2394
+ }
1923
2395
  await appendLocalEvent(ctx, task.id, {
1924
2396
  level: "error",
1925
2397
  message: error instanceof Error ? error.message : "Task failed",
@@ -2025,6 +2497,9 @@ async function detectCapabilities() {
2025
2497
  git,
2026
2498
  node: true,
2027
2499
  python: python3 || python,
2500
+ financialReports: true,
2501
+ financialReportsNotebookLm: false,
2502
+ financialReportsSecHtmlRenderer: false,
2028
2503
  investWikiRuntime,
2029
2504
  codex,
2030
2505
  claude
@@ -2101,9 +2576,9 @@ async function subscribeToTaskAvailable(options, onEvent, onInvalidEvent) {
2101
2576
  }
2102
2577
 
2103
2578
  // src/service.ts
2104
- import { mkdir as mkdir5, unlink, writeFile as writeFile4 } from "fs/promises";
2579
+ import { mkdir as mkdir6, unlink, writeFile as writeFile5 } from "fs/promises";
2105
2580
  import os2 from "os";
2106
- import path7 from "path";
2581
+ import path9 from "path";
2107
2582
  var LAUNCH_AGENT_LABEL = "com.xdsjs.dossierx-daemon";
2108
2583
  function xmlEscape(value) {
2109
2584
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
@@ -2112,10 +2587,10 @@ function shellQuote(value) {
2112
2587
  return `'${value.replaceAll("'", "'\\''")}'`;
2113
2588
  }
2114
2589
  function launchAgentsDir() {
2115
- return path7.join(os2.homedir(), "Library", "LaunchAgents");
2590
+ return path9.join(os2.homedir(), "Library", "LaunchAgents");
2116
2591
  }
2117
2592
  function launchAgentPlistPath(label = LAUNCH_AGENT_LABEL, dir = launchAgentsDir()) {
2118
- return path7.join(dir, `${label}.plist`);
2593
+ return path9.join(dir, `${label}.plist`);
2119
2594
  }
2120
2595
  function resolveDaemonProgramArguments(input) {
2121
2596
  if (input.daemonCommand?.trim()) {
@@ -2126,14 +2601,14 @@ function resolveDaemonProgramArguments(input) {
2126
2601
  if (!entry) {
2127
2602
  throw new Error("Unable to resolve daemon entrypoint for LaunchAgent");
2128
2603
  }
2129
- if (path7.extname(entry) === ".ts") {
2604
+ if (path9.extname(entry) === ".ts") {
2130
2605
  throw new Error(
2131
2606
  "LaunchAgent cannot run a TypeScript daemon entrypoint directly; pass --daemon-command when installing from source"
2132
2607
  );
2133
2608
  }
2134
2609
  return [
2135
2610
  input.execPath ?? process.execPath,
2136
- path7.resolve(entry),
2611
+ path9.resolve(entry),
2137
2612
  "--log-level",
2138
2613
  input.logLevel ?? "info"
2139
2614
  ];
@@ -2180,6 +2655,10 @@ function runtimeOptionsFrom(input) {
2180
2655
  codexCommand: input.codexCommand,
2181
2656
  codexModel: input.codexModel,
2182
2657
  codexSandbox: input.codexSandbox,
2658
+ gitMirrorRoot: input.gitMirrorRoot,
2659
+ gitMirrorRemote: input.gitMirrorRemote,
2660
+ gitMirrorBranch: input.gitMirrorBranch,
2661
+ gitCommand: input.gitCommand,
2183
2662
  localApiPort: input.localApiPort
2184
2663
  };
2185
2664
  }
@@ -2188,7 +2667,7 @@ async function installLaunchAgent(options = {}) {
2188
2667
  throw new Error("DossierX LaunchAgent service is only supported on macOS");
2189
2668
  }
2190
2669
  const label = options.label ?? LAUNCH_AGENT_LABEL;
2191
- const configDir = path7.resolve(
2670
+ const configDir = path9.resolve(
2192
2671
  expandHomePath(options.configDir ?? getDaemonConfigDir())
2193
2672
  );
2194
2673
  const config = await readDaemonLocalConfig(configDir);
@@ -2208,8 +2687,8 @@ async function installLaunchAgent(options = {}) {
2208
2687
  );
2209
2688
  }
2210
2689
  const plistPath = launchAgentPlistPath(label, options.launchAgentsDir);
2211
- const stdoutPath = path7.join(configDir, "daemon.out.log");
2212
- const stderrPath = path7.join(configDir, "daemon.err.log");
2690
+ const stdoutPath = path9.join(configDir, "daemon.out.log");
2691
+ const stderrPath = path9.join(configDir, "daemon.err.log");
2213
2692
  const uid = typeof process.getuid === "function" ? process.getuid() : "<uid>";
2214
2693
  const programArguments = resolveDaemonProgramArguments({
2215
2694
  daemonCommand: options.daemonCommand,
@@ -2217,9 +2696,9 @@ async function installLaunchAgent(options = {}) {
2217
2696
  argv: options.argv,
2218
2697
  execPath: options.execPath
2219
2698
  });
2220
- await mkdir5(path7.dirname(plistPath), { recursive: true });
2221
- await mkdir5(configDir, { recursive: true, mode: 448 });
2222
- await writeFile4(
2699
+ await mkdir6(path9.dirname(plistPath), { recursive: true });
2700
+ await mkdir6(configDir, { recursive: true, mode: 448 });
2701
+ await writeFile5(
2223
2702
  plistPath,
2224
2703
  buildLaunchAgentPlist({
2225
2704
  label,
@@ -2260,7 +2739,7 @@ async function uninstallLaunchAgent(options = {}) {
2260
2739
 
2261
2740
  // src/cli.ts
2262
2741
  async function ensureWorkspaceDirectory(workspace) {
2263
- await mkdir6(workspace, { recursive: true }).catch(async (error) => {
2742
+ await mkdir7(workspace, { recursive: true }).catch(async (error) => {
2264
2743
  const stats2 = await stat4(workspace).catch(() => null);
2265
2744
  if (!stats2?.isDirectory()) {
2266
2745
  throw new Error("Workspace path is not a directory");
@@ -2309,11 +2788,11 @@ function buildProgram() {
2309
2788
  ).option("--codex-command <command>", "installed Codex CLI command").option("--codex-model <model>", "Codex model override for exec mode").option(
2310
2789
  "--codex-sandbox <mode>",
2311
2790
  "Codex sandbox mode: workspace-write or danger-full-access"
2312
- ).option("--local-api-port <port>", "local workspace preview API port").option("--no-local-api", "disable local workspace preview API").action(async (options) => {
2791
+ ).option("--git-mirror-root <path>", "local Git mirror repository path").option("--git-mirror-remote <url>", "remote Git mirror repository URL").option("--git-mirror-branch <branch>", "Git mirror branch name").option("--git-command <command>", "installed Git command").option("--local-api-port <port>", "local workspace preview API port").option("--no-local-api", "disable local workspace preview API").action(async (options) => {
2313
2792
  await runDaemon(options);
2314
2793
  });
2315
2794
  const service = program.command("service").description("Manage the macOS LaunchAgent for dossierx-daemon");
2316
- service.command("install").description("Write a macOS LaunchAgent plist for persistent daemon runs").option("--label <label>", "LaunchAgent label").option("--daemon-command <command>", "custom command used by launchd").option("--log-level <level>", "daemon log level", "info").option("--invest-wiki-mode <mode>", "invest wiki runner mode").option("--invest-wiki-local-repo <path>", "local llm-wiki-invest repo path").option("--invest-wiki-command <command>", "installed llm-wiki-invest command").option("--codex-command <command>", "installed Codex CLI command").option("--codex-model <model>", "Codex model override for exec mode").option("--codex-sandbox <mode>", "Codex sandbox mode").option("--local-api-port <port>", "local workspace preview API port").action(async (options) => {
2795
+ service.command("install").description("Write a macOS LaunchAgent plist for persistent daemon runs").option("--label <label>", "LaunchAgent label").option("--daemon-command <command>", "custom command used by launchd").option("--log-level <level>", "daemon log level", "info").option("--invest-wiki-mode <mode>", "invest wiki runner mode").option("--invest-wiki-local-repo <path>", "local llm-wiki-invest repo path").option("--invest-wiki-command <command>", "installed llm-wiki-invest command").option("--codex-command <command>", "installed Codex CLI command").option("--codex-model <model>", "Codex model override for exec mode").option("--codex-sandbox <mode>", "Codex sandbox mode").option("--git-mirror-root <path>", "local Git mirror repository path").option("--git-mirror-remote <url>", "remote Git mirror repository URL").option("--git-mirror-branch <branch>", "Git mirror branch name").option("--git-command <command>", "installed Git command").option("--local-api-port <port>", "local workspace preview API port").action(async (options) => {
2317
2796
  const result = await installLaunchAgent({
2318
2797
  ...options,
2319
2798
  localApiPort: parsePort(options.localApiPort)
@@ -2347,6 +2826,10 @@ async function runDaemon(options) {
2347
2826
  codexCommand: options.codexCommand ?? localConfig?.codexCommand,
2348
2827
  codexModel: options.codexModel ?? localConfig?.codexModel,
2349
2828
  codexSandbox: options.codexSandbox ?? localConfig?.codexSandbox,
2829
+ gitMirrorRoot: options.gitMirrorRoot ?? localConfig?.gitMirrorRoot,
2830
+ gitMirrorRemote: options.gitMirrorRemote ?? localConfig?.gitMirrorRemote,
2831
+ gitMirrorBranch: options.gitMirrorBranch ?? localConfig?.gitMirrorBranch,
2832
+ gitCommand: options.gitCommand ?? localConfig?.gitCommand,
2350
2833
  localApiPort: parsePort(options.localApiPort) ?? localConfig?.localApiPort ?? parsePort(process.env.DOSSIERX_LOCAL_API_PORT) ?? DOSSIERX_DEFAULT_LOCAL_API_PORT
2351
2834
  };
2352
2835
  if (!serverUrl || !supabaseUrl || !supabaseAnonKey || !machineKey) {
@@ -2354,11 +2837,12 @@ async function runDaemon(options) {
2354
2837
  "Missing daemon connection config. Run the generated daemon command from DossierX first."
2355
2838
  );
2356
2839
  }
2357
- const workspaceRoot = path8.resolve(expandHomePath(workspacePath3));
2840
+ const workspaceRoot = path10.resolve(expandHomePath(workspacePath3));
2358
2841
  await ensureWorkspaceDirectory(workspaceRoot);
2359
2842
  const capabilities = await detectCapabilities();
2360
2843
  const investWiki = createInvestWikiRunnerFromOptions(runtimeOptions);
2361
2844
  const codex = createCodexRunnerFromOptions(runtimeOptions);
2845
+ const gitMirror = createGitMirrorFromOptions(runtimeOptions);
2362
2846
  const taskArchive = createTaskArchive({ workspaceRoot });
2363
2847
  const api = new ApiClient({
2364
2848
  serverUrl,
@@ -2384,6 +2868,10 @@ async function runDaemon(options) {
2384
2868
  codexCommand: runtimeOptions.codexCommand,
2385
2869
  codexModel: runtimeOptions.codexModel,
2386
2870
  codexSandbox: runtimeOptions.codexSandbox,
2871
+ gitMirrorRoot: runtimeOptions.gitMirrorRoot,
2872
+ gitMirrorRemote: runtimeOptions.gitMirrorRemote,
2873
+ gitMirrorBranch: runtimeOptions.gitMirrorBranch,
2874
+ gitCommand: runtimeOptions.gitCommand,
2387
2875
  localApiPort: runtimeOptions.localApiPort
2388
2876
  });
2389
2877
  const state = { running: false, pending: false };
@@ -2397,6 +2885,7 @@ async function runDaemon(options) {
2397
2885
  codex,
2398
2886
  codexOptions: runtimeOptions,
2399
2887
  investWiki,
2888
+ gitMirror,
2400
2889
  taskArchive
2401
2890
  };
2402
2891
  async function heartbeat(status) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdsjs/dossierx-daemon",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -28,8 +28,10 @@
28
28
  "execa": "^9.0.0",
29
29
  "pino": "^10.0.0",
30
30
  "zod": "^4.0.0",
31
- "@xdsjs/dossierx-workspace": "^0.1.0",
32
- "@xdsjs/dossierx-shared": "^0.1.1"
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"
33
35
  },
34
36
  "devDependencies": {
35
37
  "@types/node": "^24.0.0",