@adhdev/daemon-core 0.5.35 → 0.5.37

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.5.35",
3
+ "version": "0.5.37",
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",
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "scripts": {
14
14
  "build": "tsup",
15
- "dev": "tsup --watch"
15
+ "dev": "tsup --watch",
16
+ "typecheck": "tsc --noEmit -p tsconfig.json"
16
17
  },
17
18
  "files": [
18
19
  "dist",
@@ -689,8 +689,14 @@ export class DaemonCdpManager {
689
689
 
690
690
  const value = result?.result?.value;
691
691
  if (value != null) {
692
+ const strValue = typeof value === 'string' ? value : JSON.stringify(value);
693
+ // Let provider script explicitly tell us to skip this iframe and try the next one
694
+ if (strValue.includes('__adhdev_skip_iframe')) {
695
+ this.log(`[CDP] evaluateInWebviewFrame: script requested skip in ${iframe.targetId.substring(0, 12)}`);
696
+ continue;
697
+ }
692
698
  this.log(`[CDP] evaluateInWebviewFrame: success in ${iframe.targetId.substring(0, 12)}`);
693
- return typeof value === 'string' ? value : JSON.stringify(value);
699
+ return strValue;
694
700
  }
695
701
  } catch (e: any) {
696
702
  if (sessionId) {
@@ -58,6 +58,9 @@ export interface ADHDevConfig {
58
58
  // Machine nickname (user-customizable label for this machine)
59
59
  machineNickname: string | null;
60
60
 
61
+ // Stable machine ID (prevents duplicate daemon entries when OS hostname changes dynamically)
62
+ machineId?: string;
63
+
61
64
  // CLI launch history
62
65
  cliHistory: CliHistoryEntry[];
63
66
 
@@ -98,6 +101,7 @@ const DEFAULT_CONFIG: ADHDevConfig = {
98
101
  defaultWorkspaceId: null,
99
102
  recentWorkspaceActivity: [],
100
103
  machineNickname: null,
104
+ machineId: undefined,
101
105
  cliHistory: [],
102
106
  providerSettings: {},
103
107
  ideSettings: {},
@@ -141,7 +145,22 @@ export function loadConfig(): ADHDevConfig {
141
145
  delete (merged as any).activeWorkspaceId;
142
146
  const hadStoredWorkspaces = Array.isArray(parsed.workspaces) && parsed.workspaces.length > 0;
143
147
  migrateWorkspacesFromRecent(merged);
148
+
149
+ let configChanged = false;
150
+ if (!merged.machineId) {
151
+ const os = require('os');
152
+ const crypto = require('crypto');
153
+ const safeHostname = os.hostname().replace(/[^a-zA-Z0-9]/g, '_');
154
+ const machineHash = crypto.createHash('md5').update(os.hostname() + os.homedir()).digest('hex').slice(0, 8);
155
+ merged.machineId = `${safeHostname}_${machineHash}`;
156
+ configChanged = true;
157
+ }
158
+
144
159
  if (!hadStoredWorkspaces && (merged.workspaces?.length || 0) > 0) {
160
+ configChanged = true;
161
+ }
162
+
163
+ if (configChanged) {
145
164
  try {
146
165
  saveConfig(merged);
147
166
  } catch { /* ignore */ }
@@ -337,13 +337,17 @@ export class DevServer {
337
337
  this.json(res, 500, { error: 'Script function returned null' });
338
338
  return;
339
339
  }
340
+ this.log(`Exec script length: ${scriptCode.length}, first 50 chars: ${scriptCode.slice(0, 50)}...`);
340
341
 
341
342
  // Execute webview script via evaluateInWebviewFrame
342
- const isWebviewScript = scriptName.toLowerCase().includes('webview');
343
+ const isWebviewScript = provider.category === 'extension' || scriptName.toLowerCase().includes('webview');
343
344
  let raw: any;
344
345
  if (isWebviewScript) {
345
346
  const matchText = provider.webviewMatchText;
346
347
  const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
348
+ if (!cdp.evaluateInWebviewFrame) {
349
+ throw new Error(`CDP manager does not support evaluateInWebviewFrame`);
350
+ }
347
351
  raw = await cdp.evaluateInWebviewFrame(scriptCode, matchFn);
348
352
  } else {
349
353
  raw = await cdp.evaluate(scriptCode, 30000);
@@ -87,6 +87,7 @@ export class ExtensionProviderInstance implements ProviderInstance {
87
87
  // Reflect data collected from agent-stream-manager
88
88
  if (data?.streams) this.agentStreams = data.streams;
89
89
  if (data?.messages) this.messages = data.messages;
90
+ if (data?.activeModal !== undefined) this.activeModal = data.activeModal;
90
91
  if (data?.status) {
91
92
  const newStatus = data.status;
92
93
  this.detectTransition(newStatus, data);
@@ -125,11 +126,12 @@ export class ExtensionProviderInstance implements ProviderInstance {
125
126
  this.pushEvent({ event: 'agent:generating_started', chatTitle, timestamp: now });
126
127
  } else if (agentStatus === 'waiting_approval') {
127
128
  if (!this.generatingStartedAt) this.generatingStartedAt = now;
129
+ const msg = data?.activeModal?.message || data?.modalMessage;
128
130
  this.pushEvent({
129
131
  event: 'agent:waiting_approval', chatTitle, timestamp: now,
130
132
  ideType: this.ideType,
131
133
  agentType: this.type,
132
- modalMessage: data?.activeModal?.message || data?.modalMessage,
134
+ modalMessage: msg,
133
135
  modalButtons: data?.activeModal?.buttons || data?.modalButtons,
134
136
  });
135
137
  } else if (agentStatus === 'idle' && (this.lastAgentStatus === 'generating' || this.lastAgentStatus === 'waiting_approval')) {
@@ -253,8 +253,8 @@ export class IdeProviderInstance implements ProviderInstance {
253
253
  activeModal = undefined;
254
254
  } else {
255
255
  activeModal = {
256
- message: activeModal.message?.slice(0, 300) ?? '',
257
- buttons: (activeModal.buttons ?? []).filter((t: string) => t.length < 30),
256
+ message: activeModal.message?.slice(0, 5000) ?? '',
257
+ buttons: (activeModal.buttons ?? []).filter((t: string) => t.length < 200),
258
258
  };
259
259
  }
260
260
  }
@@ -345,9 +345,10 @@ export class IdeProviderInstance implements ProviderInstance {
345
345
  this.pushEvent({ event: 'agent:generating_started', chatTitle, timestamp: now, ideType: this.type });
346
346
  } else if (agentStatus === 'waiting_approval') {
347
347
  if (!this.generatingStartedAt.has(agentKey)) this.generatingStartedAt.set(agentKey, now);
348
+ const msg = chatData.activeModal?.message;
348
349
  this.pushEvent({
349
350
  event: 'agent:waiting_approval', chatTitle, timestamp: now, ideType: this.type,
350
- modalMessage: chatData.activeModal?.message,
351
+ modalMessage: msg,
351
352
  modalButtons: chatData.activeModal?.buttons,
352
353
  });
353
354
  } else if (agentStatus === 'idle' && (lastStatus === 'generating' || lastStatus === 'waiting_approval')) {
@@ -904,8 +904,32 @@ export class ProviderLoader {
904
904
  if (!file.endsWith('.js')) continue;
905
905
  const scriptName = toCamel(file.replace('.js', ''));
906
906
  const filePath = path.join(dir, file);
907
- (result as any)[scriptName] = (..._args: any[]): string => {
908
- try { return fs.readFileSync(filePath, 'utf-8'); } catch { return ''; }
907
+ (result as any)[scriptName] = (...args: any[]): string => {
908
+ try {
909
+ let content = fs.readFileSync(filePath, 'utf-8');
910
+ if (args[0] && typeof args[0] === 'object') {
911
+ for (const [key, val] of Object.entries(args[0])) {
912
+ let v = val;
913
+ if (typeof v === 'string') {
914
+ // If it doesn't start with a quote, user probably passed raw text
915
+ if (!v.startsWith('"') && !v.startsWith("'") && !v.startsWith('`')) {
916
+ v = JSON.stringify(v);
917
+ }
918
+ } else {
919
+ v = JSON.stringify(v);
920
+ }
921
+ content = content.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), String(v));
922
+ }
923
+ } else if (args[0] !== undefined) {
924
+ // legacy fallback for single argument usually MESSAGE
925
+ let v = String(args[0]);
926
+ if (!v.startsWith('"') && !v.startsWith("'") && !v.startsWith('`')) {
927
+ v = JSON.stringify(v);
928
+ }
929
+ content = content.replace(/\$\{MESSAGE\}/g, v);
930
+ }
931
+ return content;
932
+ } catch { return ''; }
909
933
  };
910
934
  }
911
935
  } catch { /* ignore */ }
@@ -25,7 +25,7 @@ import type {
25
25
  export interface StatusReporterDeps {
26
26
  serverConn: { isConnected(): boolean; sendMessage(type: string, data: any): void; getUserPlan(): string } | null;
27
27
  cdpManagers: Map<string, { isConnected: boolean }>;
28
- p2p: { isConnected: boolean; isAvailable: boolean; connectionState: string; connectedPeerCount: number; screenshotActive: boolean; sendStatus(data: any): void } | null;
28
+ p2p: { isConnected: boolean; isAvailable: boolean; connectionState: string; connectedPeerCount: number; screenshotActive: boolean; sendStatus(data: any): void; sendStatusEvent?(event: Record<string, unknown>): boolean } | null;
29
29
  providerLoader: { resolve(type: string): any; getAll(): any[] };
30
30
  adapters: Map<string, { cliType: string; cliName: string; workingDir: string; getStatus(): any; getPartialResponse(): string }>;
31
31
  detectedIdes: any[];
@@ -98,7 +98,15 @@ export class DaemonStatusReporter {
98
98
 
99
99
  emitStatusEvent(event: Record<string, unknown>): void {
100
100
  LOG.info('StatusEvent', `${event.event} (${event.providerType || event.ideType || ''})`);
101
+ // Send via WS (server relay → dashboard + push notifications)
101
102
  this.deps.serverConn?.sendMessage('status_event', event);
103
+ // Also send via P2P (direct → dashboard, works even when WS is flaky)
104
+ // Frontend dedup prevents duplicate toasts
105
+ if (this.deps.p2p?.isConnected) {
106
+ try {
107
+ this.deps.p2p.sendStatusEvent?.(event);
108
+ } catch { /* P2P send failure is non-critical */ }
109
+ }
102
110
  }
103
111
 
104
112
  removeAgentTracking(_key: string): void { /* Managed by Instance itself */ }