@adhdev/daemon-core 0.8.4 → 0.8.6

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "ADHDev local session host core — session registry, protocol, buffers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
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",
@@ -215,6 +215,42 @@ function resolveSafePath(requestedPath: string): string {
215
215
  return resolved;
216
216
  }
217
217
 
218
+ function listDirectoryEntriesSafe(dirPath: string): Array<{ name: string; type: 'directory' | 'file'; size?: number }> {
219
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
220
+ const files: Array<{ name: string; type: 'directory' | 'file'; size?: number }> = [];
221
+
222
+ for (const entry of entries) {
223
+ const entryPath = path.join(dirPath, entry.name);
224
+ try {
225
+ if (entry.isDirectory()) {
226
+ files.push({ name: entry.name, type: 'directory' });
227
+ continue;
228
+ }
229
+ if (entry.isFile()) {
230
+ let size: number | undefined;
231
+ try {
232
+ size = fs.statSync(entryPath).size;
233
+ } catch {
234
+ size = undefined;
235
+ }
236
+ files.push({ name: entry.name, type: 'file', size });
237
+ continue;
238
+ }
239
+
240
+ const stat = fs.statSync(entryPath);
241
+ files.push({
242
+ name: entry.name,
243
+ type: stat.isDirectory() ? 'directory' : 'file',
244
+ size: stat.isFile() ? stat.size : undefined,
245
+ });
246
+ } catch {
247
+ // Skip inaccessible entries such as protected Windows root files.
248
+ }
249
+ }
250
+
251
+ return files;
252
+ }
253
+
218
254
  export async function handleFileRead(h: CommandHelpers, args: any): Promise<CommandResult> {
219
255
  try {
220
256
  const filePath = resolveSafePath(args?.path);
@@ -239,12 +275,7 @@ export async function handleFileWrite(h: CommandHelpers, args: any): Promise<Com
239
275
  export async function handleFileList(h: CommandHelpers, args: any): Promise<CommandResult> {
240
276
  try {
241
277
  const dirPath = resolveSafePath(args?.path || '.');
242
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
243
- const files = entries.map(e => ({
244
- name: e.name,
245
- type: e.isDirectory() ? 'directory' : 'file',
246
- size: e.isFile() ? fs.statSync(path.join(dirPath, e.name)).size : undefined,
247
- }));
278
+ const files = listDirectoryEntriesSafe(dirPath);
248
279
  return { success: true, files, path: dirPath };
249
280
  } catch (e: any) {
250
281
  return { success: false, error: e.message };
@@ -252,5 +283,13 @@ export async function handleFileList(h: CommandHelpers, args: any): Promise<Comm
252
283
  }
253
284
 
254
285
  export async function handleFileListBrowse(h: CommandHelpers, args: any): Promise<CommandResult> {
255
- return handleFileList(h, args);
286
+ try {
287
+ const dirPath = resolveSafePath(args?.path || '.');
288
+ const files = listDirectoryEntriesSafe(dirPath)
289
+ .filter(entry => entry.type === 'directory')
290
+ .sort((a, b) => a.name.localeCompare(b.name));
291
+ return { success: true, files, path: dirPath };
292
+ } catch (e: any) {
293
+ return { success: false, error: e.message };
294
+ }
256
295
  }
@@ -516,6 +516,8 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
516
516
  let approvalBuffer = '';
517
517
  let lastApprovalTime = 0;
518
518
  let completionSignalSeen = false;
519
+ let autoStopTimer: ReturnType<typeof setTimeout> | null = null;
520
+ let autoStopIssued = false;
519
521
 
520
522
  try {
521
523
  const { normalizeCliProviderForRuntime } = await import('../cli-adapters/provider-cli-adapter.js');
@@ -566,8 +568,40 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
566
568
  }
567
569
  };
568
570
 
571
+ const clearAutoStopTimer = () => {
572
+ if (autoStopTimer) {
573
+ clearTimeout(autoStopTimer);
574
+ autoStopTimer = null;
575
+ }
576
+ };
577
+
578
+ const scheduleAutoStopForVerification = () => {
579
+ if (!verification || command !== 'codex' || completionSignalSeen || autoStopIssued) return;
580
+ const elapsed = Date.now() - spawnedAt;
581
+ if (elapsed < 30000) return;
582
+ clearAutoStopTimer();
583
+ autoStopTimer = setTimeout(() => {
584
+ if (!ctx.autoImplProcess || completionSignalSeen || autoStopIssued) return;
585
+ autoStopIssued = true;
586
+ ctx.log(`Auto-implement output quiet for 30s after ${Math.round((Date.now() - spawnedAt) / 1000)}s. Interrupting agent and switching to daemon verification.`);
587
+ sendAutoImplSSE(ctx, {
588
+ event: 'output',
589
+ data: {
590
+ chunk: '\n[🤖 ADHDev Pipeline] Agent output quiet. Interrupting and running daemon verification...\n',
591
+ stream: 'stdout',
592
+ },
593
+ });
594
+ try {
595
+ (ctx.autoImplProcess as any).kill('SIGINT');
596
+ } catch {
597
+ // ignore
598
+ }
599
+ }, 30000);
600
+ };
601
+
569
602
  const finalizeCliAutoImpl = async (code: number | null) => {
570
603
  ctx.autoImplProcess = null;
604
+ clearAutoStopTimer();
571
605
  let success = completionSignalSeen || code === 0;
572
606
  let message = success
573
607
  ? (completionSignalSeen && code !== 0 ? '✅ Auto-implement complete (completion signal)' : '✅ Auto-implement complete')
@@ -620,12 +654,14 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
620
654
  if (isPty) {
621
655
  child.onData((data: string) => {
622
656
  stdout += data;
657
+ clearAutoStopTimer();
623
658
  if (data.includes('\x1b[6n')) {
624
659
  child.write('\x1b[12;1R');
625
660
  ctx.log('Terminal CPR request (\\x1b[6n) intercepted in PTY, responding with dummy coordinates [12;1R]');
626
661
  }
627
662
  checkAutoApproval(data, (s) => child.write(s));
628
663
  sendAutoImplSSE(ctx, { event: 'output', data: { chunk: data, stream: 'stdout' } });
664
+ scheduleAutoStopForVerification();
629
665
  });
630
666
  child.onExit(({ exitCode: code }: { exitCode: number }) => {
631
667
  void finalizeCliAutoImpl(code);
@@ -634,15 +670,19 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
634
670
  child.stdout?.on('data', (d: Buffer) => {
635
671
  const chunk = d.toString();
636
672
  stdout += chunk;
673
+ clearAutoStopTimer();
637
674
  if (chunk.includes('\x1b[6n')) child.stdin?.write('\x1b[1;1R');
638
675
  checkAutoApproval(chunk, (s) => child.stdin?.write(s));
639
676
  sendAutoImplSSE(ctx, { event: 'output', data: { chunk, stream: 'stdout' } });
677
+ scheduleAutoStopForVerification();
640
678
  });
641
679
  child.stderr?.on('data', (d: Buffer) => {
642
680
  const chunk = d.toString();
643
681
  stderr += chunk;
682
+ clearAutoStopTimer();
644
683
  checkAutoApproval(chunk, (s) => child.stdin?.write(s));
645
684
  sendAutoImplSSE(ctx, { event: 'output', data: { chunk, stream: 'stderr' } });
685
+ scheduleAutoStopForVerification();
646
686
  });
647
687
  child.on('exit', (code: number) => {
648
688
  void finalizeCliAutoImpl(code);