@adhdev/daemon-core 0.6.67 → 0.6.68

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.6.67",
3
+ "version": "0.6.68",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * AgentStreamPoller — Periodic agent stream polling + extension dynamic management
3
3
  *
4
- * Extracted from cloud daemon's startAgentStreamPolling() logic.
5
- * Used by both daemon-cloud and daemon-standalone.
4
+ * Handles periodic agent stream polling and extension dynamic management.
6
5
  *
7
6
  * Responsibilities:
8
7
  * 1. Refresh extension providers in CDP managers (config changes take effect immediately)
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Daemon Lifecycle — Shared init + shutdown for cloud/standalone
2
+ * Daemon Lifecycle — Shared init + shutdown logic
3
3
  *
4
4
  * initDaemonComponents(): Creates all core daemon components in correct order.
5
5
  * shutdownDaemonComponents(): Graceful shutdown of all components.
@@ -77,7 +77,6 @@ export interface DaemonComponents {
77
77
 
78
78
  /**
79
79
  * Initialize all daemon core components.
80
- * Shared by both cloud and standalone daemons.
81
80
  *
82
81
  * Order:
83
82
  * 1. Global log interceptor
@@ -99,6 +98,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
99
98
  const providerLoader = new ProviderLoader({
100
99
  logFn: config.providerLogFn,
101
100
  disableUpstream,
101
+ userDir: appConfig.providerDir,
102
102
  });
103
103
 
104
104
  // If no upstream providers exist, fetch them first (blocking — critical for new users)
@@ -244,7 +244,6 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
244
244
 
245
245
  /**
246
246
  * Graceful shutdown of all daemon components.
247
- * Shared by both cloud and standalone daemons.
248
247
  *
249
248
  * Order:
250
249
  * 1. Stop timers (poller, cdpInitializer)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * DaemonCdpInitializer — Unified CDP initialization + periodic scanning
3
3
  *
4
- * Replaces initCdp() in both cloud and standalone daemons.
4
+ * Unified CDP initialization + periodic scanning.
5
5
  *
6
6
  * Features:
7
7
  * 1. Initial connection: connectAll() — multi-window aware
@@ -90,6 +90,8 @@ export class DaemonCdpInitializer {
90
90
  const targets = await DaemonCdpManager.listAllTargets(port);
91
91
 
92
92
  if (targets.length === 0) {
93
+ // Prevent duplicate fallback connection
94
+ if (cdpManagers.has(ide)) return;
93
95
  // Fallback: direct single connection (probeCdpPort first)
94
96
  if (!await probeCdpPort(port)) return;
95
97
  const provider = providerLoader.getMeta(ide);
@@ -112,13 +114,23 @@ export class DaemonCdpInitializer {
112
114
  // 2. Multi-window: create separate CdpManager per page
113
115
  for (let i = 0; i < targets.length; i++) {
114
116
  const target = targets[i];
115
- // Key: single page = ide type as-is, multi-page = ide_workspaceName
117
+
118
+ // Check if ANY existing manager for this IDE is already tracking this target.id
119
+ let alreadyTracked = false;
120
+ for (const [key, m] of cdpManagers.entries()) {
121
+ if ((key === ide || key.startsWith(`${ide}_`)) && m.targetId === target.id) {
122
+ alreadyTracked = true;
123
+ break;
124
+ }
125
+ }
126
+ if (alreadyTracked) continue;
127
+
128
+ // Stable key using target.id instead of fluctuating window title
116
129
  let managerKey: string;
117
- if (targets.length === 1) {
130
+ if (targets.length === 1 && !cdpManagers.has(ide)) {
118
131
  managerKey = ide;
119
132
  } else {
120
- const workspaceName = (target.title || '').split(' — ')[0].trim() || `window_${i}`;
121
- managerKey = `${ide}_${workspaceName}`;
133
+ managerKey = `${ide}_${target.id}`;
122
134
  }
123
135
 
124
136
  if (cdpManagers.has(managerKey)) continue;
@@ -156,13 +168,7 @@ export class DaemonCdpInitializer {
156
168
 
157
169
  for (const [ide, ports] of Object.entries(portMap)) {
158
170
  const primaryPort = ports[0];
159
-
160
- // Skip if already connected
161
- const alreadyConnected = [...cdpManagers.entries()].some(([key, m]) =>
162
- m.isConnected && (key === ide || key.startsWith(ide + '_'))
163
- );
164
- if (alreadyConnected) continue;
165
-
171
+ // Always try to connect to find new windows
166
172
  await this.connectIdePort(primaryPort, ide);
167
173
  }
168
174
  }, intervalMs);
@@ -107,6 +107,9 @@ export class DaemonCdpManager {
107
107
  /** Connected page title (includes workspace name) */
108
108
  get pageTitle(): string { return this._pageTitle; }
109
109
 
110
+ /** Connected target ID */
111
+ get targetId(): string | null { return this._targetId; }
112
+
110
113
  /**
111
114
  * Query all workbench pages on port (static)
112
115
  * Returns multiple entries if multiple IDE windows are open on same port
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * DaemonCdpScanner — Periodic CDP port scanning & auto-connect
3
3
  *
4
- * Extracted from daemon-cloud initCdp() and daemon-standalone initCdp().
4
+ * Periodic CDP port scanning and auto-connect for IDE discovery.
5
5
  * Provides a unified approach to:
6
6
  * 1. Initial CDP port discovery
7
7
  * 2. Periodic scanning for newly launched IDEs
@@ -142,7 +142,7 @@ export class DaemonCdpScanner {
142
142
  }
143
143
 
144
144
  /**
145
- * Multi-window connection (cloud mode).
145
+ * Multi-window connection.
146
146
  * Multiple CDP managers per IDE — one per workbench page.
147
147
  */
148
148
  private async connectMultiWindow(port: number, ide: string): Promise<void> {
package/src/cdp/setup.ts CHANGED
@@ -1,11 +1,8 @@
1
1
  /**
2
2
  * DaemonCdpSetup — Shared CDP initialization helpers
3
3
  *
4
- * Extracted from daemon-cloud/adhdev-daemon.ts setupCdpManager()
5
- * and daemon-standalone/index.ts inline CDP setup logic.
6
- *
7
- * Used by both daemon-cloud and daemon-standalone to ensure
8
- * consistent CDP → ProviderInstance registration.
4
+ * Common CDP setup logic for consistent
5
+ * CDP ProviderInstance registration.
9
6
  */
10
7
 
11
8
  import { DaemonCdpManager } from './manager.js';
@@ -20,7 +17,7 @@ export interface CdpSetupContext {
20
17
  cdpManagers: Map<string, DaemonCdpManager>;
21
18
  /** UUID instanceId → CDP manager key mapping */
22
19
  instanceIdMap: Map<string, string>;
23
- /** Server connection (optional, cloud-only) */
20
+ /** Server connection (optional) */
24
21
  serverConn?: any;
25
22
  }
26
23
 
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * DaemonCommandRouter — Unified command routing for daemon-level commands
3
3
  *
4
- * Extracted from cloud daemon's executeDaemonCommand() logic.
5
- * Used by both daemon-cloud and daemon-standalone.
4
+ * Unified command routing for daemon-level commands.
6
5
  *
7
6
  * Routing flow:
8
7
  * 1. Daemon-level commands (launch_ide, stop_ide, restart_ide, etc.) → handled here
@@ -39,13 +38,13 @@ export interface CommandRouterDeps {
39
38
  detectedIdes: { value: any[] };
40
39
  /** UUID instanceId → CDP manager key mapping */
41
40
  instanceIdMap: Map<string, string>;
42
- /** Callback for CDP manager creation after launch_ide (cloud: setupCdpManager, standalone: no-op) */
41
+ /** Callback for CDP manager creation after launch_ide */
43
42
  onCdpManagerCreated?: (ideType: string, manager: DaemonCdpManager) => void;
44
43
  /** Callback after IDE connected (e.g., startAgentStreamPolling) */
45
44
  onIdeConnected?: () => void;
46
45
  /** Callback after status change (stop_ide, restart) */
47
46
  onStatusChange?: () => void;
48
- /** Callback after chat-related commands (cloud: throttled report) */
47
+ /** Callback after chat-related commands */
49
48
  onPostChatCommand?: () => void;
50
49
  /** Get a connected CDP manager (for agent stream reset check) */
51
50
  getCdpLogFn?: (ideType: string) => (msg: string) => void;
@@ -234,7 +233,7 @@ export class DaemonCommandRouter {
234
233
  LOG.info('CDP', `Connected: ${result.ideId} (port ${result.port})`);
235
234
  LOG.info('CDP', `${this.deps.cdpManagers.size} IDE(s) connected`);
236
235
 
237
- // Notify consumer (cloud: setupCdpManager, standalone: setupIdeInstance)
236
+ // Notify consumer (e.g. setupIdeInstance)
238
237
  this.deps.onCdpManagerCreated?.(result.ideId, manager);
239
238
  }
240
239
  }
@@ -271,7 +270,7 @@ export class DaemonCommandRouter {
271
270
  try {
272
271
  const { execSync } = await import('child_process');
273
272
 
274
- // Detect package: standalone uses @adhdev/daemon-standalone, cloud uses adhdev
273
+ // Detect package name for upgrade
275
274
  const isStandalone = this.deps.packageName === '@adhdev/daemon-standalone'
276
275
  || process.argv[1]?.includes('daemon-standalone');
277
276
  const pkgName = isStandalone ? '@adhdev/daemon-standalone' : 'adhdev';
@@ -84,6 +84,9 @@ export interface ADHDevConfig {
84
84
  // Disable upstream provider auto-download (use builtin only)
85
85
  // Controllable from CLI (--no-upstream) and dashboard (machine page)
86
86
  disableUpstream?: boolean;
87
+
88
+ // Optional custom provider directory for local development
89
+ providerDir?: string;
87
90
  }
88
91
 
89
92
  export interface CliHistoryEntry {
@@ -2405,6 +2405,34 @@ export class DevServer {
2405
2405
  }
2406
2406
  }
2407
2407
 
2408
+ // ── Markdown Guides (Provider Fix) ──
2409
+ const docsDir = path.join(providerDir, '../../docs');
2410
+ const loadGuide = (name: string) => {
2411
+ try {
2412
+ const p = path.join(docsDir, name);
2413
+ if (fs.existsSync(p)) return fs.readFileSync(p, 'utf-8');
2414
+ } catch { /* ignore */ }
2415
+ return null;
2416
+ };
2417
+
2418
+ const providerGuide = loadGuide('PROVIDER_GUIDE.md');
2419
+ if (providerGuide) {
2420
+ lines.push('## Documentation: PROVIDER_GUIDE.md');
2421
+ lines.push('```markdown');
2422
+ lines.push(providerGuide);
2423
+ lines.push('```');
2424
+ lines.push('');
2425
+ }
2426
+
2427
+ const cdpGuide = loadGuide('CDP_SELECTOR_GUIDE.md');
2428
+ if (cdpGuide) {
2429
+ lines.push('## Documentation: CDP_SELECTOR_GUIDE.md');
2430
+ lines.push('```markdown');
2431
+ lines.push(cdpGuide);
2432
+ lines.push('```');
2433
+ lines.push('');
2434
+ }
2435
+
2408
2436
  // ── Task ──
2409
2437
  lines.push('## Task');
2410
2438
  lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(', ')}**`);
@@ -2629,6 +2657,25 @@ export class DevServer {
2629
2657
  }
2630
2658
  }
2631
2659
 
2660
+ // ── Markdown Guides (Provider Fix) ──
2661
+ const docsDir = path.join(providerDir, '../../docs');
2662
+ const loadGuide = (name: string) => {
2663
+ try {
2664
+ const p = path.join(docsDir, name);
2665
+ if (fs.existsSync(p)) return fs.readFileSync(p, 'utf-8');
2666
+ } catch { /* ignore */ }
2667
+ return null;
2668
+ };
2669
+
2670
+ const providerGuide = loadGuide('PROVIDER_GUIDE.md');
2671
+ if (providerGuide) {
2672
+ lines.push('## Documentation: PROVIDER_GUIDE.md');
2673
+ lines.push('```markdown');
2674
+ lines.push(providerGuide);
2675
+ lines.push('```');
2676
+ lines.push('');
2677
+ }
2678
+
2632
2679
  lines.push('## Runtime Contract');
2633
2680
  lines.push('The daemon runtime is already implemented in `packages/daemon-core/src/cli-adapters/provider-cli-adapter.ts`.');
2634
2681
  lines.push('Your scripts receive PTY-derived input and must return plain JS objects.');
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * DaemonCore — Core daemon orchestrator interface
3
3
  *
4
- * Both daemon-standalone and daemon-cloud use this interface via daemon-core.
4
+ * Provides the core daemon orchestrator interface consumed by daemon-standalone.
5
5
  * Actual implementation extracted from launcher and placed in this package.
6
6
  */
7
7
 
package/src/index.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  * @adhdev/daemon-core — Public API
3
3
  *
4
4
  * Core logic for daemon: CDP, Provider, IDE detection, CLI/ACP adapters and more.
5
- * Used by both daemon-standalone and daemon-cloud.
6
5
  */
7
6
 
8
7
  // ── Types ──
@@ -59,11 +59,14 @@ export class ProviderLoader {
59
59
  } else {
60
60
  this.builtinDirs = [];
61
61
  }
62
- // User custom directory: ~/.adhdev/providers/
63
- this.userDir = options?.userDir ||
64
- path.join(os.homedir(), '.adhdev', 'providers');
65
- // Upstream auto-download directory: ~/.adhdev/providers/.upstream/
66
- this.upstreamDir = path.join(this.userDir, '.upstream');
62
+ // Default directory for auto-downloads
63
+ const defaultProvidersDir = path.join(os.homedir(), '.adhdev', 'providers');
64
+
65
+ // User custom directory: ~/.adhdev/providers/ or custom via config
66
+ this.userDir = options?.userDir || defaultProvidersDir;
67
+
68
+ // Upstream auto-download directory is always in the default location to avoid polluting custom dirs
69
+ this.upstreamDir = path.join(defaultProvidersDir, '.upstream');
67
70
  this.disableUpstream = options?.disableUpstream ?? false;
68
71
  this.logFn = options?.logFn || LOG.forComponent('Provider').asLogFn();
69
72
  }
@@ -301,7 +304,8 @@ export class ProviderLoader {
301
304
  try {
302
305
  const { loadConfig } = require('../config/config.js');
303
306
  const config = loadConfig();
304
- const val = config.ideSettings?.[ideType]?.extensions?.[type]?.enabled;
307
+ const baseIdeType = ideType.split('_')[0];
308
+ const val = config.ideSettings?.[baseIdeType]?.extensions?.[type]?.enabled;
305
309
  return val === true; // undefined → false (default inactive)
306
310
  } catch {
307
311
  return false;
@@ -315,10 +319,11 @@ export class ProviderLoader {
315
319
  try {
316
320
  const { loadConfig, saveConfig } = require('../config/config.js');
317
321
  const config = loadConfig();
322
+ const baseIdeType = ideType.split('_')[0];
318
323
  if (!config.ideSettings) config.ideSettings = {};
319
- if (!config.ideSettings[ideType]) config.ideSettings[ideType] = {};
320
- if (!config.ideSettings[ideType].extensions) config.ideSettings[ideType].extensions = {};
321
- config.ideSettings[ideType].extensions[extensionType] = { enabled };
324
+ if (!config.ideSettings[baseIdeType]) config.ideSettings[baseIdeType] = {};
325
+ if (!config.ideSettings[baseIdeType].extensions) config.ideSettings[baseIdeType].extensions = {};
326
+ config.ideSettings[baseIdeType].extensions[extensionType] = { enabled };
322
327
  saveConfig(config);
323
328
  this.log(`IDE extension setting: ${ideType}.${extensionType}.enabled = ${enabled}`);
324
329
  return true;
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * ADHDev Shared Types — Cross-package type definitions
3
3
  *
4
- * Types used across daemon-core, web-core, web-cloud, server, etc.
4
+ * Types used across daemon-core, web-core, and downstream consumers.
5
5
  * Import via: import type { ... } from '@adhdev/daemon-core/types'
6
6
  *
7
7
  * IMPORTANT: This file must remain runtime-free (types only).
8
- * Cloudflare Workers (server) can import type-only modules safely.
9
8
  */
10
9
 
11
10
  import type {
@@ -50,9 +49,9 @@ export type { WorkspaceEntry } from './config/workspaces.js';
50
49
 
51
50
  // ─── Managed Entry Types (reporter → server/web) ────────────────────
52
51
  // These define the shape of data sent by DaemonStatusReporter
53
- // and consumed by web-core, web-cloud, and server.
52
+ // and consumed by web-core and downstream consumers.
54
53
 
55
- /** IDE entry as reported by daemon to server/dashboard */
54
+ /** IDE entry as reported by daemon to dashboard */
56
55
  export interface ManagedIdeEntry {
57
56
  ideType: string;
58
57
  ideVersion: string;
@@ -69,7 +68,7 @@ export interface ManagedIdeEntry {
69
68
  currentAutoApprove?: string;
70
69
  }
71
70
 
72
- /** CLI entry as reported by daemon to server/dashboard */
71
+ /** CLI entry as reported by daemon to dashboard */
73
72
  export interface ManagedCliEntry {
74
73
  id: string;
75
74
  instanceId: string;
@@ -81,7 +80,7 @@ export interface ManagedCliEntry {
81
80
  activeChat: _ActiveChatData | null;
82
81
  }
83
82
 
84
- /** ACP entry as reported by daemon to server/dashboard */
83
+ /** ACP entry as reported by daemon to dashboard */
85
84
  export interface ManagedAcpEntry {
86
85
  id: string;
87
86
  acpType: string;
@@ -2,11 +2,10 @@
2
2
  * Status Builders — shared conversion functions for ProviderState → ManagedEntry
3
3
  *
4
4
  * Used by:
5
- * - daemon-cloud (DaemonStatusReporter)
6
5
  * - daemon-standalone (StandaloneServer.getStatus)
6
+ * - DaemonStatusReporter
7
7
  *
8
- * Consolidates duplicated ProviderState→ManagedEntry mapping that was
9
- * previously copy-pasted between cloud and standalone codebases.
8
+ * Consolidates ProviderState→ManagedEntry mapping logic.
10
9
  */
11
10
 
12
11
  import type { DaemonCdpManager } from '../cdp/manager.js';
package/src/types.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * ADHDev Daemon Core — Shared Types
3
3
  *
4
- * Shared types referenced by daemon-core, daemon-standalone, daemon-cloud, web-core.
4
+ * Shared types referenced by daemon-core, daemon-standalone, and web-core.
5
5
  * When modifying this file, also update interface contracts in AGENT_PROTOCOL.md.
6
6
  */
7
7
  import type { StatusReportPayload } from './shared-types.js';