@daeda/mcp-pro 0.1.54 → 0.1.55

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 +217 -33
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -84524,6 +84524,20 @@ async function evictDatabaseConnections(portalId) {
84524
84524
  }
84525
84525
  }
84526
84526
  }
84527
+ async function evictReadOnlyDatabaseConnections(portalId) {
84528
+ const readOnlyCachePrefix = `${portalId}:read_only:`;
84529
+ const matchingEntries = [...databaseConnectionCache.entries()].filter(
84530
+ ([cacheKey]) => cacheKey.startsWith(readOnlyCachePrefix)
84531
+ );
84532
+ for (const [cacheKey, handlePromise] of matchingEntries) {
84533
+ databaseConnectionCache.delete(cacheKey);
84534
+ try {
84535
+ const handle = await handlePromise;
84536
+ await closeDatabaseHandle(handle);
84537
+ } catch {
84538
+ }
84539
+ }
84540
+ }
84527
84541
 
84528
84542
  // src/pure/query-sql.ts
84529
84543
  var isWordCharacter = (char) => char !== void 0 && /[A-Za-z0-9_]/.test(char);
@@ -84635,6 +84649,7 @@ var resolveMasterLockDependencies = (config, defaults) => ({
84635
84649
 
84636
84650
  // src/pure/master-lock-events.ts
84637
84651
  var promotionListeners = /* @__PURE__ */ new Set();
84652
+ var demotionListeners = /* @__PURE__ */ new Set();
84638
84653
  var onPromotedToMaster = (listener) => {
84639
84654
  promotionListeners.add(listener);
84640
84655
  return () => {
@@ -84650,6 +84665,21 @@ var emitPromotedToMaster = () => {
84650
84665
  }
84651
84666
  }
84652
84667
  };
84668
+ var onDemotedToReadOnly = (listener) => {
84669
+ demotionListeners.add(listener);
84670
+ return () => {
84671
+ demotionListeners.delete(listener);
84672
+ };
84673
+ };
84674
+ var emitDemotedToReadOnly = () => {
84675
+ for (const listener of demotionListeners) {
84676
+ try {
84677
+ listener();
84678
+ } catch (error) {
84679
+ console.error("[master-lock] Demotion listener failed:", error);
84680
+ }
84681
+ }
84682
+ };
84653
84683
 
84654
84684
  // src/layers/MasterLockLive.ts
84655
84685
  var PROMOTION_POLL_MS = 3e3;
@@ -84700,6 +84730,7 @@ var handleCompromisedLease = (runtime, error) => pipe19(
84700
84730
  `[master-lock] master lease compromised: ${error.message}`
84701
84731
  ),
84702
84732
  Effect30.flatMap(() => Ref.set(runtime.connectionTypeRef, "READ_ONLY")),
84733
+ Effect30.tap(() => Effect30.sync(() => emitDemotedToReadOnly())),
84703
84734
  Effect30.flatMap(() => startPromotionPolling(runtime))
84704
84735
  );
84705
84736
  })
@@ -85202,6 +85233,32 @@ var DuckDBInterfaceLiveBase = Layer4.effect(
85202
85233
  }))
85203
85234
  )
85204
85235
  );
85236
+ const setMetadataWithoutReplica = (portalId, key2, value) => withPortalWritePermit(
85237
+ portalId,
85238
+ "setMetadataWithoutReplica",
85239
+ Effect32.orDie(
85240
+ Effect32.tryPromise({
85241
+ try: () => setPortalMetadata({
85242
+ masterLock: { isMaster: true },
85243
+ portalId,
85244
+ key: key2,
85245
+ value
85246
+ }),
85247
+ catch: (error) => error
85248
+ })
85249
+ )
85250
+ );
85251
+ const publishReplica2 = (input) => withPortalWritePermit(
85252
+ input.portalId,
85253
+ "publishReplica",
85254
+ Effect32.orDie(
85255
+ replica.syncReplica({
85256
+ portalId: input.portalId,
85257
+ reason: input.reason,
85258
+ mode: input.mode
85259
+ })
85260
+ )
85261
+ );
85205
85262
  const syncObjectArtifact2 = (input) => withPortalWritePermit(
85206
85263
  input.portalId,
85207
85264
  "syncObjectArtifact",
@@ -85406,6 +85463,8 @@ var DuckDBInterfaceLiveBase = Layer4.effect(
85406
85463
  getPropertyDefinitionStateSummary: getPropertyDefinitionStateSummary2,
85407
85464
  getWorkflowStateSummary: getWorkflowStateSummary2,
85408
85465
  setMetadata: setMetadata2,
85466
+ setMetadataWithoutReplica,
85467
+ publishReplica: publishReplica2,
85409
85468
  syncObjectArtifact: syncObjectArtifact2,
85410
85469
  syncAssociationArtifact: syncAssociationArtifact2,
85411
85470
  syncPlugin: syncPlugin2,
@@ -85420,9 +85479,13 @@ var DuckDBInterfaceLiveBase = Layer4.effect(
85420
85479
  })
85421
85480
  )
85422
85481
  );
85482
+ var SharedMasterLockLive = Layer4.orDie(MasterLockLive);
85483
+ var SharedReplicaLive = makeReplicaLive({
85484
+ masterLockLayer: SharedMasterLockLive
85485
+ });
85423
85486
  var DuckDBInterfaceLive = pipe21(
85424
85487
  DuckDBInterfaceLiveBase,
85425
- Layer4.provide(Layer4.mergeAll(Layer4.orDie(MasterLockLive), ReplicaLive))
85488
+ Layer4.provide(Layer4.mergeAll(SharedMasterLockLive, SharedReplicaLive))
85426
85489
  );
85427
85490
 
85428
85491
  // src/layers/FileDownloadLive.ts
@@ -86281,7 +86344,7 @@ var getReadConnection = (portalId, options8) => pipe25(
86281
86344
  return openLiveConnection(portalId, error);
86282
86345
  })
86283
86346
  );
86284
- var closeReadConnection = (portalId) => Effect37.promise(() => evictDatabaseConnections(portalId));
86347
+ var closeReadConnection = (portalId) => Effect37.promise(() => evictReadOnlyDatabaseConnections(portalId));
86285
86348
 
86286
86349
  // src/layers/PortalFileStateLive.ts
86287
86350
  import { Effect as Effect38, Layer as Layer6, pipe as pipe26 } from "effect";
@@ -88702,7 +88765,7 @@ Optional: pass portalIds to run the same query across one or more specific porta
88702
88765
 
88703
88766
  Data freshness: enabled portals are automatically checked for freshness before querying. If portal sync is disabled, queries use stale local data when available and otherwise return an error.
88704
88767
 
88705
- IMPORTANT: the automatic freshness check covers artifact-backed CRM data, but it does NOT make lightweight message-plugin tables current. Before querying plugin-backed tables such as lists, owners, pipelines, forms, inboxes, or sequences when current values matter, first inspect status(section="schema"), call refresh_plugins if needed, and wait for completion via status(section="schema") before running the query.
88768
+ IMPORTANT: the automatic freshness check covers artifact-backed CRM data, but it does NOT make lightweight message-plugin tables current. Before querying plugin-backed tables such as lists, owners, pipelines, forms, inboxes, or sequences when current values matter, first inspect status(section="schema"), call refresh_plugins if needed, and wait for replica-verified completion via status(section="schema") before running the query.
88706
88769
 
88707
88770
  If your task depends on both artifact-backed data and plugin-backed metadata, refresh the plugin data first, wait for completion, then run the query.
88708
88771
 
@@ -89067,7 +89130,7 @@ Optional: pass portalIds to run the same chart query across one or more specific
89067
89130
 
89068
89131
  Data freshness: enabled portals are automatically checked for freshness before charting. If portal sync is disabled, charting uses stale local data when available and otherwise returns an error.
89069
89132
 
89070
- IMPORTANT: the automatic freshness check covers artifact-backed CRM data, but it does NOT automatically refresh lightweight message-plugin tables. Before charting plugin-backed tables such as lists, owners, pipelines, forms, inboxes, or sequences when current values matter, first inspect status(section="schema"), call refresh_plugins if needed, and wait for completion via status(section="schema") before charting.
89133
+ IMPORTANT: the automatic freshness check covers artifact-backed CRM data, but it does NOT automatically refresh lightweight message-plugin tables. Before charting plugin-backed tables such as lists, owners, pipelines, forms, inboxes, or sequences when current values matter, first inspect status(section="schema"), call refresh_plugins if needed, and wait for replica-verified completion via status(section="schema") before charting.
89071
89134
 
89072
89135
  The query results map to the chart as follows:
89073
89136
  - First column \u2192 X-axis labels (categories)
@@ -89243,6 +89306,7 @@ SELECT json_extract_string(properties, '$.dealstage') as stage, COUNT(*) as coun
89243
89306
  import { z as z25 } from "zod";
89244
89307
  import fs12 from "fs";
89245
89308
  import { Effect as Effect66 } from "effect";
89309
+ var PLUGIN_SYNC_METADATA_PREFIX = "last_synced:plugin:";
89246
89310
  function formatBytes(bytes) {
89247
89311
  if (bytes === 0) return "0 B";
89248
89312
  const units = ["B", "KB", "MB", "GB"];
@@ -89255,12 +89319,16 @@ function getDbFileSize(portalId) {
89255
89319
  if (!fs12.existsSync(db2)) return null;
89256
89320
  return fs12.statSync(db2).size;
89257
89321
  }
89258
- var buildLightweightPluginFreshnessSection = (portalState, now = Date.now()) => Object.fromEntries(
89322
+ var buildLightweightPluginFreshnessSection = (portalState, now = Date.now(), replicaPluginSyncMetadata = {}) => Object.fromEntries(
89259
89323
  getMessagePlugins().map((plugin) => {
89260
89324
  const pluginState = portalState?.plugins.find((entry) => entry.name === plugin.name) ?? null;
89261
- const freshness = buildPluginFreshness(pluginState?.lastSynced?.toISOString() ?? null, now);
89325
+ const replicaLastSynced = replicaPluginSyncMetadata[`${PLUGIN_SYNC_METADATA_PREFIX}${plugin.name}`] ?? null;
89326
+ const freshness = buildPluginFreshness(
89327
+ replicaLastSynced ?? pluginState?.lastSynced?.toISOString() ?? null,
89328
+ now
89329
+ );
89262
89330
  return [plugin.name, {
89263
- status: pluginState?.status ?? "NOT_STARTED",
89331
+ status: pluginState?.status ?? (replicaLastSynced === null ? "NOT_STARTED" : "SYNCED"),
89264
89332
  error: pluginState?.error ?? null,
89265
89333
  lastRefreshedAt: freshness.lastRefreshedAt,
89266
89334
  ageMs: freshness.ageMs,
@@ -89268,6 +89336,29 @@ var buildLightweightPluginFreshnessSection = (portalState, now = Date.now()) =>
89268
89336
  }];
89269
89337
  })
89270
89338
  );
89339
+ var isMissingSyncMetadataTableError2 = (error) => {
89340
+ const message = error instanceof Error ? error.message : String(error);
89341
+ return message.includes("sync_metadata") && message.includes("does not exist");
89342
+ };
89343
+ var loadReplicaPluginSyncMetadata = async (conn) => {
89344
+ try {
89345
+ const reader = await conn.runAndReadAll(
89346
+ `SELECT key, value FROM sync_metadata WHERE key LIKE '${PLUGIN_SYNC_METADATA_PREFIX}%'`
89347
+ );
89348
+ const rows = reader.getRowObjects();
89349
+ const result = {};
89350
+ for (const row of rows) {
89351
+ if (typeof row.key !== "string" || typeof row.value !== "string") continue;
89352
+ result[row.key] = row.value;
89353
+ }
89354
+ return result;
89355
+ } catch (error) {
89356
+ if (isMissingSyncMetadataTableError2(error)) {
89357
+ return {};
89358
+ }
89359
+ throw error;
89360
+ }
89361
+ };
89271
89362
  async function buildConnectionSection(deps) {
89272
89363
  const selectedPortalId2 = deps.getSelectedPortalId();
89273
89364
  const connectionState = deps.getConnectionState();
@@ -89310,7 +89401,12 @@ async function buildSchemaSection(portalId, deps) {
89310
89401
  );
89311
89402
  const allPluginTableNames = new Set(getAllTableNames());
89312
89403
  const portalState = await deps.getPortalState(portalId);
89313
- const lightweightPlugins = buildLightweightPluginFreshnessSection(portalState);
89404
+ const replicaPluginSyncMetadata = await loadReplicaPluginSyncMetadata(conn);
89405
+ const lightweightPlugins = buildLightweightPluginFreshnessSection(
89406
+ portalState,
89407
+ Date.now(),
89408
+ replicaPluginSyncMetadata
89409
+ );
89314
89410
  const sectionResults = await Promise.all(
89315
89411
  getPlugins().map(async (plugin) => {
89316
89412
  try {
@@ -89384,7 +89480,7 @@ The schema output also includes lightweight plugin freshness data so you can see
89384
89480
 
89385
89481
  Status output also includes active and recent lightweight plugin refresh jobs so callers can inspect queued, running, completed, or failed manual refresh requests without blocking on the initial tool call.
89386
89482
 
89387
- Use status(section="schema") as the authoritative freshness gate for lightweight plugins. After calling refresh_plugins, poll status(section="schema") for the same portal until the matching refresh job is COMPLETED, none of the requested plugins are still QUEUED, RUNNING, or REPLICATING, and the requested plugins show updated lastRefreshedAt values. Do not proceed with plugin-dependent work before those conditions are met.
89483
+ Use status(section="schema") as the authoritative freshness gate for lightweight plugins. After calling refresh_plugins, poll status(section="schema") for the same portal until the matching refresh job is COMPLETED, none of the requested plugins are still QUEUED, RUNNING, or REPLICATING, and the requested plugins show updated replica-backed lastRefreshedAt values. COMPLETED means the executor verified the newly published read replica, not merely that remote execution finished. Do not proceed with plugin-dependent work before those conditions are met.
89388
89484
 
89389
89485
  Artifact-backed CRM data and lightweight plugin metadata have different freshness rules. Artifact freshness is handled automatically for query/chart reads, but lightweight plugin freshness is not.
89390
89486
 
@@ -133781,7 +133877,7 @@ VIEWING THE PLAN:
133781
133877
 
133782
133878
  IMPORTANT: After building the plan, present it to the user and ask for confirmation before calling submit_plan.
133783
133879
 
133784
- When building operations that depend on current lightweight metadata or IDs from local tables, such as owners, lists, pipelines, forms, inboxes, or sequences, first ensure those plugin tables are fresh. Use status(section="schema") to inspect freshness, refresh_plugins to refresh the required plugin set, and wait for completion with status(section="schema") before resolving IDs or constructing operations that depend on them.
133880
+ When building operations that depend on current lightweight metadata or IDs from local tables, such as owners, lists, pipelines, forms, inboxes, or sequences, first ensure those plugin tables are fresh. Use status(section="schema") to inspect freshness, refresh_plugins to refresh the required plugin set, and wait for replica-verified completion with status(section="schema") before resolving IDs or constructing operations that depend on them.
133785
133881
 
133786
133882
  WORKFLOW:
133787
133883
  1. describe_operations - Discover available operation types and their required fields
@@ -134449,7 +134545,7 @@ The requested pluginNames array is required. Optional portalIds lets you refresh
134449
134545
 
134450
134546
  This tool starts an asynchronous refresh job and returns immediately. It does NOT wait for fresh data to be available.
134451
134547
 
134452
- After calling refresh_plugins, do not run any query, chart, or plan-building step that depends on those plugin tables until you confirm completion with status(section="schema"). Wait until the relevant refresh job is COMPLETED and the requested plugins show updated freshness data. If the job FAILED, surface the error instead of proceeding.
134548
+ After calling refresh_plugins, do not run any query, chart, or plan-building step that depends on those plugin tables until you confirm completion with status(section="schema"). Wait until the relevant refresh job is COMPLETED and the requested plugins show updated replica-backed freshness data. COMPLETED means the executor verified the newly published read replica, not merely that remote execution finished. If the job FAILED, surface the error instead of proceeding.
134453
134549
 
134454
134550
  If the same plugin set is needed across multiple portals, prefer one call using portalIds. Use \`status\` to inspect in-flight and recent refresh jobs.`,
134455
134551
  inputSchema: {
@@ -134734,6 +134830,25 @@ var createPluginRefreshJobStore = () => {
134734
134830
  var earliestStartedAt = (plugins2) => plugins2.map((plugin) => plugin.startedAt).filter((value) => value !== null).sort()[0] ?? null;
134735
134831
  var finalizeManualPluginRefreshJob = async (input) => {
134736
134832
  const startedAt = earliestStartedAt(input.plugins);
134833
+ try {
134834
+ await input.prepareReplicaVerification();
134835
+ } catch (error) {
134836
+ const message = input.formatError(error);
134837
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
134838
+ await input.updateJobProgress({
134839
+ jobId: input.jobId,
134840
+ portalId: input.portalId,
134841
+ pluginNames: input.pluginNames,
134842
+ createdAt: input.createdAt,
134843
+ status: "FAILED",
134844
+ plugins: input.plugins,
134845
+ startedAt,
134846
+ finishedAt,
134847
+ error: message,
134848
+ relayToServer: input.relayToServer
134849
+ });
134850
+ return;
134851
+ }
134737
134852
  await input.updateJobProgress({
134738
134853
  jobId: input.jobId,
134739
134854
  portalId: input.portalId,
@@ -134746,6 +134861,7 @@ var finalizeManualPluginRefreshJob = async (input) => {
134746
134861
  });
134747
134862
  try {
134748
134863
  await input.publishReplica();
134864
+ await input.verifyReplica();
134749
134865
  } catch (error) {
134750
134866
  const message = input.formatError(error);
134751
134867
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -135172,6 +135288,7 @@ var STATUS_METADATA = {
135172
135288
  };
135173
135289
  var UPDATE_CHECK_TIMEOUT_MS = 2e3;
135174
135290
  var UPDATE_CHECK_CACHE_TTL_MS = 5 * 6e4;
135291
+ var PLUGIN_REFRESH_VERIFICATION_PREFIX = "plugin_refresh_verification:";
135175
135292
  var latestPackageVersionCache = null;
135176
135293
  var fetchLatestPackageVersion = async (packageName) => {
135177
135294
  const now = Date.now();
@@ -135247,6 +135364,26 @@ var mainProgram = Effect147.gen(function* () {
135247
135364
  const isMasterClient = () => getConnectionType() === "MASTER";
135248
135365
  const getSyncRole = () => isMasterClient() ? "master" : "read_only";
135249
135366
  const formatErrorMessage2 = (error) => error instanceof Error ? error.message : String(error);
135367
+ const buildPluginRefreshVerificationKey = (jobId) => `${PLUGIN_REFRESH_VERIFICATION_PREFIX}${jobId}`;
135368
+ const buildStalePluginRefreshExecutorProgress = (jobId, portalId, pluginNames) => {
135369
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
135370
+ const error = `Rejected plugin refresh execute for portal ${portalId} because this client is read-only locally.`;
135371
+ return {
135372
+ job_id: jobId,
135373
+ target_portal: portalId,
135374
+ plugin_names: [...pluginNames],
135375
+ status: "FAILED",
135376
+ plugins: pluginNames.map((pluginName) => ({
135377
+ plugin_name: pluginName,
135378
+ status: "FAILED",
135379
+ error,
135380
+ finished_at: finishedAt
135381
+ })),
135382
+ created_at: finishedAt,
135383
+ finished_at: finishedAt,
135384
+ error
135385
+ };
135386
+ };
135250
135387
  const logReadOnlySkip = (action, portalId) => {
135251
135388
  const portalPart = portalId === void 0 ? "" : ` for portal ${portalId}`;
135252
135389
  console.error(`[sync] Skipping ${action}${portalPart} on read-only client`);
@@ -135273,6 +135410,20 @@ var mainProgram = Effect147.gen(function* () {
135273
135410
  syncRole: getSyncRole()
135274
135411
  })
135275
135412
  );
135413
+ const refreshRegistrationForConnectionTypeChange = (reason) => {
135414
+ scheduleRuntimeReconcile();
135415
+ Effect147.runFork(
135416
+ pipe122(
135417
+ Effect147.promise(() => syncRegistrationContext()),
135418
+ Effect147.flatMap(() => ws6.refreshRegistration()),
135419
+ Effect147.catchAll(
135420
+ (error) => Effect147.sync(() => {
135421
+ console.error(`[ws] Failed to refresh registration after ${reason}:`, error);
135422
+ })
135423
+ )
135424
+ )
135425
+ );
135426
+ };
135276
135427
  const ensureFresh = makeEnsureFresh({
135277
135428
  getSelectedPortalId: getPortalId,
135278
135429
  isMasterClient,
@@ -135502,6 +135653,10 @@ var mainProgram = Effect147.gen(function* () {
135502
135653
  });
135503
135654
  return;
135504
135655
  }
135656
+ const verificationMarker = {
135657
+ key: buildPluginRefreshVerificationKey(input.jobId),
135658
+ value: (/* @__PURE__ */ new Date()).toISOString()
135659
+ };
135505
135660
  await finalizeManualPluginRefreshJob({
135506
135661
  jobId: input.jobId,
135507
135662
  portalId: input.portalId,
@@ -135509,20 +135664,36 @@ var mainProgram = Effect147.gen(function* () {
135509
135664
  createdAt: input.createdAt,
135510
135665
  plugins: plugins2,
135511
135666
  relayToServer: input.relayToServer,
135667
+ prepareReplicaVerification: async () => {
135668
+ await Effect147.runPromise(
135669
+ duckDb.setMetadataWithoutReplica(
135670
+ input.portalId,
135671
+ verificationMarker.key,
135672
+ verificationMarker.value
135673
+ )
135674
+ );
135675
+ },
135512
135676
  updateJobProgress,
135513
135677
  publishReplica: () => Effect147.runPromise(
135514
- pipe122(
135515
- ReplicaService,
135516
- Effect147.flatMap(
135517
- (replica) => replica.syncReplica({
135518
- portalId: input.portalId,
135519
- reason: `manualPluginRefresh:${input.jobId}`,
135520
- mode: "immediate"
135521
- })
135522
- ),
135523
- Effect147.provide(ReplicaLive)
135524
- )
135678
+ duckDb.publishReplica({
135679
+ portalId: input.portalId,
135680
+ reason: `manualPluginRefresh:${input.jobId}`,
135681
+ mode: "immediate"
135682
+ })
135525
135683
  ),
135684
+ verifyReplica: async () => {
135685
+ await evictReadOnlyDatabaseConnections(input.portalId);
135686
+ const replicatedValue = await getPortalMetadata({
135687
+ portalId: input.portalId,
135688
+ key: verificationMarker.key,
135689
+ masterLock: { isMaster: false }
135690
+ });
135691
+ if (replicatedValue !== verificationMarker.value) {
135692
+ throw new Error(
135693
+ `Replica verification failed for portal ${input.portalId}: expected marker '${verificationMarker.key}' to equal '${verificationMarker.value}', got '${replicatedValue ?? "null"}'.`
135694
+ );
135695
+ }
135696
+ },
135526
135697
  formatError: formatErrorMessage2
135527
135698
  });
135528
135699
  }
@@ -135926,17 +136097,10 @@ var mainProgram = Effect147.gen(function* () {
135926
136097
  scheduleRuntimeReconcile = runtimeReconciler.scheduleReconcile;
135927
136098
  runtimeReconciler.startPolling();
135928
136099
  onPromotedToMaster(() => {
135929
- Effect147.runFork(
135930
- pipe122(
135931
- Effect147.promise(() => syncRegistrationContext()),
135932
- Effect147.flatMap(() => ws6.refreshRegistration()),
135933
- Effect147.catchAll(
135934
- (error) => Effect147.sync(() => {
135935
- console.error("[ws] Failed to refresh registration after promotion:", error);
135936
- })
135937
- )
135938
- )
135939
- );
136100
+ refreshRegistrationForConnectionTypeChange("promotion");
136101
+ });
136102
+ onDemotedToReadOnly(() => {
136103
+ refreshRegistrationForConnectionTypeChange("demotion");
135940
136104
  });
135941
136105
  runtimeReconciler.scheduleReconcile();
135942
136106
  configureArtifactQueue(
@@ -136214,6 +136378,26 @@ var mainProgram = Effect147.gen(function* () {
136214
136378
  onPluginRefreshExecute: (payload) => {
136215
136379
  if (!isMasterClient()) {
136216
136380
  logReadOnlySkip("plugin refresh execute", payload.target_portal);
136381
+ refreshRegistrationForConnectionTypeChange("demotion");
136382
+ Effect147.runFork(
136383
+ pipe122(
136384
+ ws6.sendPluginRefreshProgress(
136385
+ buildStalePluginRefreshExecutorProgress(
136386
+ payload.job_id,
136387
+ payload.target_portal,
136388
+ payload.plugin_names
136389
+ )
136390
+ ),
136391
+ Effect147.catchAll(
136392
+ (error) => Effect147.sync(() => {
136393
+ console.error(
136394
+ `[plugin-refresh] Failed to reject stale executor for portal ${payload.target_portal}:`,
136395
+ error
136396
+ );
136397
+ })
136398
+ )
136399
+ )
136400
+ );
136217
136401
  return;
136218
136402
  }
136219
136403
  const existing = refreshJobStore.getSnapshot(payload.job_id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daeda/mcp-pro",
3
- "version": "0.1.54",
3
+ "version": "0.1.55",
4
4
  "description": "MCP server for HubSpot CRM — sync, query, and manage your portal data",
5
5
  "type": "module",
6
6
  "bin": {