@codewithdan/zingit 0.17.5 → 0.17.7

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,182 +0,0 @@
1
- // server/src/agents/copilot.ts
2
- // Agent that uses GitHub Copilot SDK
3
- import { CopilotClient } from '@github/copilot-sdk';
4
- import { BaseAgent } from './base.js';
5
- import { promises as fs } from 'fs';
6
- import * as path from 'path';
7
- import * as os from 'os';
8
- import { randomUUID } from 'crypto';
9
- export class CopilotAgent extends BaseAgent {
10
- name = 'copilot';
11
- model;
12
- client = null;
13
- constructor() {
14
- super();
15
- this.model = process.env.COPILOT_MODEL || 'claude-sonnet-4-20250514';
16
- }
17
- async start() {
18
- // Initialize the Copilot client
19
- this.client = new CopilotClient({
20
- logLevel: 'info',
21
- autoRestart: true,
22
- });
23
- await this.client.start();
24
- console.log(`✓ Copilot SDK initialized (model: ${this.model})`);
25
- }
26
- async stop() {
27
- if (this.client) {
28
- await this.client.stop();
29
- this.client = null;
30
- }
31
- }
32
- async createSession(wsRef, projectDir, resumeSessionId) {
33
- if (!this.client) {
34
- throw new Error('Copilot client not initialized');
35
- }
36
- const send = (data) => {
37
- const ws = wsRef.current;
38
- if (ws && ws.readyState === ws.OPEN) {
39
- ws.send(JSON.stringify(data));
40
- }
41
- };
42
- // System message and permission config for both new and resumed sessions
43
- const sessionConfig = {
44
- model: this.model,
45
- streaming: true,
46
- systemMessage: {
47
- mode: 'append',
48
- content: `
49
- <context>
50
- You are a UI debugging assistant working in the project directory: ${projectDir}
51
-
52
- When given markers about UI elements:
53
- 1. Search for the corresponding code using the selectors and HTML context provided
54
- 2. Make the requested changes in the project at ${projectDir}
55
- 3. Be thorough in finding the right files and making precise edits
56
-
57
- When screenshots are provided, use them to:
58
- - Better understand the visual context and styling of the elements
59
- - Identify the exact appearance that needs to be changed
60
- - Verify you're targeting the correct element based on its visual representation
61
-
62
- IMPORTANT: Format all responses using markdown:
63
- - Use **bold** for emphasis on important points
64
- - Use numbered lists for sequential steps (1. 2. 3.)
65
- - Use bullet points for non-sequential items
66
- - Use code blocks with \`\`\`language syntax for code examples
67
- - Use inline \`code\` for file paths, selectors, and technical terms
68
- </context>
69
- `
70
- },
71
- onPermissionRequest: async (request) => {
72
- // Auto-approve read/write operations for file edits
73
- if (request.kind === 'read' || request.kind === 'write') {
74
- return { kind: 'approved' };
75
- }
76
- return { kind: 'approved' };
77
- },
78
- };
79
- // Resume existing session if we have a sessionId, otherwise create new session
80
- const session = resumeSessionId
81
- ? await this.client.resumeSession(resumeSessionId, sessionConfig)
82
- : await this.client.createSession(sessionConfig);
83
- // Track temp files for cleanup on session destroy (prevents race condition)
84
- const sessionTempFiles = [];
85
- // Subscribe to streaming events and capture unsubscribe function
86
- const unsubscribe = session.on((event) => {
87
- switch (event.type) {
88
- case 'assistant.message_delta':
89
- // Streaming chunk
90
- send({ type: 'delta', content: event.data.deltaContent });
91
- break;
92
- case 'assistant.message':
93
- // Final message (we already sent deltas, so just log)
94
- console.log('[Copilot Agent] Assistant message complete');
95
- break;
96
- case 'tool.execution_start':
97
- console.log('[Copilot Agent] Tool executing:', event.data.toolName);
98
- send({ type: 'tool_start', tool: event.data.toolName });
99
- break;
100
- case 'tool.execution_complete':
101
- console.log('[Copilot Agent] Tool complete:', event.data.toolCallId);
102
- send({ type: 'tool_end', tool: event.data.toolCallId });
103
- break;
104
- case 'session.idle':
105
- console.log('[Copilot Agent] Session idle, sending idle message');
106
- send({ type: 'idle' });
107
- break;
108
- case 'session.error':
109
- console.error('[Copilot Agent] Session error:', event.data.message);
110
- send({ type: 'error', message: event.data.message });
111
- break;
112
- }
113
- });
114
- return {
115
- send: async (msg) => {
116
- try {
117
- console.log('[Copilot Agent] send() called, processing request...');
118
- // If images are provided, save them as temp files and attach them
119
- // Copilot SDK supports file attachments for images
120
- const attachments = [];
121
- if (msg.images && msg.images.length > 0) {
122
- const tempDir = os.tmpdir();
123
- for (let i = 0; i < msg.images.length; i++) {
124
- const img = msg.images[i];
125
- // Use UUID to avoid filename collisions
126
- const ext = img.mediaType.split('/')[1] || 'png';
127
- const tempPath = path.join(tempDir, `zingit-screenshot-${randomUUID()}.${ext}`);
128
- // Decode base64 to buffer with error handling
129
- let buffer;
130
- try {
131
- buffer = Buffer.from(img.base64, 'base64');
132
- }
133
- catch (decodeErr) {
134
- console.warn(`ZingIt: Failed to decode base64 for image ${i + 1}:`, decodeErr);
135
- continue; // Skip this image
136
- }
137
- // Save with restrictive permissions (owner read/write only)
138
- await fs.writeFile(tempPath, buffer, { mode: 0o600 });
139
- sessionTempFiles.push(tempPath);
140
- attachments.push({
141
- type: 'file',
142
- path: tempPath,
143
- displayName: img.label || `Screenshot ${i + 1}`
144
- });
145
- }
146
- }
147
- console.log('[Copilot Agent] Calling session.sendAndWait...');
148
- await session.sendAndWait({
149
- prompt: msg.prompt,
150
- attachments: attachments.length > 0 ? attachments : undefined
151
- });
152
- console.log('[Copilot Agent] session.sendAndWait completed');
153
- }
154
- catch (err) {
155
- console.error('[Copilot Agent] Error in send():', err.message);
156
- send({ type: 'error', message: err.message });
157
- }
158
- // Note: Temp files cleaned up on session destroy to avoid race condition
159
- },
160
- destroy: async () => {
161
- try {
162
- unsubscribe();
163
- await session.destroy();
164
- }
165
- finally {
166
- // Clean up all temp files even if destroy() fails
167
- for (const tempPath of sessionTempFiles) {
168
- try {
169
- await fs.unlink(tempPath);
170
- }
171
- catch (cleanupErr) {
172
- // Ignore errors (file may already be deleted)
173
- console.warn(`ZingIt: Failed to clean up temp file ${tempPath}:`, cleanupErr.message);
174
- }
175
- }
176
- sessionTempFiles.length = 0; // Clear the array
177
- }
178
- },
179
- getSessionId: () => session.sessionId
180
- };
181
- }
182
- }