daeda-mcp 1.0.2 → 1.0.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.
@@ -0,0 +1,41 @@
1
+ {
2
+ "_comment": [
3
+ "Copy this file to ~/.daeda-mcp/export_override.json to use export override.",
4
+ "Set skipExports=true to skip HubSpot exports entirely and rely on seeding only.",
5
+ "Set skipExports=false and provide export IDs to use existing exports (avoids hitting 30/day limit).",
6
+ "Export IDs can be found from init_state.json or HubSpot API exports list."
7
+ ],
8
+ "skipExports": true,
9
+ "exports": {
10
+ "contacts": {
11
+ "exportId": null,
12
+ "status": "synced",
13
+ "error": null
14
+ },
15
+ "companies": {
16
+ "exportId": null,
17
+ "status": "synced",
18
+ "error": null
19
+ },
20
+ "deals": {
21
+ "exportId": null,
22
+ "status": "synced",
23
+ "error": null
24
+ },
25
+ "contact_company": {
26
+ "exportId": null,
27
+ "status": "synced",
28
+ "error": null
29
+ },
30
+ "deal_contact": {
31
+ "exportId": null,
32
+ "status": "synced",
33
+ "error": null
34
+ },
35
+ "deal_company": {
36
+ "exportId": null,
37
+ "status": "synced",
38
+ "error": null
39
+ }
40
+ }
41
+ }
@@ -1,4 +1,4 @@
1
- import { readInitState, writeInitState, createInitialState, isFullySynced, getSyncedCount, ALL_EXPORTS, } from "./init-state.js";
1
+ import { readInitState, writeInitState, createInitialState, isFullySynced, getSyncedCount, readExportOverride, ALL_EXPORTS, } from "./init-state.js";
2
2
  import { validateToken, startObjectExport, startAssociationExport, getExportStatus, downloadExportCsvToFile, fetchObjectProperties, findReusableExports, } from "./export-api.js";
3
3
  import { loadContactsCsvFromFile, loadCompaniesCsvFromFile, loadDealsCsvFromFile, loadAssociationsCsvFromFile, } from "./csv-loader.js";
4
4
  import { runSeeding } from "./seeder.js";
@@ -56,10 +56,20 @@ export async function startInitialization(force = false) {
56
56
  writeInitState(newState);
57
57
  console.error("[init-manager] Starting seeding first, then export requests...");
58
58
  try {
59
- runSeeding(token).catch((err) => {
59
+ const seedingPromise = runSeeding(token).catch((err) => {
60
60
  console.error("[init-manager] Seeding failed (non-fatal):", err);
61
61
  });
62
- await fireAllExportRequests(token, newState);
62
+ const skipPolling = await fireAllExportRequests(token, newState);
63
+ if (skipPolling) {
64
+ console.error("[init-manager] Override specifies skip exports or all synced - waiting for seeding only");
65
+ await seedingPromise;
66
+ newState.status = "ready";
67
+ await setMetadata("last_synced", new Date().toISOString());
68
+ await setMetadata("initialized_at", newState.startedAt || new Date().toISOString());
69
+ writeInitState(newState);
70
+ console.error("[init-manager] Initialization complete (seeding only mode)");
71
+ return;
72
+ }
63
73
  newState.status = "polling_exports";
64
74
  newState.seedingStatus = "pending";
65
75
  writeInitState(newState);
@@ -74,6 +84,34 @@ export async function startInitialization(force = false) {
74
84
  }
75
85
  }
76
86
  async function fireAllExportRequests(token, state) {
87
+ const override = readExportOverride();
88
+ if (override) {
89
+ console.error("[init-manager] Using export override file");
90
+ if (override.skipExports) {
91
+ console.error("[init-manager] skipExports=true - skipping export setup, using seeding only");
92
+ for (const exportName of ALL_EXPORTS) {
93
+ state.exports[exportName].status = "synced";
94
+ }
95
+ writeInitState(state);
96
+ return true;
97
+ }
98
+ for (const exportName of ALL_EXPORTS) {
99
+ const overrideExport = override.exports[exportName];
100
+ if (overrideExport?.exportId) {
101
+ state.exports[exportName].exportId = overrideExport.exportId;
102
+ state.exports[exportName].status = overrideExport.status;
103
+ state.exports[exportName].error = overrideExport.error;
104
+ console.error(`[init-manager] Override ${exportName}: ${overrideExport.exportId} (${overrideExport.status})`);
105
+ }
106
+ }
107
+ writeInitState(state);
108
+ const allSynced = ALL_EXPORTS.every(name => state.exports[name].status === "synced");
109
+ if (allSynced) {
110
+ console.error("[init-manager] All exports marked as synced in override - skipping poll loop");
111
+ return true;
112
+ }
113
+ return false;
114
+ }
77
115
  console.error("[init-manager] Checking for reusable exports from the past week...");
78
116
  let reusableExports;
79
117
  try {
@@ -117,6 +155,7 @@ async function fireAllExportRequests(token, state) {
117
155
  writeInitState(state);
118
156
  console.error(`[init-manager] Started new ${assoc.name} export: ${exportId}`);
119
157
  }
158
+ return false;
120
159
  }
121
160
  function startPollLoop() {
122
161
  if (pollLoopRunning) {
@@ -29,3 +29,13 @@ export declare function resetInitState(): void;
29
29
  export declare function getSyncedCount(state: InitState): number;
30
30
  export declare function isFullySynced(state: InitState): boolean;
31
31
  export declare function getStateFilePath(): string;
32
+ export interface ExportOverride {
33
+ exports: Record<ExportName, ExportState>;
34
+ skipExports?: boolean;
35
+ }
36
+ export declare function readExportOverride(): ExportOverride | null;
37
+ export declare function getOverrideFileLocations(): {
38
+ env: string | undefined;
39
+ user: string;
40
+ package: string;
41
+ };
@@ -1,9 +1,25 @@
1
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
2
  import { join, dirname } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { homedir } from "node:os";
4
5
  const __dirname = dirname(fileURLToPath(import.meta.url));
5
- const DATA_DIR = join(__dirname, "..", "..", "data");
6
- const STATE_FILE = join(DATA_DIR, "init_state.json");
6
+ const PACKAGE_DATA_DIR = join(__dirname, "..", "..", "data");
7
+ const USER_DATA_DIR = join(homedir(), ".daeda-mcp");
8
+ const STATE_FILE = join(PACKAGE_DATA_DIR, "init_state.json");
9
+ function getOverrideFilePath() {
10
+ if (process.env.DAEDA_EXPORT_OVERRIDE) {
11
+ return process.env.DAEDA_EXPORT_OVERRIDE;
12
+ }
13
+ const userOverride = join(USER_DATA_DIR, "export_override.json");
14
+ if (existsSync(userOverride)) {
15
+ return userOverride;
16
+ }
17
+ const packageOverride = join(PACKAGE_DATA_DIR, "export_override.json");
18
+ if (existsSync(packageOverride)) {
19
+ return packageOverride;
20
+ }
21
+ return null;
22
+ }
7
23
  export const ALL_EXPORTS = [
8
24
  "contacts",
9
25
  "companies",
@@ -47,7 +63,7 @@ export function readInitState() {
47
63
  }
48
64
  }
49
65
  export function writeInitState(state) {
50
- mkdirSync(DATA_DIR, { recursive: true });
66
+ mkdirSync(PACKAGE_DATA_DIR, { recursive: true });
51
67
  writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
52
68
  }
53
69
  export function resetInitState() {
@@ -62,3 +78,29 @@ export function isFullySynced(state) {
62
78
  export function getStateFilePath() {
63
79
  return STATE_FILE;
64
80
  }
81
+ export function readExportOverride() {
82
+ try {
83
+ const overridePath = getOverrideFilePath();
84
+ if (!overridePath || !existsSync(overridePath)) {
85
+ return null;
86
+ }
87
+ console.error(`[init-state] Found override file at: ${overridePath}`);
88
+ const content = readFileSync(overridePath, "utf-8");
89
+ const parsed = JSON.parse(content);
90
+ if (parsed.exports) {
91
+ return parsed;
92
+ }
93
+ return { exports: parsed };
94
+ }
95
+ catch (err) {
96
+ console.error(`[init-state] Failed to read override file:`, err);
97
+ return null;
98
+ }
99
+ }
100
+ export function getOverrideFileLocations() {
101
+ return {
102
+ env: process.env.DAEDA_EXPORT_OVERRIDE,
103
+ user: join(USER_DATA_DIR, "export_override.json"),
104
+ package: join(PACKAGE_DATA_DIR, "export_override.json"),
105
+ };
106
+ }
@@ -22,5 +22,7 @@ export interface DbStatusResult {
22
22
  };
23
23
  error?: string | null;
24
24
  message?: string;
25
+ overrideActive?: boolean;
26
+ skipExportsMode?: boolean;
25
27
  }
26
28
  export declare function dbStatus(forceReinit?: boolean): Promise<string>;
@@ -1,7 +1,7 @@
1
1
  import { getHubSpotToken } from "../db/keychain.js";
2
2
  import { dbExists, isDbHealthy, getRecordCount, getAllMetadata, } from "../db/sqlite.js";
3
3
  import { getInitStatus, forceReinitialize, } from "../sync/init-manager.js";
4
- import { getSyncedCount, ALL_EXPORTS } from "../sync/init-state.js";
4
+ import { getSyncedCount, ALL_EXPORTS, readExportOverride } from "../sync/init-state.js";
5
5
  export async function dbStatus(forceReinit = false) {
6
6
  if (forceReinit) {
7
7
  forceReinitialize();
@@ -102,9 +102,10 @@ export async function dbStatus(forceReinit = false) {
102
102
  getRecordCount("deals"),
103
103
  ]);
104
104
  const metadata = await getAllMetadata();
105
+ const override = readExportOverride();
105
106
  const result = {
106
107
  status: "ready",
107
- syncProgress: "6/6 exports synced",
108
+ syncProgress: override?.skipExports ? "seeding only (exports skipped)" : "6/6 exports synced",
108
109
  hasToken,
109
110
  lastSynced: metadata.last_synced || null,
110
111
  initializedAt: metadata.initialized_at || initState.startedAt || null,
@@ -117,6 +118,10 @@ export async function dbStatus(forceReinit = false) {
117
118
  deal_company: parseInt(metadata.deal_company_count || "0"),
118
119
  },
119
120
  };
121
+ if (override) {
122
+ result.overrideActive = true;
123
+ result.skipExportsMode = override.skipExports ?? false;
124
+ }
120
125
  if (initState.seedingStatus) {
121
126
  result.seedingStatus = initState.seedingStatus;
122
127
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daeda-mcp",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "MCP server for HubSpot CRM data sync",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,7 +25,8 @@
25
25
  "daeda-mcp": "./dist/index.js"
26
26
  },
27
27
  "files": [
28
- "dist"
28
+ "dist",
29
+ "data/export_override.sample.json"
29
30
  ],
30
31
  "scripts": {
31
32
  "build": "tsc",