daeda-mcp 1.0.3 → 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
+ }
@@ -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);
@@ -77,17 +87,30 @@ async function fireAllExportRequests(token, state) {
77
87
  const override = readExportOverride();
78
88
  if (override) {
79
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
+ }
80
98
  for (const exportName of ALL_EXPORTS) {
81
- const overrideExport = override[exportName];
99
+ const overrideExport = override.exports[exportName];
82
100
  if (overrideExport?.exportId) {
83
101
  state.exports[exportName].exportId = overrideExport.exportId;
84
102
  state.exports[exportName].status = overrideExport.status;
85
103
  state.exports[exportName].error = overrideExport.error;
86
- console.error(`[init-manager] Override ${exportName}: ${overrideExport.exportId}`);
104
+ console.error(`[init-manager] Override ${exportName}: ${overrideExport.exportId} (${overrideExport.status})`);
87
105
  }
88
106
  }
89
107
  writeInitState(state);
90
- return;
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;
91
114
  }
92
115
  console.error("[init-manager] Checking for reusable exports from the past week...");
93
116
  let reusableExports;
@@ -132,6 +155,7 @@ async function fireAllExportRequests(token, state) {
132
155
  writeInitState(state);
133
156
  console.error(`[init-manager] Started new ${assoc.name} export: ${exportId}`);
134
157
  }
158
+ return false;
135
159
  }
136
160
  function startPollLoop() {
137
161
  if (pollLoopRunning) {
@@ -29,4 +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 declare function readExportOverride(): Record<ExportName, ExportState> | null;
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,10 +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");
7
- const OVERRIDE_FILE = join(DATA_DIR, "export_override.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
+ }
8
23
  export const ALL_EXPORTS = [
9
24
  "contacts",
10
25
  "companies",
@@ -48,7 +63,7 @@ export function readInitState() {
48
63
  }
49
64
  }
50
65
  export function writeInitState(state) {
51
- mkdirSync(DATA_DIR, { recursive: true });
66
+ mkdirSync(PACKAGE_DATA_DIR, { recursive: true });
52
67
  writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
53
68
  }
54
69
  export function resetInitState() {
@@ -65,13 +80,27 @@ export function getStateFilePath() {
65
80
  }
66
81
  export function readExportOverride() {
67
82
  try {
68
- if (!existsSync(OVERRIDE_FILE)) {
83
+ const overridePath = getOverrideFilePath();
84
+ if (!overridePath || !existsSync(overridePath)) {
69
85
  return null;
70
86
  }
71
- const content = readFileSync(OVERRIDE_FILE, "utf-8");
72
- return JSON.parse(content);
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 };
73
94
  }
74
- catch {
95
+ catch (err) {
96
+ console.error(`[init-state] Failed to read override file:`, err);
75
97
  return null;
76
98
  }
77
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.3",
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",