@codewithdan/zingit 0.0.1
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/AGENTS.md +214 -0
- package/LICENSE +21 -0
- package/README.md +301 -0
- package/bin/cli.js +90 -0
- package/client/dist/zingit-client.js +2974 -0
- package/package.json +69 -0
- package/server/dist/agents/base.d.ts +20 -0
- package/server/dist/agents/base.js +136 -0
- package/server/dist/agents/claude.d.ts +18 -0
- package/server/dist/agents/claude.js +141 -0
- package/server/dist/agents/codex.d.ts +12 -0
- package/server/dist/agents/codex.js +194 -0
- package/server/dist/agents/copilot.d.ts +12 -0
- package/server/dist/agents/copilot.js +168 -0
- package/server/dist/handlers/messageHandlers.d.ts +57 -0
- package/server/dist/handlers/messageHandlers.js +329 -0
- package/server/dist/index.d.ts +1 -0
- package/server/dist/index.js +244 -0
- package/server/dist/services/git-manager.d.ts +104 -0
- package/server/dist/services/git-manager.js +317 -0
- package/server/dist/services/index.d.ts +2 -0
- package/server/dist/services/index.js +2 -0
- package/server/dist/types.d.ts +74 -0
- package/server/dist/types.js +2 -0
- package/server/dist/utils/agent-detection.d.ts +17 -0
- package/server/dist/utils/agent-detection.js +91 -0
- package/server/dist/validation/payload.d.ts +12 -0
- package/server/dist/validation/payload.js +64 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { WebSocket } from 'ws';
|
|
2
|
+
import type { CheckpointInfo } from './services/git-manager.js';
|
|
3
|
+
export interface Agent {
|
|
4
|
+
name: string;
|
|
5
|
+
model: string;
|
|
6
|
+
start(): Promise<void>;
|
|
7
|
+
stop(): Promise<void>;
|
|
8
|
+
createSession(ws: WebSocket, projectDir: string): Promise<AgentSession>;
|
|
9
|
+
formatPrompt(data: BatchData, projectDir: string): string;
|
|
10
|
+
extractImages(data: BatchData): ImageContent[];
|
|
11
|
+
}
|
|
12
|
+
export interface ImageContent {
|
|
13
|
+
base64: string;
|
|
14
|
+
mediaType: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface AgentSession {
|
|
18
|
+
send(msg: {
|
|
19
|
+
prompt: string;
|
|
20
|
+
images?: ImageContent[];
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
destroy(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export interface Annotation {
|
|
25
|
+
id: string;
|
|
26
|
+
selector: string;
|
|
27
|
+
identifier: string;
|
|
28
|
+
html: string;
|
|
29
|
+
notes: string;
|
|
30
|
+
status?: 'pending' | 'processing' | 'completed';
|
|
31
|
+
selectedText?: string;
|
|
32
|
+
parentContext?: string;
|
|
33
|
+
textContent?: string;
|
|
34
|
+
siblingContext?: string;
|
|
35
|
+
parentHtml?: string;
|
|
36
|
+
screenshot?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface BatchData {
|
|
39
|
+
pageUrl: string;
|
|
40
|
+
pageTitle: string;
|
|
41
|
+
annotations: Annotation[];
|
|
42
|
+
projectDir?: string;
|
|
43
|
+
}
|
|
44
|
+
export type WSIncomingType = 'batch' | 'message' | 'reset' | 'stop' | 'get_agents' | 'select_agent' | 'get_history' | 'undo' | 'revert_to' | 'clear_history';
|
|
45
|
+
export interface WSIncomingMessage {
|
|
46
|
+
type: WSIncomingType;
|
|
47
|
+
data?: BatchData;
|
|
48
|
+
content?: string;
|
|
49
|
+
agent?: string;
|
|
50
|
+
checkpointId?: string;
|
|
51
|
+
}
|
|
52
|
+
export type WSOutgoingType = 'connected' | 'processing' | 'response' | 'delta' | 'tool_start' | 'tool_end' | 'idle' | 'error' | 'reset_complete' | 'agents' | 'agent_selected' | 'agent_error' | 'checkpoint_created' | 'history' | 'undo_complete' | 'revert_complete' | 'history_cleared';
|
|
53
|
+
export interface AgentInfoMessage {
|
|
54
|
+
name: string;
|
|
55
|
+
displayName: string;
|
|
56
|
+
available: boolean;
|
|
57
|
+
version?: string;
|
|
58
|
+
reason?: string;
|
|
59
|
+
installCommand: string;
|
|
60
|
+
}
|
|
61
|
+
export interface WSOutgoingMessage {
|
|
62
|
+
type: WSOutgoingType;
|
|
63
|
+
content?: string;
|
|
64
|
+
message?: string;
|
|
65
|
+
tool?: string;
|
|
66
|
+
agent?: string;
|
|
67
|
+
model?: string;
|
|
68
|
+
projectDir?: string;
|
|
69
|
+
agents?: AgentInfoMessage[];
|
|
70
|
+
checkpoint?: CheckpointInfo;
|
|
71
|
+
checkpoints?: CheckpointInfo[];
|
|
72
|
+
checkpointId?: string;
|
|
73
|
+
filesReverted?: string[];
|
|
74
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface AgentInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
displayName: string;
|
|
4
|
+
available: boolean;
|
|
5
|
+
version?: string;
|
|
6
|
+
reason?: string;
|
|
7
|
+
installCommand: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Detect all available agents and their status
|
|
11
|
+
* Uses async operations to avoid blocking the event loop
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectAgents(): Promise<AgentInfo[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Check if a specific agent is available
|
|
16
|
+
*/
|
|
17
|
+
export declare function isAgentAvailable(agentName: string): Promise<AgentInfo | undefined>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// server/src/utils/agent-detection.ts
|
|
2
|
+
// Cross-platform agent CLI detection
|
|
3
|
+
import { exec } from 'child_process';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
/**
|
|
10
|
+
* Check if a CLI command is available (cross-platform)
|
|
11
|
+
* Uses async exec to avoid blocking the event loop
|
|
12
|
+
*/
|
|
13
|
+
async function checkCLI(command) {
|
|
14
|
+
try {
|
|
15
|
+
const { stdout } = await execAsync(`${command} --version`, {
|
|
16
|
+
encoding: 'utf-8',
|
|
17
|
+
timeout: 5000
|
|
18
|
+
});
|
|
19
|
+
return { installed: true, version: stdout.split('\n')[0].trim() };
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return { installed: false };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if Codex auth file exists (cross-platform using os.homedir())
|
|
27
|
+
*/
|
|
28
|
+
function checkCodexAuth() {
|
|
29
|
+
const authPath = join(homedir(), '.codex', 'auth.json');
|
|
30
|
+
return existsSync(authPath);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detect all available agents and their status
|
|
34
|
+
* Uses async operations to avoid blocking the event loop
|
|
35
|
+
*/
|
|
36
|
+
export async function detectAgents() {
|
|
37
|
+
// Run all CLI checks in parallel for better performance
|
|
38
|
+
const [claudeCheck, copilotCheck, codexCheck] = await Promise.all([
|
|
39
|
+
checkCLI('claude'),
|
|
40
|
+
checkCLI('copilot'),
|
|
41
|
+
checkCLI('codex')
|
|
42
|
+
]);
|
|
43
|
+
const agents = [];
|
|
44
|
+
// Claude Code
|
|
45
|
+
agents.push({
|
|
46
|
+
name: 'claude',
|
|
47
|
+
displayName: 'Claude Code',
|
|
48
|
+
available: claudeCheck.installed,
|
|
49
|
+
version: claudeCheck.version,
|
|
50
|
+
reason: claudeCheck.installed ? undefined : 'Claude Code CLI not found',
|
|
51
|
+
installCommand: 'npm install -g @anthropic-ai/claude-code'
|
|
52
|
+
});
|
|
53
|
+
// GitHub Copilot
|
|
54
|
+
agents.push({
|
|
55
|
+
name: 'copilot',
|
|
56
|
+
displayName: 'GitHub Copilot CLI',
|
|
57
|
+
available: copilotCheck.installed,
|
|
58
|
+
version: copilotCheck.version,
|
|
59
|
+
reason: copilotCheck.installed ? undefined : 'Copilot CLI not found',
|
|
60
|
+
installCommand: 'npm install -g @github/copilot'
|
|
61
|
+
});
|
|
62
|
+
// OpenAI Codex
|
|
63
|
+
if (codexCheck.installed) {
|
|
64
|
+
const hasAuth = checkCodexAuth();
|
|
65
|
+
agents.push({
|
|
66
|
+
name: 'codex',
|
|
67
|
+
displayName: 'OpenAI Codex',
|
|
68
|
+
available: hasAuth,
|
|
69
|
+
version: codexCheck.version,
|
|
70
|
+
reason: hasAuth ? undefined : 'Codex CLI installed but not logged in. Run: codex',
|
|
71
|
+
installCommand: 'npm install -g @openai/codex'
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
agents.push({
|
|
76
|
+
name: 'codex',
|
|
77
|
+
displayName: 'OpenAI Codex',
|
|
78
|
+
available: false,
|
|
79
|
+
reason: 'Codex CLI not found',
|
|
80
|
+
installCommand: 'npm install -g @openai/codex'
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return agents;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check if a specific agent is available
|
|
87
|
+
*/
|
|
88
|
+
export async function isAgentAvailable(agentName) {
|
|
89
|
+
const agents = await detectAgents();
|
|
90
|
+
return agents.find(a => a.name === agentName);
|
|
91
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BatchData } from '../types.js';
|
|
2
|
+
export declare const MAX_ANNOTATIONS = 50;
|
|
3
|
+
export declare const MAX_HTML_LENGTH = 50000;
|
|
4
|
+
export declare const MAX_NOTES_LENGTH = 5000;
|
|
5
|
+
export declare const MAX_SELECTOR_LENGTH = 1000;
|
|
6
|
+
export declare const MAX_SCREENSHOT_SIZE = 5000000;
|
|
7
|
+
export interface ValidationResult {
|
|
8
|
+
valid: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
sanitizedData?: BatchData;
|
|
11
|
+
}
|
|
12
|
+
export declare function validateBatchData(data: BatchData): ValidationResult;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// server/src/validation/payload.ts
|
|
2
|
+
// ============================================
|
|
3
|
+
// Payload Validation
|
|
4
|
+
// ============================================
|
|
5
|
+
export const MAX_ANNOTATIONS = 50;
|
|
6
|
+
export const MAX_HTML_LENGTH = 50000;
|
|
7
|
+
export const MAX_NOTES_LENGTH = 5000;
|
|
8
|
+
export const MAX_SELECTOR_LENGTH = 1000;
|
|
9
|
+
export const MAX_SCREENSHOT_SIZE = 5000000; // ~5MB base64 (matches Claude API limit)
|
|
10
|
+
const VALID_STATUSES = ['pending', 'processing', 'completed'];
|
|
11
|
+
function validateAnnotation(annotation, index) {
|
|
12
|
+
if (!annotation.id || typeof annotation.id !== 'string') {
|
|
13
|
+
return { valid: false, error: `Annotation ${index}: missing or invalid id` };
|
|
14
|
+
}
|
|
15
|
+
if (!annotation.identifier || typeof annotation.identifier !== 'string') {
|
|
16
|
+
return { valid: false, error: `Annotation ${index}: missing or invalid identifier` };
|
|
17
|
+
}
|
|
18
|
+
if (annotation.status && !VALID_STATUSES.includes(annotation.status)) {
|
|
19
|
+
return { valid: false, error: `Annotation ${index}: invalid status '${annotation.status}'` };
|
|
20
|
+
}
|
|
21
|
+
if (annotation.selector && annotation.selector.length > MAX_SELECTOR_LENGTH) {
|
|
22
|
+
return { valid: false, error: `Annotation ${index}: selector too long (max ${MAX_SELECTOR_LENGTH})` };
|
|
23
|
+
}
|
|
24
|
+
if (annotation.html && annotation.html.length > MAX_HTML_LENGTH) {
|
|
25
|
+
return { valid: false, error: `Annotation ${index}: html too long (max ${MAX_HTML_LENGTH})` };
|
|
26
|
+
}
|
|
27
|
+
if (annotation.notes && annotation.notes.length > MAX_NOTES_LENGTH) {
|
|
28
|
+
return { valid: false, error: `Annotation ${index}: notes too long (max ${MAX_NOTES_LENGTH})` };
|
|
29
|
+
}
|
|
30
|
+
if (annotation.screenshot && annotation.screenshot.length > MAX_SCREENSHOT_SIZE) {
|
|
31
|
+
return { valid: false, error: `Annotation ${index}: screenshot too large (max ${MAX_SCREENSHOT_SIZE / 1000}KB)` };
|
|
32
|
+
}
|
|
33
|
+
return { valid: true };
|
|
34
|
+
}
|
|
35
|
+
export function validateBatchData(data) {
|
|
36
|
+
if (!data) {
|
|
37
|
+
return { valid: false, error: 'Missing batch data' };
|
|
38
|
+
}
|
|
39
|
+
if (!data.annotations || !Array.isArray(data.annotations)) {
|
|
40
|
+
return { valid: false, error: 'Missing or invalid annotations array' };
|
|
41
|
+
}
|
|
42
|
+
if (data.annotations.length === 0) {
|
|
43
|
+
return { valid: false, error: 'No annotations provided' };
|
|
44
|
+
}
|
|
45
|
+
if (data.annotations.length > MAX_ANNOTATIONS) {
|
|
46
|
+
return { valid: false, error: `Too many annotations (max ${MAX_ANNOTATIONS})` };
|
|
47
|
+
}
|
|
48
|
+
// Validate each annotation
|
|
49
|
+
for (let i = 0; i < data.annotations.length; i++) {
|
|
50
|
+
const result = validateAnnotation(data.annotations[i], i);
|
|
51
|
+
if (!result.valid) {
|
|
52
|
+
return { valid: false, error: result.error };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Sanitize and return
|
|
56
|
+
return {
|
|
57
|
+
valid: true,
|
|
58
|
+
sanitizedData: {
|
|
59
|
+
...data,
|
|
60
|
+
pageUrl: data.pageUrl ? data.pageUrl.slice(0, 2000) : data.pageUrl,
|
|
61
|
+
pageTitle: data.pageTitle ? data.pageTitle.slice(0, 500) : data.pageTitle,
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|