genbox-agent 0.0.2
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/daemon.d.ts +12 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +272 -0
- package/dist/daemon.js.map +1 -0
- package/dist/hook.d.ts +15 -0
- package/dist/hook.d.ts.map +1 -0
- package/dist/hook.js +207 -0
- package/dist/hook.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/session-manager.d.ts +52 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +253 -0
- package/dist/session-manager.js.map +1 -0
- package/package.json +28 -0
- package/src/daemon.ts +329 -0
- package/src/hook.ts +200 -0
- package/src/index.ts +1 -0
- package/src/session-manager.ts +270 -0
- package/tsconfig.json +19 -0
package/src/hook.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code Hook Script
|
|
4
|
+
*
|
|
5
|
+
* Called by Claude Code hooks to report all interactions to the backend.
|
|
6
|
+
* This script reads from stdin (hook input) and POSTs to the monitoring API.
|
|
7
|
+
*
|
|
8
|
+
* Environment variables:
|
|
9
|
+
* - GENBOX_ID: Unique identifier for this genbox
|
|
10
|
+
* - GENBOX_TOKEN: Authentication token for the API
|
|
11
|
+
* - MONITORING_API_URL: Base URL for the monitoring API
|
|
12
|
+
* - CLAUDE_HOOK_TYPE: The type of hook being called (set by Claude Code)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import * as crypto from 'crypto';
|
|
18
|
+
import * as http from 'http';
|
|
19
|
+
import * as https from 'https';
|
|
20
|
+
|
|
21
|
+
// Configuration
|
|
22
|
+
const GENBOX_ID = process.env.GENBOX_ID || '';
|
|
23
|
+
const GENBOX_TOKEN = process.env.GENBOX_TOKEN || '';
|
|
24
|
+
const MONITORING_API_URL = process.env.MONITORING_API_URL || 'http://localhost:3001';
|
|
25
|
+
const HOOK_TYPE = process.env.CLAUDE_HOOK_TYPE || 'Unknown';
|
|
26
|
+
|
|
27
|
+
// Session file path
|
|
28
|
+
const SESSION_FILE = path.join(process.env.HOME || '/tmp', '.claude-session-id');
|
|
29
|
+
|
|
30
|
+
function getOrCreateSessionId(): string {
|
|
31
|
+
try {
|
|
32
|
+
if (fs.existsSync(SESSION_FILE)) {
|
|
33
|
+
return fs.readFileSync(SESSION_FILE, 'utf-8').trim();
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
// Ignore read errors
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Generate new session ID
|
|
40
|
+
const sessionId = crypto
|
|
41
|
+
.createHash('sha256')
|
|
42
|
+
.update(`${GENBOX_ID}-${Date.now()}-${process.pid}`)
|
|
43
|
+
.digest('hex')
|
|
44
|
+
.slice(0, 32);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
fs.writeFileSync(SESSION_FILE, sessionId);
|
|
48
|
+
} catch {
|
|
49
|
+
// Ignore write errors
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return sessionId;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function clearSessionId(): void {
|
|
56
|
+
try {
|
|
57
|
+
if (fs.existsSync(SESSION_FILE)) {
|
|
58
|
+
fs.unlinkSync(SESSION_FILE);
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// Ignore errors
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getTmuxSession(): string | undefined {
|
|
66
|
+
if (process.env.TMUX) {
|
|
67
|
+
const tmuxPane = process.env.TMUX_PANE || '';
|
|
68
|
+
return tmuxPane.split(',')[0] || undefined;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function sendEvent(eventData: Record<string, any>): Promise<boolean> {
|
|
74
|
+
if (!GENBOX_ID || !GENBOX_TOKEN) {
|
|
75
|
+
console.error('Warning: GENBOX_ID or GENBOX_TOKEN not set, skipping event');
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const url = new URL('/claude/events', MONITORING_API_URL);
|
|
80
|
+
|
|
81
|
+
// Add metadata
|
|
82
|
+
eventData.genboxId = GENBOX_ID;
|
|
83
|
+
eventData.genboxToken = GENBOX_TOKEN;
|
|
84
|
+
eventData.sessionId = getOrCreateSessionId();
|
|
85
|
+
eventData.tmuxSession = getTmuxSession();
|
|
86
|
+
eventData.projectPath = process.cwd();
|
|
87
|
+
eventData.timestamp = new Date().toISOString();
|
|
88
|
+
|
|
89
|
+
const data = JSON.stringify(eventData);
|
|
90
|
+
const isHttps = url.protocol === 'https:';
|
|
91
|
+
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
const options = {
|
|
94
|
+
hostname: url.hostname,
|
|
95
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
96
|
+
path: url.pathname,
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers: {
|
|
99
|
+
'Content-Type': 'application/json',
|
|
100
|
+
'Content-Length': Buffer.byteLength(data),
|
|
101
|
+
'X-Genbox-Token': GENBOX_TOKEN,
|
|
102
|
+
},
|
|
103
|
+
timeout: 5000,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const req = (isHttps ? https : http).request(options, (res) => {
|
|
107
|
+
resolve(res.statusCode === 200 || res.statusCode === 201);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
req.on('error', (error) => {
|
|
111
|
+
console.error(`Warning: Failed to send event: ${error.message}`);
|
|
112
|
+
resolve(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
req.on('timeout', () => {
|
|
116
|
+
req.destroy();
|
|
117
|
+
console.error('Warning: Request timeout');
|
|
118
|
+
resolve(false);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
req.write(data);
|
|
122
|
+
req.end();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function parseHookInput(input: string): Record<string, any> {
|
|
127
|
+
try {
|
|
128
|
+
if (!input.trim()) {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
return JSON.parse(input);
|
|
132
|
+
} catch {
|
|
133
|
+
return { raw_input: input };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function main(): Promise<void> {
|
|
138
|
+
// Read input from stdin
|
|
139
|
+
let input = '';
|
|
140
|
+
if (!process.stdin.isTTY) {
|
|
141
|
+
input = fs.readFileSync(0, 'utf-8');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const hookInput = parseHookInput(input);
|
|
145
|
+
|
|
146
|
+
// Build event data based on hook type
|
|
147
|
+
const eventData: Record<string, any> = {
|
|
148
|
+
eventType: HOOK_TYPE,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
switch (HOOK_TYPE) {
|
|
152
|
+
case 'PreToolUse':
|
|
153
|
+
eventData.toolName = hookInput.tool_name || '';
|
|
154
|
+
eventData.toolInput = hookInput.tool_input || {};
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
case 'PostToolUse':
|
|
158
|
+
eventData.toolName = hookInput.tool_name || '';
|
|
159
|
+
eventData.toolInput = hookInput.tool_input || {};
|
|
160
|
+
eventData.toolResponse = hookInput.tool_response || {};
|
|
161
|
+
eventData.success = hookInput.tool_response?.success !== false;
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
case 'UserPromptSubmit':
|
|
165
|
+
const prompt = hookInput.prompt || '';
|
|
166
|
+
eventData.prompt = prompt;
|
|
167
|
+
eventData.promptLength = prompt.length;
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case 'Stop':
|
|
171
|
+
eventData.stopReason = hookInput.stop_reason || '';
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case 'SubagentStop':
|
|
175
|
+
eventData.subagentType = hookInput.subagent_type || '';
|
|
176
|
+
eventData.stopReason = hookInput.stop_reason || '';
|
|
177
|
+
break;
|
|
178
|
+
|
|
179
|
+
case 'Notification':
|
|
180
|
+
eventData.notification = hookInput.message || '';
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case 'SessionStart':
|
|
184
|
+
eventData.cliVersion = hookInput.cli_version || '';
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
case 'SessionEnd':
|
|
188
|
+
clearSessionId();
|
|
189
|
+
eventData.sessionDuration = hookInput.duration_ms || 0;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Send the event (don't await - fire and forget)
|
|
194
|
+
sendEvent(eventData).catch(() => {});
|
|
195
|
+
|
|
196
|
+
// Exit immediately - hooks should not block Claude
|
|
197
|
+
process.exit(0);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
main().catch(() => process.exit(0));
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SessionManager, ClaudeSession } from './session-manager';
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { execSync, spawn } from 'child_process';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
export interface ClaudeSession {
|
|
5
|
+
sessionName: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
projectPath: string;
|
|
8
|
+
createdAt: number;
|
|
9
|
+
status: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Manages tmux sessions running Claude Code.
|
|
14
|
+
*/
|
|
15
|
+
export class SessionManager {
|
|
16
|
+
private readonly TMUX_PREFIX = 'claude-';
|
|
17
|
+
private sessions: Map<string, ClaudeSession> = new Map();
|
|
18
|
+
|
|
19
|
+
constructor(private readonly genboxId: string) {}
|
|
20
|
+
|
|
21
|
+
private runTmux(args: string[], timeout = 10000): { stdout: string; stderr: string; success: boolean } {
|
|
22
|
+
try {
|
|
23
|
+
const stdout = execSync(`tmux ${args.join(' ')}`, {
|
|
24
|
+
timeout,
|
|
25
|
+
encoding: 'utf-8',
|
|
26
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
27
|
+
});
|
|
28
|
+
return { stdout: stdout || '', stderr: '', success: true };
|
|
29
|
+
} catch (error: any) {
|
|
30
|
+
return {
|
|
31
|
+
stdout: error.stdout?.toString() || '',
|
|
32
|
+
stderr: error.stderr?.toString() || error.message,
|
|
33
|
+
success: false,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private generateSessionId(): string {
|
|
39
|
+
return crypto
|
|
40
|
+
.createHash('sha256')
|
|
41
|
+
.update(`${this.genboxId}-${Date.now()}-${process.pid}`)
|
|
42
|
+
.digest('hex')
|
|
43
|
+
.slice(0, 16);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create a new Claude Code session in tmux.
|
|
48
|
+
*/
|
|
49
|
+
createSession(projectPath = '/home/dev'): ClaudeSession | null {
|
|
50
|
+
const sessionId = this.generateSessionId();
|
|
51
|
+
const sessionName = `${this.TMUX_PREFIX}${sessionId}`;
|
|
52
|
+
|
|
53
|
+
// Create a new detached tmux session
|
|
54
|
+
const result = this.runTmux([
|
|
55
|
+
'new-session',
|
|
56
|
+
'-d',
|
|
57
|
+
'-s', sessionName,
|
|
58
|
+
'-c', projectPath,
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
if (!result.success) {
|
|
62
|
+
console.error(`Failed to create tmux session: ${result.stderr}`);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Give tmux a moment to initialize
|
|
67
|
+
execSync('sleep 0.5');
|
|
68
|
+
|
|
69
|
+
// Start Claude Code in the session
|
|
70
|
+
this.runTmux([
|
|
71
|
+
'send-keys',
|
|
72
|
+
'-t', sessionName,
|
|
73
|
+
'claude',
|
|
74
|
+
'Enter',
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
const session: ClaudeSession = {
|
|
78
|
+
sessionName,
|
|
79
|
+
sessionId,
|
|
80
|
+
projectPath,
|
|
81
|
+
createdAt: Date.now(),
|
|
82
|
+
status: 'active',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.sessions.set(sessionId, session);
|
|
86
|
+
return session;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Send a prompt to a Claude Code session.
|
|
91
|
+
*/
|
|
92
|
+
sendPrompt(sessionId: string, prompt: string): boolean {
|
|
93
|
+
const session = this.sessions.get(sessionId);
|
|
94
|
+
const sessionName = session?.sessionName || `${this.TMUX_PREFIX}${sessionId}`;
|
|
95
|
+
|
|
96
|
+
// Send the prompt text with literal flag to handle special characters
|
|
97
|
+
const textResult = this.runTmux([
|
|
98
|
+
'send-keys',
|
|
99
|
+
'-t', sessionName,
|
|
100
|
+
'-l', // Literal flag
|
|
101
|
+
`"${prompt.replace(/"/g, '\\"')}"`,
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
if (!textResult.success) {
|
|
105
|
+
console.error(`Failed to send prompt text: ${textResult.stderr}`);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Send Enter to submit the prompt
|
|
110
|
+
const enterResult = this.runTmux([
|
|
111
|
+
'send-keys',
|
|
112
|
+
'-t', sessionName,
|
|
113
|
+
'Enter',
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
if (!enterResult.success) {
|
|
117
|
+
console.error(`Failed to send Enter: ${enterResult.stderr}`);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Send a keystroke to a Claude Code session.
|
|
126
|
+
*/
|
|
127
|
+
sendKeystroke(sessionId: string, key: string): boolean {
|
|
128
|
+
const session = this.sessions.get(sessionId);
|
|
129
|
+
const sessionName = session?.sessionName || `${this.TMUX_PREFIX}${sessionId}`;
|
|
130
|
+
|
|
131
|
+
// Map common key names to tmux key names
|
|
132
|
+
const keyMap: Record<string, string> = {
|
|
133
|
+
enter: 'Enter',
|
|
134
|
+
escape: 'Escape',
|
|
135
|
+
up: 'Up',
|
|
136
|
+
down: 'Down',
|
|
137
|
+
left: 'Left',
|
|
138
|
+
right: 'Right',
|
|
139
|
+
tab: 'Tab',
|
|
140
|
+
backspace: 'BSpace',
|
|
141
|
+
delete: 'DC',
|
|
142
|
+
'ctrl-c': 'C-c',
|
|
143
|
+
'ctrl-d': 'C-d',
|
|
144
|
+
'ctrl-z': 'C-z',
|
|
145
|
+
y: 'y',
|
|
146
|
+
n: 'n',
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const tmuxKey = keyMap[key.toLowerCase()] || key;
|
|
150
|
+
|
|
151
|
+
const result = this.runTmux([
|
|
152
|
+
'send-keys',
|
|
153
|
+
'-t', sessionName,
|
|
154
|
+
tmuxKey,
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
return result.success;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Capture the terminal output from a session.
|
|
162
|
+
*/
|
|
163
|
+
getOutput(sessionId: string, lines = 100): string | null {
|
|
164
|
+
const session = this.sessions.get(sessionId);
|
|
165
|
+
const sessionName = session?.sessionName || `${this.TMUX_PREFIX}${sessionId}`;
|
|
166
|
+
|
|
167
|
+
const result = this.runTmux([
|
|
168
|
+
'capture-pane',
|
|
169
|
+
'-t', sessionName,
|
|
170
|
+
'-p', // Print to stdout
|
|
171
|
+
'-S', `-${lines}`, // Start from lines back
|
|
172
|
+
]);
|
|
173
|
+
|
|
174
|
+
return result.success ? result.stdout : null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get the status of a session.
|
|
179
|
+
*/
|
|
180
|
+
getSessionStatus(sessionId: string): string {
|
|
181
|
+
const session = this.sessions.get(sessionId);
|
|
182
|
+
const sessionName = session?.sessionName || `${this.TMUX_PREFIX}${sessionId}`;
|
|
183
|
+
|
|
184
|
+
// Check if session exists
|
|
185
|
+
const hasSession = this.runTmux(['has-session', '-t', sessionName]);
|
|
186
|
+
if (!hasSession.success) {
|
|
187
|
+
return 'ended';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Get pane content to determine status
|
|
191
|
+
const output = this.getOutput(sessionId, 10);
|
|
192
|
+
if (output) {
|
|
193
|
+
const lowerOutput = output.toLowerCase();
|
|
194
|
+
const lastLine = output.split('\n').pop() || '';
|
|
195
|
+
|
|
196
|
+
if (lowerOutput.includes('waiting for input') || lastLine.includes('>')) {
|
|
197
|
+
return 'waiting_input';
|
|
198
|
+
}
|
|
199
|
+
if (lowerOutput.includes('error')) {
|
|
200
|
+
return 'error';
|
|
201
|
+
}
|
|
202
|
+
if (lowerOutput.includes('thinking') || lowerOutput.includes('working')) {
|
|
203
|
+
return 'active';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return 'idle';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Kill a Claude Code session.
|
|
212
|
+
*/
|
|
213
|
+
killSession(sessionId: string): boolean {
|
|
214
|
+
const session = this.sessions.get(sessionId);
|
|
215
|
+
const sessionName = session?.sessionName || `${this.TMUX_PREFIX}${sessionId}`;
|
|
216
|
+
|
|
217
|
+
const result = this.runTmux(['kill-session', '-t', sessionName]);
|
|
218
|
+
|
|
219
|
+
if (result.success) {
|
|
220
|
+
this.sessions.delete(sessionId);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return result.success;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* List all Claude Code sessions.
|
|
228
|
+
*/
|
|
229
|
+
listSessions(): Array<{
|
|
230
|
+
sessionId: string;
|
|
231
|
+
sessionName: string;
|
|
232
|
+
createdAt: number;
|
|
233
|
+
status: string;
|
|
234
|
+
}> {
|
|
235
|
+
const result = this.runTmux([
|
|
236
|
+
'list-sessions',
|
|
237
|
+
'-F', '#{session_name}:#{session_created}',
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
if (!result.success) {
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const sessions: Array<{
|
|
245
|
+
sessionId: string;
|
|
246
|
+
sessionName: string;
|
|
247
|
+
createdAt: number;
|
|
248
|
+
status: string;
|
|
249
|
+
}> = [];
|
|
250
|
+
|
|
251
|
+
for (const line of result.stdout.trim().split('\n')) {
|
|
252
|
+
if (!line || !line.startsWith(this.TMUX_PREFIX)) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const [sessionName, createdAtStr] = line.split(':');
|
|
257
|
+
const sessionId = sessionName.replace(this.TMUX_PREFIX, '');
|
|
258
|
+
const createdAt = parseInt(createdAtStr, 10) * 1000 || 0;
|
|
259
|
+
|
|
260
|
+
sessions.push({
|
|
261
|
+
sessionId,
|
|
262
|
+
sessionName,
|
|
263
|
+
createdAt,
|
|
264
|
+
status: this.getSessionStatus(sessionId),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return sessions;
|
|
269
|
+
}
|
|
270
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|