@adhdev/daemon-core 0.8.19 → 0.8.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -60,14 +60,6 @@ function asBoolean(value, fallback) {
60
60
  }
61
61
  function normalizeConfig(raw) {
62
62
  const parsed = isPlainObject(raw) ? raw : {};
63
- const legacySessionReads = isPlainObject(parsed.recentSessionReads) ? parsed.recentSessionReads : {};
64
- const sessionReads = isPlainObject(parsed.sessionReads) ? parsed.sessionReads : {};
65
- const mergedSessionReads = Object.fromEntries(
66
- Object.entries({ ...legacySessionReads, ...sessionReads }).filter(([, value]) => typeof value === "number" && Number.isFinite(value))
67
- );
68
- const sessionReadMarkers = Object.fromEntries(
69
- Object.entries(isPlainObject(parsed.sessionReadMarkers) ? parsed.sessionReadMarkers : {}).filter(([, value]) => typeof value === "string")
70
- );
71
63
  return {
72
64
  serverUrl: typeof parsed.serverUrl === "string" && parsed.serverUrl.trim() ? parsed.serverUrl : DEFAULT_CONFIG.serverUrl,
73
65
  selectedIde: asNullableString(parsed.selectedIde),
@@ -80,10 +72,6 @@ function normalizeConfig(raw) {
80
72
  enabledIdes: asStringArray(parsed.enabledIdes),
81
73
  workspaces: Array.isArray(parsed.workspaces) ? parsed.workspaces : [],
82
74
  defaultWorkspaceId: asNullableString(parsed.defaultWorkspaceId) ?? asNullableString(parsed.activeWorkspaceId),
83
- recentActivity: Array.isArray(parsed.recentActivity) ? parsed.recentActivity : [],
84
- savedProviderSessions: Array.isArray(parsed.savedProviderSessions) ? parsed.savedProviderSessions : [],
85
- sessionReads: mergedSessionReads,
86
- sessionReadMarkers,
87
75
  machineNickname: asNullableString(parsed.machineNickname),
88
76
  machineId: asOptionalString(parsed.machineId),
89
77
  machineSecret: parsed.machineSecret === null ? null : asOptionalString(parsed.machineSecret),
@@ -124,6 +112,30 @@ function getConfigDir() {
124
112
  function getConfigPath() {
125
113
  return join(getConfigDir(), "config.json");
126
114
  }
115
+ function migrateStateToStateFile(raw) {
116
+ const statePath = join(getConfigDir(), "state.json");
117
+ if (existsSync(statePath)) return;
118
+ const recentActivity = Array.isArray(raw.recentActivity) ? raw.recentActivity : [];
119
+ const savedProviderSessions = Array.isArray(raw.savedProviderSessions) ? raw.savedProviderSessions : [];
120
+ const legacySessionReads = isPlainObject(raw.recentSessionReads) ? raw.recentSessionReads : {};
121
+ const sessionReads = isPlainObject(raw.sessionReads) ? raw.sessionReads : {};
122
+ const sessionReadMarkers = isPlainObject(raw.sessionReadMarkers) ? raw.sessionReadMarkers : {};
123
+ const hasData = recentActivity.length > 0 || savedProviderSessions.length > 0 || Object.keys(sessionReads).length > 0 || Object.keys(legacySessionReads).length > 0 || Object.keys(sessionReadMarkers).length > 0;
124
+ if (!hasData) return;
125
+ const mergedReads = Object.fromEntries(
126
+ Object.entries({ ...legacySessionReads, ...sessionReads }).filter(([, v]) => typeof v === "number" && Number.isFinite(v))
127
+ );
128
+ const cleanedMarkers = Object.fromEntries(
129
+ Object.entries(sessionReadMarkers).filter(([, v]) => typeof v === "string")
130
+ );
131
+ const state = {
132
+ recentActivity,
133
+ savedProviderSessions,
134
+ sessionReads: mergedReads,
135
+ sessionReadMarkers: cleanedMarkers
136
+ };
137
+ writeFileSync(statePath, JSON.stringify(state, null, 2), { encoding: "utf-8", mode: 384 });
138
+ }
127
139
  function loadConfig() {
128
140
  const configPath = getConfigPath();
129
141
  if (!existsSync(configPath)) {
@@ -137,6 +149,7 @@ function loadConfig() {
137
149
  try {
138
150
  const raw = readFileSync(configPath, "utf-8");
139
151
  const parsed = JSON.parse(raw);
152
+ migrateStateToStateFile(parsed);
140
153
  const normalizedInput = normalizeConfig(parsed);
141
154
  const ensured = ensureMachineId(normalizedInput);
142
155
  const normalized = ensured.config;
@@ -204,10 +217,6 @@ var init_config = __esm({
204
217
  enabledIdes: [],
205
218
  workspaces: [],
206
219
  defaultWorkspaceId: null,
207
- recentActivity: [],
208
- savedProviderSessions: [],
209
- sessionReads: {},
210
- sessionReadMarkers: {},
211
220
  machineNickname: null,
212
221
  machineId: void 0,
213
222
  machineSecret: null,
@@ -2730,35 +2739,35 @@ function buildRecentActivityKeyForEntry(entry) {
2730
2739
  }
2731
2740
  return buildRecentActivityKey(entry);
2732
2741
  }
2733
- function appendRecentActivity(config, entry) {
2742
+ function appendRecentActivity(state, entry) {
2734
2743
  const nextEntry = {
2735
2744
  ...entry,
2736
2745
  workspace: entry.workspace ? normalizeWorkspace(entry.workspace) : void 0,
2737
2746
  id: buildRecentActivityKeyForEntry(entry),
2738
2747
  lastUsedAt: entry.lastUsedAt || Date.now()
2739
2748
  };
2740
- const filtered = (config.recentActivity || []).filter((item) => item.id !== nextEntry.id);
2749
+ const filtered = (state.recentActivity || []).filter((item) => item.id !== nextEntry.id);
2741
2750
  return {
2742
- ...config,
2751
+ ...state,
2743
2752
  recentActivity: [nextEntry, ...filtered].slice(0, MAX_ACTIVITY)
2744
2753
  };
2745
2754
  }
2746
- function getRecentActivity(config, limit = 20) {
2747
- return [...config.recentActivity || []].sort((a, b) => b.lastUsedAt - a.lastUsedAt).slice(0, limit);
2755
+ function getRecentActivity(state, limit = 20) {
2756
+ return [...state.recentActivity || []].sort((a, b) => b.lastUsedAt - a.lastUsedAt).slice(0, limit);
2748
2757
  }
2749
- function getSessionSeenAt(config, sessionId) {
2750
- return config.sessionReads?.[sessionId] || 0;
2758
+ function getSessionSeenAt(state, sessionId) {
2759
+ return state.sessionReads?.[sessionId] || 0;
2751
2760
  }
2752
- function getSessionSeenMarker(config, sessionId) {
2753
- return config.sessionReadMarkers?.[sessionId] || "";
2761
+ function getSessionSeenMarker(state, sessionId) {
2762
+ return state.sessionReadMarkers?.[sessionId] || "";
2754
2763
  }
2755
- function markSessionSeen(config, sessionId, seenAt = Date.now(), completionMarker) {
2756
- const prev = config.sessionReads || {};
2764
+ function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker) {
2765
+ const prev = state.sessionReads || {};
2757
2766
  const nextSeenAt = Math.max(prev[sessionId] || 0, seenAt);
2758
- const prevMarkers = config.sessionReadMarkers || {};
2767
+ const prevMarkers = state.sessionReadMarkers || {};
2759
2768
  const nextMarker = typeof completionMarker === "string" ? completionMarker : "";
2760
2769
  return {
2761
- ...config,
2770
+ ...state,
2762
2771
  sessionReads: {
2763
2772
  ...prev,
2764
2773
  [sessionId]: nextSeenAt
@@ -2784,11 +2793,11 @@ function normalizeWorkspace2(workspace) {
2784
2793
  function buildSavedProviderSessionKey(providerSessionId) {
2785
2794
  return `saved:${providerSessionId.trim()}`;
2786
2795
  }
2787
- function upsertSavedProviderSession(config, entry) {
2796
+ function upsertSavedProviderSession(state, entry) {
2788
2797
  const providerSessionId = typeof entry.providerSessionId === "string" ? entry.providerSessionId.trim() : "";
2789
- if (!providerSessionId) return config;
2798
+ if (!providerSessionId) return state;
2790
2799
  const id = buildSavedProviderSessionKey(providerSessionId);
2791
- const existing = (config.savedProviderSessions || []).find((item) => item.id === id);
2800
+ const existing = (state.savedProviderSessions || []).find((item) => item.id === id);
2792
2801
  const nextEntry = {
2793
2802
  id,
2794
2803
  kind: entry.kind,
@@ -2801,23 +2810,75 @@ function upsertSavedProviderSession(config, entry) {
2801
2810
  createdAt: existing?.createdAt || entry.createdAt || Date.now(),
2802
2811
  lastUsedAt: entry.lastUsedAt || Date.now()
2803
2812
  };
2804
- const filtered = (config.savedProviderSessions || []).filter((item) => item.id !== id);
2813
+ const filtered = (state.savedProviderSessions || []).filter((item) => item.id !== id);
2805
2814
  return {
2806
- ...config,
2815
+ ...state,
2807
2816
  savedProviderSessions: [nextEntry, ...filtered].slice(0, MAX_SAVED_SESSIONS)
2808
2817
  };
2809
2818
  }
2810
- function getSavedProviderSessions(config, filters) {
2811
- return [...config.savedProviderSessions || []].filter((entry) => {
2819
+ function getSavedProviderSessions(state, filters) {
2820
+ return [...state.savedProviderSessions || []].filter((entry) => {
2812
2821
  if (filters?.providerType && entry.providerType !== filters.providerType) return false;
2813
2822
  if (filters?.kind && entry.kind !== filters.kind) return false;
2814
2823
  return true;
2815
2824
  }).sort((a, b) => b.lastUsedAt - a.lastUsedAt);
2816
2825
  }
2817
2826
 
2827
+ // src/config/state-store.ts
2828
+ init_config();
2829
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2830
+ import { join as join3 } from "path";
2831
+ var DEFAULT_STATE = {
2832
+ recentActivity: [],
2833
+ savedProviderSessions: [],
2834
+ sessionReads: {},
2835
+ sessionReadMarkers: {}
2836
+ };
2837
+ function isPlainObject2(value) {
2838
+ return !!value && typeof value === "object" && !Array.isArray(value);
2839
+ }
2840
+ function getStatePath() {
2841
+ return join3(getConfigDir(), "state.json");
2842
+ }
2843
+ function normalizeState(raw) {
2844
+ const parsed = isPlainObject2(raw) ? raw : {};
2845
+ const sessionReads = Object.fromEntries(
2846
+ Object.entries(isPlainObject2(parsed.sessionReads) ? parsed.sessionReads : {}).filter(([, value]) => typeof value === "number" && Number.isFinite(value))
2847
+ );
2848
+ const sessionReadMarkers = Object.fromEntries(
2849
+ Object.entries(isPlainObject2(parsed.sessionReadMarkers) ? parsed.sessionReadMarkers : {}).filter(([, value]) => typeof value === "string")
2850
+ );
2851
+ return {
2852
+ recentActivity: Array.isArray(parsed.recentActivity) ? parsed.recentActivity : [],
2853
+ savedProviderSessions: Array.isArray(parsed.savedProviderSessions) ? parsed.savedProviderSessions : [],
2854
+ sessionReads,
2855
+ sessionReadMarkers
2856
+ };
2857
+ }
2858
+ function loadState() {
2859
+ const statePath = getStatePath();
2860
+ if (!existsSync3(statePath)) {
2861
+ return { ...DEFAULT_STATE };
2862
+ }
2863
+ try {
2864
+ const raw = readFileSync2(statePath, "utf-8");
2865
+ return normalizeState(JSON.parse(raw));
2866
+ } catch {
2867
+ return { ...DEFAULT_STATE };
2868
+ }
2869
+ }
2870
+ function saveState(state) {
2871
+ const statePath = getStatePath();
2872
+ const normalized = normalizeState(state);
2873
+ writeFileSync2(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
2874
+ }
2875
+ function resetState() {
2876
+ saveState({ ...DEFAULT_STATE });
2877
+ }
2878
+
2818
2879
  // src/detection/ide-detector.ts
2819
2880
  import { execSync } from "child_process";
2820
- import { existsSync as existsSync3 } from "fs";
2881
+ import { existsSync as existsSync4 } from "fs";
2821
2882
  import { platform, homedir as homedir3 } from "os";
2822
2883
  var BUILTIN_IDE_DEFINITIONS = [];
2823
2884
  var registeredIDEs = /* @__PURE__ */ new Map();
@@ -2863,9 +2924,9 @@ function checkPathExists(paths) {
2863
2924
  if (p.includes("*")) {
2864
2925
  const username = home.split(/[\\/]/).pop() || "";
2865
2926
  const resolved = p.replace("*", username);
2866
- if (existsSync3(resolved)) return resolved;
2927
+ if (existsSync4(resolved)) return resolved;
2867
2928
  } else {
2868
- if (existsSync3(p)) return p;
2929
+ if (existsSync4(p)) return p;
2869
2930
  }
2870
2931
  }
2871
2932
  return null;
@@ -2880,7 +2941,7 @@ async function detectIDEs() {
2880
2941
  let resolvedCli = cliPath;
2881
2942
  if (!resolvedCli && appPath && os18 === "darwin") {
2882
2943
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
2883
- if (existsSync3(bundledCli)) resolvedCli = bundledCli;
2944
+ if (existsSync4(bundledCli)) resolvedCli = bundledCli;
2884
2945
  }
2885
2946
  if (!resolvedCli && appPath && os18 === "win32") {
2886
2947
  const { dirname: dirname6 } = await import("path");
@@ -2893,7 +2954,7 @@ async function detectIDEs() {
2893
2954
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
2894
2955
  ];
2895
2956
  for (const c of candidates) {
2896
- if (existsSync3(c)) {
2957
+ if (existsSync4(c)) {
2897
2958
  resolvedCli = c;
2898
2959
  break;
2899
2960
  }
@@ -9532,9 +9593,9 @@ var DaemonCliManager = class {
9532
9593
  }
9533
9594
  persistRecentActivity(entry) {
9534
9595
  try {
9535
- let nextConfig = appendRecentActivity(loadConfig(), entry);
9596
+ let nextState = appendRecentActivity(loadState(), entry);
9536
9597
  if (entry.providerSessionId && (entry.kind === "cli" || entry.kind === "acp")) {
9537
- nextConfig = upsertSavedProviderSession(nextConfig, {
9598
+ nextState = upsertSavedProviderSession(nextState, {
9538
9599
  kind: entry.kind,
9539
9600
  providerType: entry.providerType,
9540
9601
  providerName: entry.providerName,
@@ -9544,7 +9605,7 @@ var DaemonCliManager = class {
9544
9605
  title: entry.title
9545
9606
  });
9546
9607
  }
9547
- saveConfig(nextConfig);
9608
+ saveState(nextState);
9548
9609
  } catch (e) {
9549
9610
  console.error(colorize("red", ` \u2717 Failed to save recent activity: ${e}`));
9550
9611
  }
@@ -10336,14 +10397,14 @@ var ProviderLoader = class _ProviderLoader {
10336
10397
  */
10337
10398
  setIdeExtensionEnabled(ideType, extensionType, enabled) {
10338
10399
  try {
10339
- const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = (init_config(), __toCommonJS(config_exports));
10400
+ const { loadConfig: loadConfig2, saveConfig: saveConfig3 } = (init_config(), __toCommonJS(config_exports));
10340
10401
  const config = loadConfig2();
10341
10402
  const baseIdeType = ideType.split("_")[0];
10342
10403
  if (!config.ideSettings) config.ideSettings = {};
10343
10404
  if (!config.ideSettings[baseIdeType]) config.ideSettings[baseIdeType] = {};
10344
10405
  if (!config.ideSettings[baseIdeType].extensions) config.ideSettings[baseIdeType].extensions = {};
10345
10406
  config.ideSettings[baseIdeType].extensions[extensionType] = { enabled };
10346
- saveConfig2(config);
10407
+ saveConfig3(config);
10347
10408
  this.log(`IDE extension setting: ${ideType}.${extensionType}.enabled = ${enabled}`);
10348
10409
  return true;
10349
10410
  } catch (e) {
@@ -10908,12 +10969,12 @@ var ProviderLoader = class _ProviderLoader {
10908
10969
  }
10909
10970
  if (schemaDef.type === "select" && schemaDef.options && !schemaDef.options.includes(value)) return false;
10910
10971
  try {
10911
- const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = (init_config(), __toCommonJS(config_exports));
10972
+ const { loadConfig: loadConfig2, saveConfig: saveConfig3 } = (init_config(), __toCommonJS(config_exports));
10912
10973
  const config = loadConfig2();
10913
10974
  if (!config.providerSettings) config.providerSettings = {};
10914
10975
  if (!config.providerSettings[type]) config.providerSettings[type] = {};
10915
10976
  config.providerSettings[type][key] = value;
10916
- saveConfig2(config);
10977
+ saveConfig3(config);
10917
10978
  this.log(`Setting updated: ${type}.${key} = ${JSON.stringify(value)}`);
10918
10979
  return true;
10919
10980
  } catch (e) {
@@ -11691,16 +11752,17 @@ function buildRecentLaunches(recentActivity) {
11691
11752
  }
11692
11753
  function buildStatusSnapshot(options) {
11693
11754
  const cfg = loadConfig();
11755
+ const state = loadState();
11694
11756
  const wsState = getWorkspaceState(cfg);
11695
11757
  const memSnap = getHostMemorySnapshot();
11696
- const recentActivity = getRecentActivity(cfg, 20);
11758
+ const recentActivity = getRecentActivity(state, 20);
11697
11759
  const sessions = buildSessionEntries(
11698
11760
  options.allStates,
11699
11761
  options.cdpManagers
11700
11762
  );
11701
11763
  for (const session of sessions) {
11702
- const lastSeenAt = getSessionSeenAt(cfg, session.id);
11703
- const seenCompletionMarker = getSessionSeenMarker(cfg, session.id);
11764
+ const lastSeenAt = getSessionSeenAt(state, session.id);
11765
+ const seenCompletionMarker = getSessionSeenMarker(state, session.id);
11704
11766
  const lastUsedAt = getSessionLastUsedAt(session);
11705
11767
  const completionMarker = getSessionCompletionMarker(session);
11706
11768
  const { unread, inboxBucket } = session.surfaceHidden ? { unread: false, inboxBucket: "idle" } : getUnreadState(
@@ -12038,9 +12100,9 @@ var DaemonCommandRouter = class {
12038
12100
  const offset = Math.max(0, Number(args?.offset) || 0);
12039
12101
  const limit = Math.max(1, Math.min(100, Number(args?.limit) || 30));
12040
12102
  const { sessions: historySessions, hasMore } = listSavedHistorySessions(providerType, { offset, limit });
12041
- const config = loadConfig();
12042
- const savedSessions = getSavedProviderSessions(config, { providerType, kind });
12043
- const recentSessions = getRecentActivity(config, 200).filter((entry) => entry.providerType === providerType && entry.kind === kind && entry.providerSessionId);
12103
+ const state = loadState();
12104
+ const savedSessions = getSavedProviderSessions(state, { providerType, kind });
12105
+ const recentSessions = getRecentActivity(state, 200).filter((entry) => entry.providerType === providerType && entry.kind === kind && entry.providerSessionId);
12044
12106
  const savedSessionById = new Map(savedSessions.map((entry) => [entry.providerSessionId, entry]));
12045
12107
  const recentSessionById = new Map(recentSessions.map((entry) => [entry.providerSessionId, entry]));
12046
12108
  const providerMeta = this.deps.providerLoader.getMeta(providerType);
@@ -12131,19 +12193,19 @@ var DaemonCommandRouter = class {
12131
12193
  this.deps.onIdeConnected?.();
12132
12194
  if (result.success && resolvedWorkspace) {
12133
12195
  try {
12134
- const next = appendRecentActivity(loadConfig(), {
12196
+ const next = appendRecentActivity(loadState(), {
12135
12197
  kind: "ide",
12136
12198
  providerType: result.ideId || ideKey,
12137
12199
  providerName: result.ideId || ideKey,
12138
12200
  workspace: resolvedWorkspace,
12139
12201
  title: result.ideId || ideKey
12140
12202
  });
12141
- saveConfig(next);
12203
+ saveState(next);
12142
12204
  } catch {
12143
12205
  }
12144
12206
  } else if (result.success && (result.ideId || ideKey)) {
12145
12207
  try {
12146
- saveConfig(appendRecentActivity(loadConfig(), {
12208
+ saveState(appendRecentActivity(loadState(), {
12147
12209
  kind: "ide",
12148
12210
  providerType: result.ideId || ideKey,
12149
12211
  providerName: result.ideId || ideKey,
@@ -12172,8 +12234,8 @@ var DaemonCommandRouter = class {
12172
12234
  if (!sessionId || typeof sessionId !== "string") {
12173
12235
  return { success: false, error: "sessionId is required" };
12174
12236
  }
12175
- const currentConfig = loadConfig();
12176
- const prevSeenAt = currentConfig.sessionReads?.[sessionId] || 0;
12237
+ const currentState = loadState();
12238
+ const prevSeenAt = currentState.sessionReads?.[sessionId] || 0;
12177
12239
  const sessionEntries = buildSessionEntries(
12178
12240
  this.deps.instanceManager.collectAllStates(),
12179
12241
  this.deps.cdpManagers
@@ -12181,7 +12243,7 @@ var DaemonCommandRouter = class {
12181
12243
  const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
12182
12244
  const completionMarker = targetSession ? getSessionCompletionMarker(targetSession) : "";
12183
12245
  const next = markSessionSeen(
12184
- currentConfig,
12246
+ currentState,
12185
12247
  sessionId,
12186
12248
  typeof args?.seenAt === "number" ? args.seenAt : Date.now(),
12187
12249
  completionMarker
@@ -12189,7 +12251,7 @@ var DaemonCommandRouter = class {
12189
12251
  if (READ_DEBUG_ENABLED2) {
12190
12252
  LOG.info("RecentRead", `mark_session_seen sessionId=${sessionId} seenAt=${String(args?.seenAt || "")} prevSeenAt=${String(prevSeenAt)} nextSeenAt=${String(next.sessionReads?.[sessionId] || 0)} marker=${completionMarker || "-"}`);
12191
12253
  }
12192
- saveConfig(next);
12254
+ saveState(next);
12193
12255
  this.deps.onStatusChange?.();
12194
12256
  return {
12195
12257
  success: true,
@@ -19539,6 +19601,7 @@ export {
19539
19601
  launchWithCdp,
19540
19602
  listHostedCliRuntimes,
19541
19603
  loadConfig,
19604
+ loadState,
19542
19605
  logCommand,
19543
19606
  markSetupComplete,
19544
19607
  maybeRunDaemonUpgradeHelperFromEnv,
@@ -19548,7 +19611,9 @@ export {
19548
19611
  readChatHistory,
19549
19612
  registerExtensionProviders,
19550
19613
  resetConfig,
19614
+ resetState,
19551
19615
  saveConfig,
19616
+ saveState,
19552
19617
  setLogLevel,
19553
19618
  setupIdeInstance,
19554
19619
  shutdownDaemonComponents,