@siftd/connect-agent 0.2.23 → 0.2.25

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/heartbeat.js CHANGED
@@ -8,9 +8,26 @@
8
8
  */
9
9
  import { hostname } from 'os';
10
10
  import { createHash } from 'crypto';
11
+ import { readFileSync } from 'fs';
12
+ import { fileURLToPath } from 'url';
13
+ import { dirname, join } from 'path';
11
14
  import { getServerUrl, getAgentToken, getUserId, isCloudMode } from './config.js';
12
15
  const HEARTBEAT_INTERVAL = 10000; // 10 seconds
13
- const VERSION = '0.2.20'; // Should match package.json
16
+ // Read version from package.json dynamically
17
+ function getVersion() {
18
+ try {
19
+ // Try to find package.json relative to this file
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+ const pkgPath = join(__dirname, '..', 'package.json');
23
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
24
+ return pkg.version || '0.0.0';
25
+ }
26
+ catch {
27
+ return '0.0.0';
28
+ }
29
+ }
30
+ const VERSION = getVersion();
14
31
  const state = {
15
32
  intervalId: null,
16
33
  runnerId: null,
@@ -174,12 +174,19 @@ export class MasterOrchestrator {
174
174
  */
175
175
  setGalleryCallback(callback) {
176
176
  this.galleryCallback = callback;
177
+ // Also set on workerTools for spawn_worker jobs
178
+ this.workerTools.setGalleryCallback(callback ? (workers) => {
179
+ // Merge with delegate_to_worker jobs and broadcast
180
+ this.broadcastGalleryUpdate();
181
+ } : null);
177
182
  }
178
183
  /**
179
184
  * Get gallery workers with their assets
180
185
  */
181
186
  getGalleryWorkers() {
182
187
  const workers = [];
188
+ const seenIds = new Set();
189
+ // Include jobs from delegateToWorker (this.jobs)
183
190
  for (const [id, job] of this.jobs) {
184
191
  // Only include jobs with assets or that are running
185
192
  if (job.assets || job.status === 'running') {
@@ -189,8 +196,21 @@ export class MasterOrchestrator {
189
196
  status: job.status === 'timeout' ? 'failed' : job.status,
190
197
  assets: job.assets || []
191
198
  });
199
+ seenIds.add(id);
200
+ }
201
+ }
202
+ // Include jobs from workerTools (spawn_worker)
203
+ try {
204
+ const spawnedWorkers = this.workerTools.getGalleryWorkers();
205
+ for (const worker of spawnedWorkers) {
206
+ if (!seenIds.has(worker.id)) {
207
+ workers.push(worker);
208
+ }
192
209
  }
193
210
  }
211
+ catch {
212
+ // WorkerTools not available
213
+ }
194
214
  return workers;
195
215
  }
196
216
  /**
@@ -2,10 +2,20 @@
2
2
  * Worker Tools
3
3
  * Tools for spawning and managing Claude Code workers
4
4
  */
5
+ import { GalleryCallback, GalleryWorker } from '../workers/manager.js';
5
6
  import type { ToolResult } from './bash.js';
7
+ export { GalleryCallback, GalleryWorker };
6
8
  export declare class WorkerTools {
7
9
  private manager;
8
10
  constructor(workspaceDir: string);
11
+ /**
12
+ * Set callback for gallery updates (worker assets for UI)
13
+ */
14
+ setGalleryCallback(callback: GalleryCallback | null): void;
15
+ /**
16
+ * Get gallery workers with assets
17
+ */
18
+ getGalleryWorkers(): GalleryWorker[];
9
19
  /**
10
20
  * Spawn a new Claude Code worker
11
21
  */
@@ -8,6 +8,18 @@ export class WorkerTools {
8
8
  constructor(workspaceDir) {
9
9
  this.manager = new WorkerManager(workspaceDir);
10
10
  }
11
+ /**
12
+ * Set callback for gallery updates (worker assets for UI)
13
+ */
14
+ setGalleryCallback(callback) {
15
+ this.manager.setGalleryCallback(callback);
16
+ }
17
+ /**
18
+ * Get gallery workers with assets
19
+ */
20
+ getGalleryWorkers() {
21
+ return this.manager.getGalleryWorkers();
22
+ }
11
23
  /**
12
24
  * Spawn a new Claude Code worker
13
25
  */
@@ -2,11 +2,32 @@
2
2
  * Claude Code Worker Manager
3
3
  * Spawns and manages Claude Code CLI instances for parallel task execution
4
4
  */
5
- import { WorkerJob, SpawnOptions, WorkerConfig } from './types.js';
5
+ import { WorkerJob, SpawnOptions, WorkerConfig, WorkerAsset } from './types.js';
6
+ export interface GalleryWorker {
7
+ id: string;
8
+ task: string;
9
+ status: 'running' | 'completed' | 'failed';
10
+ assets: WorkerAsset[];
11
+ }
12
+ export type GalleryCallback = (workers: GalleryWorker[]) => void;
6
13
  export declare class WorkerManager {
7
14
  private config;
8
15
  private activeWorkers;
16
+ private fileSnapshots;
17
+ private galleryCallback;
9
18
  constructor(workspaceDir: string, configOverrides?: Partial<WorkerConfig>);
19
+ /**
20
+ * Set callback for gallery updates (worker assets for UI)
21
+ */
22
+ setGalleryCallback(callback: GalleryCallback | null): void;
23
+ /**
24
+ * Get gallery workers with assets for UI
25
+ */
26
+ getGalleryWorkers(): GalleryWorker[];
27
+ /**
28
+ * Broadcast gallery update
29
+ */
30
+ private broadcastGalleryUpdate;
10
31
  /**
11
32
  * Generate a unique job ID
12
33
  */
@@ -6,9 +6,12 @@ import { spawn } from 'child_process';
6
6
  import * as fs from 'fs';
7
7
  import * as path from 'path';
8
8
  import { DEFAULT_WORKER_CONFIG } from './types.js';
9
+ import { takeSnapshot, compareSnapshots } from '../core/file-tracker.js';
9
10
  export class WorkerManager {
10
11
  config;
11
12
  activeWorkers = new Map();
13
+ fileSnapshots = new Map(); // Before-snapshots per job
14
+ galleryCallback = null;
12
15
  constructor(workspaceDir, configOverrides) {
13
16
  this.config = {
14
17
  ...DEFAULT_WORKER_CONFIG,
@@ -20,6 +23,39 @@ export class WorkerManager {
20
23
  fs.mkdirSync(this.config.jobsDir, { recursive: true });
21
24
  }
22
25
  }
26
+ /**
27
+ * Set callback for gallery updates (worker assets for UI)
28
+ */
29
+ setGalleryCallback(callback) {
30
+ this.galleryCallback = callback;
31
+ }
32
+ /**
33
+ * Get gallery workers with assets for UI
34
+ */
35
+ getGalleryWorkers() {
36
+ const jobs = this.list();
37
+ return jobs
38
+ .filter(job => job.assets && job.assets.length > 0 || job.status === 'running')
39
+ .slice(0, 20) // Limit for UI
40
+ .map(job => ({
41
+ id: job.id,
42
+ task: job.task.slice(0, 60),
43
+ status: (job.status === 'timeout' || job.status === 'cancelled') ? 'failed' :
44
+ (job.status === 'pending' ? 'running' : job.status),
45
+ assets: job.assets || []
46
+ }));
47
+ }
48
+ /**
49
+ * Broadcast gallery update
50
+ */
51
+ broadcastGalleryUpdate() {
52
+ if (!this.galleryCallback)
53
+ return;
54
+ const workers = this.getGalleryWorkers();
55
+ if (workers.length > 0) {
56
+ this.galleryCallback(workers);
57
+ }
58
+ }
23
59
  /**
24
60
  * Generate a unique job ID
25
61
  */
@@ -58,6 +94,15 @@ export class WorkerManager {
58
94
  // Validate timeout
59
95
  const effectiveTimeout = Math.min(timeout, this.config.maxTimeout);
60
96
  const jobId = this.generateJobId();
97
+ // Take snapshot before spawning (for asset tracking)
98
+ try {
99
+ const beforeSnapshot = takeSnapshot(workspace);
100
+ this.fileSnapshots.set(jobId, beforeSnapshot);
101
+ console.log(`[WORKER] Snapshot for ${jobId}: ${beforeSnapshot.files.size} files`);
102
+ }
103
+ catch (err) {
104
+ console.log(`[WORKER] Could not take snapshot for ${jobId}: ${err}`);
105
+ }
61
106
  const job = {
62
107
  id: jobId,
63
108
  task,
@@ -155,6 +200,25 @@ This ensures nothing is lost even if your output gets truncated.`;
155
200
  if (stderr && code !== 0) {
156
201
  currentJob.error = stderr.trim();
157
202
  }
203
+ // Track file changes (for gallery)
204
+ const beforeSnapshot = this.fileSnapshots.get(jobId);
205
+ if (beforeSnapshot) {
206
+ try {
207
+ const afterSnapshot = takeSnapshot(currentJob.workspace);
208
+ const assets = compareSnapshots(beforeSnapshot, afterSnapshot);
209
+ currentJob.assets = assets;
210
+ const newCount = assets.filter(a => a.type === 'new').length;
211
+ const modCount = assets.filter(a => a.type === 'modified').length;
212
+ console.log(`[WORKER] ${jobId} assets: ${assets.length} files (${newCount} new, ${modCount} modified)`);
213
+ // Clean up snapshot
214
+ this.fileSnapshots.delete(jobId);
215
+ // Broadcast gallery update
216
+ this.broadcastGalleryUpdate();
217
+ }
218
+ catch (err) {
219
+ console.log(`[WORKER] Could not track assets for ${jobId}: ${err}`);
220
+ }
221
+ }
158
222
  this.saveJob(currentJob);
159
223
  }
160
224
  });
@@ -1,6 +1,17 @@
1
1
  /**
2
2
  * Worker System Types
3
3
  */
4
+ export interface WorkerAsset {
5
+ path: string;
6
+ name: string;
7
+ type: 'new' | 'modified' | 'unchanged';
8
+ fileType: 'code' | 'image' | 'pdf' | 'text' | 'other';
9
+ preview?: string;
10
+ diff?: Array<{
11
+ type: 'context' | 'add' | 'remove';
12
+ content: string;
13
+ }>;
14
+ }
4
15
  export interface WorkerJob {
5
16
  id: string;
6
17
  task: string;
@@ -15,6 +26,7 @@ export interface WorkerJob {
15
26
  pid?: number;
16
27
  timeout: number;
17
28
  exitCode?: number;
29
+ assets?: WorkerAsset[];
18
30
  }
19
31
  export interface SpawnOptions {
20
32
  workspace?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siftd/connect-agent",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "Master orchestrator agent - control Claude Code remotely via web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",