@tleblancureta/proto 0.1.2 → 0.3.0
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/core-web/src/ProtoApp.tsx +30 -20
- package/core-web/src/components/admin/AdminPanel.tsx +285 -0
- package/core-web/src/components/admin/SystemTab.tsx +167 -0
- package/core-web/src/components/shell/Toolbar.tsx +11 -1
- package/core-web/src/index.ts +1 -0
- package/dist/core-gateway/auth.d.ts +7 -0
- package/dist/core-gateway/auth.d.ts.map +1 -0
- package/dist/core-gateway/auth.js +12 -0
- package/dist/core-gateway/auth.js.map +1 -0
- package/dist/core-gateway/claude-runner.d.ts +10 -0
- package/dist/core-gateway/claude-runner.d.ts.map +1 -0
- package/dist/core-gateway/claude-runner.js +206 -0
- package/dist/core-gateway/claude-runner.js.map +1 -0
- package/dist/core-gateway/config.d.ts +33 -0
- package/dist/core-gateway/config.d.ts.map +1 -0
- package/dist/core-gateway/config.js +84 -0
- package/dist/core-gateway/config.js.map +1 -0
- package/dist/core-gateway/email-sender.d.ts +36 -0
- package/dist/core-gateway/email-sender.d.ts.map +1 -0
- package/dist/core-gateway/email-sender.js +121 -0
- package/dist/core-gateway/email-sender.js.map +1 -0
- package/dist/core-gateway/mail-ingester.d.ts +3 -0
- package/dist/core-gateway/mail-ingester.d.ts.map +1 -0
- package/dist/core-gateway/mail-ingester.js +294 -0
- package/dist/core-gateway/mail-ingester.js.map +1 -0
- package/dist/core-gateway/mail-router.d.ts +18 -0
- package/dist/core-gateway/mail-router.d.ts.map +1 -0
- package/dist/core-gateway/mail-router.js +37 -0
- package/dist/core-gateway/mail-router.js.map +1 -0
- package/dist/core-gateway/mail-threads.d.ts +78 -0
- package/dist/core-gateway/mail-threads.d.ts.map +1 -0
- package/dist/core-gateway/mail-threads.js +100 -0
- package/dist/core-gateway/mail-threads.js.map +1 -0
- package/dist/core-gateway/rate-limiter.d.ts +9 -0
- package/dist/core-gateway/rate-limiter.d.ts.map +1 -0
- package/dist/core-gateway/rate-limiter.js +13 -0
- package/dist/core-gateway/rate-limiter.js.map +1 -0
- package/dist/core-gateway/registry.d.ts +14 -0
- package/dist/core-gateway/registry.d.ts.map +1 -0
- package/dist/core-gateway/registry.js +83 -0
- package/dist/core-gateway/registry.js.map +1 -0
- package/dist/core-gateway/routes/admin.d.ts +3 -0
- package/dist/core-gateway/routes/admin.d.ts.map +1 -0
- package/dist/core-gateway/routes/admin.js +91 -0
- package/dist/core-gateway/routes/admin.js.map +1 -0
- package/dist/core-gateway/routes/chat.d.ts +9 -0
- package/dist/core-gateway/routes/chat.d.ts.map +1 -0
- package/dist/core-gateway/routes/chat.js +149 -0
- package/dist/core-gateway/routes/chat.js.map +1 -0
- package/dist/core-gateway/routes/cron.d.ts +13 -0
- package/dist/core-gateway/routes/cron.d.ts.map +1 -0
- package/dist/core-gateway/routes/cron.js +79 -0
- package/dist/core-gateway/routes/cron.js.map +1 -0
- package/dist/core-gateway/routes/gmail.d.ts +8 -0
- package/dist/core-gateway/routes/gmail.d.ts.map +1 -0
- package/dist/core-gateway/routes/gmail.js +57 -0
- package/dist/core-gateway/routes/gmail.js.map +1 -0
- package/dist/core-gateway/routes/health.d.ts +3 -0
- package/dist/core-gateway/routes/health.d.ts.map +1 -0
- package/dist/core-gateway/routes/health.js +7 -0
- package/dist/core-gateway/routes/health.js.map +1 -0
- package/dist/core-gateway/routes/upload.d.ts +7 -0
- package/dist/core-gateway/routes/upload.d.ts.map +1 -0
- package/dist/core-gateway/routes/upload.js +31 -0
- package/dist/core-gateway/routes/upload.js.map +1 -0
- package/dist/core-gateway/scheduler.d.ts +68 -0
- package/dist/core-gateway/scheduler.d.ts.map +1 -0
- package/dist/core-gateway/scheduler.js +252 -0
- package/dist/core-gateway/scheduler.js.map +1 -0
- package/dist/core-gateway/server.d.ts +17 -0
- package/dist/core-gateway/server.d.ts.map +1 -0
- package/dist/core-gateway/server.js +45 -0
- package/dist/core-gateway/server.js.map +1 -0
- package/dist/core-gateway/session.d.ts +18 -0
- package/dist/core-gateway/session.d.ts.map +1 -0
- package/dist/core-gateway/session.js +178 -0
- package/dist/core-gateway/session.js.map +1 -0
- package/dist/core-gateway/skills.d.ts +4 -0
- package/dist/core-gateway/skills.d.ts.map +1 -0
- package/dist/core-gateway/skills.js +100 -0
- package/dist/core-gateway/skills.js.map +1 -0
- package/dist/core-gateway/supabase.d.ts +3 -0
- package/dist/core-gateway/supabase.d.ts.map +1 -0
- package/dist/core-gateway/supabase.js +18 -0
- package/dist/core-gateway/supabase.js.map +1 -0
- package/dist/core-web/src/ProtoApp.d.ts.map +1 -1
- package/dist/core-web/src/ProtoApp.js +5 -3
- package/dist/core-web/src/ProtoApp.js.map +1 -1
- package/dist/core-web/src/components/admin/AdminPanel.d.ts +7 -0
- package/dist/core-web/src/components/admin/AdminPanel.d.ts.map +1 -0
- package/dist/core-web/src/components/admin/AdminPanel.js +112 -0
- package/dist/core-web/src/components/admin/AdminPanel.js.map +1 -0
- package/dist/core-web/src/components/admin/SystemTab.d.ts +2 -0
- package/dist/core-web/src/components/admin/SystemTab.d.ts.map +1 -0
- package/dist/core-web/src/components/admin/SystemTab.js +25 -0
- package/dist/core-web/src/components/admin/SystemTab.js.map +1 -0
- package/dist/core-web/src/components/shell/Toolbar.d.ts.map +1 -1
- package/dist/core-web/src/components/shell/Toolbar.js +4 -2
- package/dist/core-web/src/components/shell/Toolbar.js.map +1 -1
- package/dist/core-web/src/index.d.ts +1 -0
- package/dist/core-web/src/index.d.ts.map +1 -1
- package/dist/core-web/src/index.js +1 -0
- package/dist/core-web/src/index.js.map +1 -1
- package/dist/gateway.d.ts +3 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +3 -0
- package/dist/gateway.js.map +1 -0
- package/package.json +22 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { config, CLAUDE_MODEL, MAX_TURNS, TIMEOUT_SECONDS } from './config.js';
|
|
5
|
+
import { prepareSession, syncCredentialsBack } from './session.js';
|
|
6
|
+
function buildAllowedTools() {
|
|
7
|
+
const baseTools = ['Bash', 'Read'];
|
|
8
|
+
const mcpTools = Object.keys(config.mcp_servers).map(name => `mcp__${name}`);
|
|
9
|
+
return [...baseTools, ...mcpTools].join(',');
|
|
10
|
+
}
|
|
11
|
+
function prependTimestamp(message) {
|
|
12
|
+
const now = new Date();
|
|
13
|
+
const formatter = new Intl.DateTimeFormat('es-CL', {
|
|
14
|
+
timeZone: config.timezone,
|
|
15
|
+
day: '2-digit', month: '2-digit', year: 'numeric',
|
|
16
|
+
hour: '2-digit', minute: '2-digit',
|
|
17
|
+
});
|
|
18
|
+
return `[${formatter.format(now)} (${config.timezone})]\n\n${message}`;
|
|
19
|
+
}
|
|
20
|
+
/** Persist Claude's session_id so we can --resume on next request */
|
|
21
|
+
function getSessionFile(sessionDir) {
|
|
22
|
+
return join(sessionDir, '.claude-session-id');
|
|
23
|
+
}
|
|
24
|
+
function getSavedSessionId(sessionDir) {
|
|
25
|
+
const file = getSessionFile(sessionDir);
|
|
26
|
+
if (existsSync(file))
|
|
27
|
+
return readFileSync(file, 'utf-8').trim();
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function saveSessionId(sessionDir, sessionId) {
|
|
31
|
+
writeFileSync(getSessionFile(sessionDir), sessionId);
|
|
32
|
+
}
|
|
33
|
+
function buildCmd(message, promptFile, sessionDir, outputFormat, mcpConfigFile) {
|
|
34
|
+
const savedClaudeSession = getSavedSessionId(sessionDir);
|
|
35
|
+
const cmd = [
|
|
36
|
+
'claude',
|
|
37
|
+
'-p', message,
|
|
38
|
+
'--model', CLAUDE_MODEL,
|
|
39
|
+
'--allowedTools', buildAllowedTools(),
|
|
40
|
+
'--max-turns', String(MAX_TURNS),
|
|
41
|
+
'--mcp-config', mcpConfigFile,
|
|
42
|
+
];
|
|
43
|
+
// Always include system prompt (works with both new and resumed sessions)
|
|
44
|
+
cmd.push('--system-prompt-file', promptFile);
|
|
45
|
+
if (savedClaudeSession) {
|
|
46
|
+
// Resume existing session (keeps conversation history)
|
|
47
|
+
cmd.push('--resume', savedClaudeSession);
|
|
48
|
+
}
|
|
49
|
+
// Output format: always json for blocking (to capture session_id), stream-json for streaming
|
|
50
|
+
cmd.push('--output-format', outputFormat);
|
|
51
|
+
if (outputFormat === 'stream-json') {
|
|
52
|
+
cmd.push('--verbose');
|
|
53
|
+
}
|
|
54
|
+
return cmd;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Execute a Claude Code turn (blocking, returns full response).
|
|
58
|
+
*/
|
|
59
|
+
export async function runClaude(request) {
|
|
60
|
+
const { sessionId, sessionDir, env, promptFile, mcpConfigFile, claudeConfigDir } = await prepareSession(request);
|
|
61
|
+
const message = prependTimestamp(request.message);
|
|
62
|
+
const cmd = buildCmd(message, promptFile, sessionDir, 'json', mcpConfigFile);
|
|
63
|
+
const start = Date.now();
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
const proc = spawn(cmd[0], cmd.slice(1), {
|
|
66
|
+
cwd: sessionDir,
|
|
67
|
+
env,
|
|
68
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
69
|
+
});
|
|
70
|
+
let stdout = '';
|
|
71
|
+
let stderr = '';
|
|
72
|
+
let lastActivity = Date.now();
|
|
73
|
+
proc.stdout.on('data', (data) => { stdout += data.toString(); lastActivity = Date.now(); });
|
|
74
|
+
proc.stderr.on('data', (data) => { stderr += data.toString(); lastActivity = Date.now(); });
|
|
75
|
+
// Idle timeout (no absolute deadline)
|
|
76
|
+
const timer = setInterval(() => {
|
|
77
|
+
if (Date.now() - lastActivity > TIMEOUT_SECONDS * 1000) {
|
|
78
|
+
proc.kill('SIGKILL');
|
|
79
|
+
clearInterval(timer);
|
|
80
|
+
reject(new Error('Claude Code idle timeout'));
|
|
81
|
+
}
|
|
82
|
+
}, 5000);
|
|
83
|
+
proc.on('close', (code) => {
|
|
84
|
+
clearInterval(timer);
|
|
85
|
+
const durationMs = Date.now() - start;
|
|
86
|
+
syncCredentialsBack(claudeConfigDir);
|
|
87
|
+
if (code !== 0) {
|
|
88
|
+
reject(new Error(`Claude Code error (rc=${code}): ${stderr.slice(0, 500)}`));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Parse JSON response to get session_id
|
|
92
|
+
try {
|
|
93
|
+
const result = JSON.parse(stdout.trim());
|
|
94
|
+
if (result.session_id)
|
|
95
|
+
saveSessionId(sessionDir, result.session_id);
|
|
96
|
+
resolve({
|
|
97
|
+
response: result.result || stdout.trim(),
|
|
98
|
+
session_id: sessionId,
|
|
99
|
+
duration_ms: durationMs,
|
|
100
|
+
cost_usd: result.total_cost_usd,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
resolve({
|
|
105
|
+
response: stdout.trim(),
|
|
106
|
+
session_id: sessionId,
|
|
107
|
+
duration_ms: durationMs,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Execute a Claude Code turn and yield SSE events.
|
|
115
|
+
*/
|
|
116
|
+
export async function* streamClaude(request) {
|
|
117
|
+
const { sessionId, sessionDir, env, promptFile, mcpConfigFile, claudeConfigDir } = await prepareSession(request);
|
|
118
|
+
const message = prependTimestamp(request.message);
|
|
119
|
+
const cmd = buildCmd(message, promptFile, sessionDir, 'stream-json', mcpConfigFile);
|
|
120
|
+
const start = Date.now();
|
|
121
|
+
yield { type: 'init', session_id: sessionId };
|
|
122
|
+
const proc = spawn(cmd[0], cmd.slice(1), {
|
|
123
|
+
cwd: sessionDir,
|
|
124
|
+
env,
|
|
125
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
126
|
+
});
|
|
127
|
+
// Idle timeout: kill the process only if it goes silent for TIMEOUT_SECONDS.
|
|
128
|
+
// No absolute deadline — long tool chains (multi-scrape, etc) are OK as long
|
|
129
|
+
// as they keep producing output.
|
|
130
|
+
let lastActivity = Date.now();
|
|
131
|
+
const idleCheck = setInterval(() => {
|
|
132
|
+
if (Date.now() - lastActivity > TIMEOUT_SECONDS * 1000) {
|
|
133
|
+
proc.kill('SIGKILL');
|
|
134
|
+
clearInterval(idleCheck);
|
|
135
|
+
}
|
|
136
|
+
}, 5000);
|
|
137
|
+
let buffer = '';
|
|
138
|
+
try {
|
|
139
|
+
for await (const chunk of proc.stdout) {
|
|
140
|
+
lastActivity = Date.now();
|
|
141
|
+
buffer += chunk.toString();
|
|
142
|
+
const lines = buffer.split('\n');
|
|
143
|
+
buffer = lines.pop() || '';
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
const trimmed = line.trim();
|
|
146
|
+
if (!trimmed)
|
|
147
|
+
continue;
|
|
148
|
+
let event;
|
|
149
|
+
try {
|
|
150
|
+
event = JSON.parse(trimmed);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const etype = event.type || '';
|
|
156
|
+
if (etype === 'assistant') {
|
|
157
|
+
for (const block of event.message?.content || []) {
|
|
158
|
+
if (block.type === 'thinking') {
|
|
159
|
+
yield { type: 'thinking', text: block.thinking || '' };
|
|
160
|
+
}
|
|
161
|
+
else if (block.type === 'text') {
|
|
162
|
+
yield { type: 'text', text: block.text };
|
|
163
|
+
}
|
|
164
|
+
else if (block.type === 'tool_use') {
|
|
165
|
+
yield { type: 'tool_use', tool: block.name || '', args: block.input || {} };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (etype === 'tool_result') {
|
|
170
|
+
let content = event.content || '';
|
|
171
|
+
if (Array.isArray(content)) {
|
|
172
|
+
content = content.filter((c) => c.type === 'text').map((c) => c.text).join('\n');
|
|
173
|
+
}
|
|
174
|
+
yield { type: 'tool_result', tool: event.tool_use_id || '', content: String(content).slice(0, 2000) };
|
|
175
|
+
}
|
|
176
|
+
else if (etype === 'result') {
|
|
177
|
+
const durationMs = Date.now() - start;
|
|
178
|
+
const costUsd = event.total_cost_usd;
|
|
179
|
+
// Save session ID for --resume on next request
|
|
180
|
+
if (event.session_id)
|
|
181
|
+
saveSessionId(sessionDir, event.session_id);
|
|
182
|
+
yield {
|
|
183
|
+
type: 'result',
|
|
184
|
+
text: event.result || '',
|
|
185
|
+
session_id: sessionId,
|
|
186
|
+
duration_ms: durationMs,
|
|
187
|
+
cost_usd: costUsd,
|
|
188
|
+
num_turns: event.num_turns,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
proc.kill('SIGKILL');
|
|
196
|
+
yield { type: 'error', message: String(err) };
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
clearInterval(idleCheck);
|
|
200
|
+
// Kill the process if still running (e.g. consumer broke out of the loop)
|
|
201
|
+
if (!proc.killed)
|
|
202
|
+
proc.kill('SIGTERM');
|
|
203
|
+
syncCredentialsBack(claudeConfigDir);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=claude-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-runner.js","sourceRoot":"","sources":["../../core-gateway/claude-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface MCPServerStdio {
|
|
2
|
+
type: 'stdio';
|
|
3
|
+
command: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
env: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
interface MCPServerHTTP {
|
|
8
|
+
type: 'http';
|
|
9
|
+
url: string;
|
|
10
|
+
}
|
|
11
|
+
type MCPServerConfig = MCPServerStdio | MCPServerHTTP;
|
|
12
|
+
interface ProjectConfig {
|
|
13
|
+
name: string;
|
|
14
|
+
display_name: string;
|
|
15
|
+
timezone: string;
|
|
16
|
+
mcp_servers: Record<string, MCPServerConfig>;
|
|
17
|
+
prompts: Record<string, string>;
|
|
18
|
+
skills_dir: string;
|
|
19
|
+
agents_dir: string;
|
|
20
|
+
channel_excluded_skills: Record<string, Set<string>>;
|
|
21
|
+
always_allowed_tools: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare const APP_ROOT: string;
|
|
24
|
+
/** Resolve a relative path against APP_ROOT (or return absolute paths as-is). */
|
|
25
|
+
export declare function resolveAppPath(relative: string): string;
|
|
26
|
+
export declare const config: ProjectConfig;
|
|
27
|
+
export declare const INTERNAL_SECRET: string;
|
|
28
|
+
export declare const MAX_TURNS: number;
|
|
29
|
+
export declare const TIMEOUT_SECONDS: number;
|
|
30
|
+
export declare const CLAUDE_MODEL: string;
|
|
31
|
+
export declare const PORT: number;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../core-gateway/config.ts"],"names":[],"mappings":"AAOA,UAAU,cAAc;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC5B;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,KAAK,eAAe,GAAG,cAAc,GAAG,aAAa,CAAA;AAErD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;IACpD,oBAAoB,EAAE,MAAM,EAAE,CAAA;CAC/B;AAmBD,eAAO,MAAM,QAAQ,QAAmB,CAAA;AAExC,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAwDD,eAAO,MAAM,MAAM,eAAe,CAAA;AAGlC,eAAO,MAAM,eAAe,QAAwC,CAAA;AACpE,eAAO,MAAM,SAAS,QAAqD,CAAA;AAC3E,eAAO,MAAM,eAAe,QAAoD,CAAA;AAChF,eAAO,MAAM,YAAY,QAAkD,CAAA;AAC3E,eAAO,MAAM,IAAI,QAA2C,CAAA"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname, isAbsolute } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import yaml from 'yaml';
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
/**
|
|
7
|
+
* APP_ROOT: the directory of the proto app being served (contains project.yaml + app/).
|
|
8
|
+
* Resolution order:
|
|
9
|
+
* 1. PROTO_APP_ROOT env var (absolute or relative to cwd)
|
|
10
|
+
* 2. cwd, if it contains project.yaml
|
|
11
|
+
* 3. Legacy fallback: 3 levels up from this file (backward compat for pre-carve-out layout)
|
|
12
|
+
*/
|
|
13
|
+
function resolveAppRoot() {
|
|
14
|
+
const envRoot = process.env.PROTO_APP_ROOT;
|
|
15
|
+
if (envRoot) {
|
|
16
|
+
return isAbsolute(envRoot) ? envRoot : resolve(process.cwd(), envRoot);
|
|
17
|
+
}
|
|
18
|
+
const cwdConfig = resolve(process.cwd(), 'project.yaml');
|
|
19
|
+
if (existsSync(cwdConfig))
|
|
20
|
+
return process.cwd();
|
|
21
|
+
return resolve(__dirname, '..', '..', '..');
|
|
22
|
+
}
|
|
23
|
+
export const APP_ROOT = resolveAppRoot();
|
|
24
|
+
/** Resolve a relative path against APP_ROOT (or return absolute paths as-is). */
|
|
25
|
+
export function resolveAppPath(relative) {
|
|
26
|
+
return isAbsolute(relative) ? relative : resolve(APP_ROOT, relative);
|
|
27
|
+
}
|
|
28
|
+
function loadConfig() {
|
|
29
|
+
const configPath = process.env.PROJECT_CONFIG || 'project.yaml';
|
|
30
|
+
const resolved = resolveAppPath(configPath);
|
|
31
|
+
if (!existsSync(resolved)) {
|
|
32
|
+
return {
|
|
33
|
+
name: 'proto',
|
|
34
|
+
display_name: 'Proto',
|
|
35
|
+
timezone: 'UTC',
|
|
36
|
+
mcp_servers: {},
|
|
37
|
+
prompts: { default: 'prompts/default.md' },
|
|
38
|
+
skills_dir: 'skills',
|
|
39
|
+
agents_dir: 'agents',
|
|
40
|
+
channel_excluded_skills: {},
|
|
41
|
+
always_allowed_tools: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const raw = yaml.parse(readFileSync(resolved, 'utf-8')) || {};
|
|
45
|
+
const mcpRaw = raw.mcp?.servers || {};
|
|
46
|
+
const mcp_servers = {};
|
|
47
|
+
for (const [name, cfg] of Object.entries(mcpRaw)) {
|
|
48
|
+
if (cfg.type === 'http') {
|
|
49
|
+
mcp_servers[name] = { type: 'http', url: cfg.url };
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
mcp_servers[name] = {
|
|
53
|
+
type: 'stdio',
|
|
54
|
+
command: cfg.command || 'node',
|
|
55
|
+
args: cfg.args || [],
|
|
56
|
+
env: cfg.env || {},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const skillsRaw = raw.skills || {};
|
|
61
|
+
const excluded = {};
|
|
62
|
+
for (const [channel, skills] of Object.entries(skillsRaw.excluded_channels || {})) {
|
|
63
|
+
excluded[channel] = new Set(skills);
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
name: raw.name || 'proto',
|
|
67
|
+
display_name: raw.display_name || 'Proto',
|
|
68
|
+
timezone: raw.timezone || 'UTC',
|
|
69
|
+
mcp_servers,
|
|
70
|
+
prompts: raw.prompts || { default: 'prompts/default.md' },
|
|
71
|
+
skills_dir: skillsRaw.dir || 'skills',
|
|
72
|
+
agents_dir: raw.agents?.dir || 'agents',
|
|
73
|
+
channel_excluded_skills: excluded,
|
|
74
|
+
always_allowed_tools: raw.always_allowed_tools || [],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export const config = loadConfig();
|
|
78
|
+
// Gateway env vars
|
|
79
|
+
export const INTERNAL_SECRET = process.env.INTERNAL_API_SECRET || '';
|
|
80
|
+
export const MAX_TURNS = parseInt(process.env.CLAUDE_MAX_TURNS || '25', 10);
|
|
81
|
+
export const TIMEOUT_SECONDS = parseInt(process.env.CLAUDE_TIMEOUT || '120', 10);
|
|
82
|
+
export const CLAUDE_MODEL = process.env.CLAUDE_MODEL || 'claude-sonnet-4-6';
|
|
83
|
+
export const PORT = parseInt(process.env.PORT || '8090', 10);
|
|
84
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../core-gateway/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface SendResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
messageId?: string;
|
|
4
|
+
threadId?: string;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SendOptions {
|
|
8
|
+
/** Company this mail belongs to — required so the thread is scoped correctly. */
|
|
9
|
+
companyId: string;
|
|
10
|
+
/** Recipient address. */
|
|
11
|
+
to: string;
|
|
12
|
+
/** Subject line. "Re: " will be added automatically if replying. */
|
|
13
|
+
subject: string;
|
|
14
|
+
/** text/plain body. */
|
|
15
|
+
body: string;
|
|
16
|
+
/** If replying to an existing thread, pass its id. */
|
|
17
|
+
threadId?: string;
|
|
18
|
+
/** If replying, the parent Message-ID (goes into In-Reply-To / References). */
|
|
19
|
+
inReplyTo?: string;
|
|
20
|
+
/** If this mail originates from a scheduled task, link it. */
|
|
21
|
+
sourceTaskId?: string | null;
|
|
22
|
+
/** Optional explicit session_key for thread creation. */
|
|
23
|
+
sessionKey?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Send mail from the system account and record it as an outbound
|
|
27
|
+
* thread message. Returns the generated Message-ID + thread id for
|
|
28
|
+
* downstream bookkeeping. Never throws.
|
|
29
|
+
*/
|
|
30
|
+
export declare function sendSystemMail(opts: SendOptions): Promise<SendResult>;
|
|
31
|
+
export declare function isMailConfigured(): boolean;
|
|
32
|
+
/** @deprecated Use sendSystemMail instead. */
|
|
33
|
+
export declare const sendFromHermes: typeof sendSystemMail;
|
|
34
|
+
/** @deprecated Use isMailConfigured instead. */
|
|
35
|
+
export declare const isHermesMailConfigured: typeof isMailConfigured;
|
|
36
|
+
//# sourceMappingURL=email-sender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-sender.d.ts","sourceRoot":"","sources":["../../core-gateway/email-sender.ts"],"names":[],"mappings":"AAgDA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAA;IACf,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA0E3E;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,8CAA8C;AAC9C,eAAO,MAAM,cAAc,uBAAiB,CAAA;AAC5C,gDAAgD;AAChD,eAAO,MAAM,sBAAsB,yBAAmB,CAAA"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
import { config } from './config.js';
|
|
3
|
+
import { createThread, findThreadByMessageId, generateMessageId, recordMessage, } from './mail-threads.js';
|
|
4
|
+
/**
|
|
5
|
+
* System mailer. Single SMTP identity used for all outbound
|
|
6
|
+
* notifications across all companies. Distinct from the user's personal
|
|
7
|
+
* Gmail (which lives in gmail_tokens and is only used when the agent
|
|
8
|
+
* reads/writes the user's own inbox as a tool).
|
|
9
|
+
*
|
|
10
|
+
* Every outbound also records a row in mail_messages so inbound replies
|
|
11
|
+
* can be routed back to the same thread/session_key.
|
|
12
|
+
*/
|
|
13
|
+
let transporter = null;
|
|
14
|
+
let cachedFrom = null;
|
|
15
|
+
let cachedReplyTo = null;
|
|
16
|
+
function getTransporter() {
|
|
17
|
+
if (transporter)
|
|
18
|
+
return transporter;
|
|
19
|
+
const host = process.env.MAIL_SMTP_HOST || process.env.HERMES_SMTP_HOST;
|
|
20
|
+
const port = parseInt(process.env.MAIL_SMTP_PORT || process.env.HERMES_SMTP_PORT || '587', 10);
|
|
21
|
+
const user = process.env.MAIL_SMTP_USER || process.env.HERMES_SMTP_USER;
|
|
22
|
+
const pass = process.env.MAIL_SMTP_PASS || process.env.HERMES_SMTP_PASS;
|
|
23
|
+
if (!host || !user || !pass)
|
|
24
|
+
return null;
|
|
25
|
+
transporter = nodemailer.createTransport({
|
|
26
|
+
host,
|
|
27
|
+
port,
|
|
28
|
+
secure: port === 465,
|
|
29
|
+
auth: { user, pass },
|
|
30
|
+
});
|
|
31
|
+
const fromEnv = process.env.MAIL_SMTP_FROM || process.env.HERMES_SMTP_FROM;
|
|
32
|
+
cachedFrom = fromEnv || `${config.display_name} <${user}>`;
|
|
33
|
+
cachedReplyTo = process.env.MAIL_SMTP_REPLY_TO || process.env.HERMES_SMTP_REPLY_TO || cachedFrom;
|
|
34
|
+
return transporter;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Send mail from the system account and record it as an outbound
|
|
38
|
+
* thread message. Returns the generated Message-ID + thread id for
|
|
39
|
+
* downstream bookkeeping. Never throws.
|
|
40
|
+
*/
|
|
41
|
+
export async function sendSystemMail(opts) {
|
|
42
|
+
const t = getTransporter();
|
|
43
|
+
if (!t) {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
error: 'MAIL_SMTP_* env vars not configured. Set MAIL_SMTP_HOST, _USER, _PASS (and optionally _PORT, _FROM, _REPLY_TO).',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Thread resolution: reuse an existing one or create a new one
|
|
50
|
+
let thread = null;
|
|
51
|
+
if (opts.threadId) {
|
|
52
|
+
// Caller passed an explicit thread — look it up via its latest message
|
|
53
|
+
// (simplest path: findThreadByMessageId of inReplyTo)
|
|
54
|
+
if (opts.inReplyTo) {
|
|
55
|
+
thread = await findThreadByMessageId(opts.inReplyTo);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!thread) {
|
|
59
|
+
try {
|
|
60
|
+
thread = await createThread({
|
|
61
|
+
companyId: opts.companyId,
|
|
62
|
+
subject: opts.subject,
|
|
63
|
+
initiatedBy: 'out',
|
|
64
|
+
sourceTaskId: opts.sourceTaskId,
|
|
65
|
+
externalAddress: opts.to,
|
|
66
|
+
sessionKey: opts.sessionKey,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
return { ok: false, error: `createThread: ${err?.message || err}` };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Generate a stable Message-ID we control so dedup + threading work
|
|
74
|
+
const messageId = generateMessageId();
|
|
75
|
+
const subject = opts.inReplyTo && !/^\s*re:/i.test(opts.subject)
|
|
76
|
+
? `Re: ${opts.subject}`
|
|
77
|
+
: opts.subject;
|
|
78
|
+
try {
|
|
79
|
+
await t.sendMail({
|
|
80
|
+
from: cachedFrom,
|
|
81
|
+
replyTo: cachedReplyTo,
|
|
82
|
+
to: opts.to,
|
|
83
|
+
subject,
|
|
84
|
+
text: opts.body,
|
|
85
|
+
messageId,
|
|
86
|
+
...(opts.inReplyTo
|
|
87
|
+
? { inReplyTo: opts.inReplyTo, references: opts.inReplyTo }
|
|
88
|
+
: {}),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return { ok: false, error: err?.message || String(err) };
|
|
93
|
+
}
|
|
94
|
+
// Record the outbound message
|
|
95
|
+
try {
|
|
96
|
+
await recordMessage({
|
|
97
|
+
threadId: thread.id,
|
|
98
|
+
companyId: opts.companyId,
|
|
99
|
+
direction: 'out',
|
|
100
|
+
messageId,
|
|
101
|
+
inReplyTo: opts.inReplyTo ?? null,
|
|
102
|
+
fromAddress: cachedFrom,
|
|
103
|
+
toAddress: opts.to,
|
|
104
|
+
subject,
|
|
105
|
+
body: opts.body,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
// Mail already sent; surface but don't rollback
|
|
110
|
+
return { ok: true, messageId, threadId: thread.id, error: `record failed: ${err?.message}` };
|
|
111
|
+
}
|
|
112
|
+
return { ok: true, messageId, threadId: thread.id };
|
|
113
|
+
}
|
|
114
|
+
export function isMailConfigured() {
|
|
115
|
+
return getTransporter() !== null;
|
|
116
|
+
}
|
|
117
|
+
/** @deprecated Use sendSystemMail instead. */
|
|
118
|
+
export const sendFromHermes = sendSystemMail;
|
|
119
|
+
/** @deprecated Use isMailConfigured instead. */
|
|
120
|
+
export const isHermesMailConfigured = isMailConfigured;
|
|
121
|
+
//# sourceMappingURL=email-sender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-sender.js","sourceRoot":"","sources":["../../core-gateway/email-sender.ts"],"names":[],"mappings":"AAAA,OAAO,UAAgC,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,GAEd,MAAM,mBAAmB,CAAA;AAE1B;;;;;;;;GAQG;AAEH,IAAI,WAAW,GAAuB,IAAI,CAAA;AAC1C,IAAI,UAAU,GAAkB,IAAI,CAAA;AACpC,IAAI,aAAa,GAAkB,IAAI,CAAA;AAEvC,SAAS,cAAc;IACrB,IAAI,WAAW;QAAE,OAAO,WAAW,CAAA;IAEnC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;IACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,KAAK,EAAE,EAAE,CAAC,CAAA;IAC9F,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;IACvE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;IAEvE,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAExC,WAAW,GAAG,UAAU,CAAC,eAAe,CAAC;QACvC,IAAI;QACJ,IAAI;QACJ,MAAM,EAAE,IAAI,KAAK,GAAG;QACpB,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;KACrB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;IAC1E,UAAU,GAAG,OAAO,IAAI,GAAG,MAAM,CAAC,YAAY,KAAK,IAAI,GAAG,CAAA;IAC1D,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,UAAU,CAAA;IAEhG,OAAO,WAAW,CAAA;AACpB,CAAC;AA4BD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAiB;IACpD,MAAM,CAAC,GAAG,cAAc,EAAE,CAAA;IAC1B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,iHAAiH;SACzH,CAAA;IACH,CAAC;IAED,+DAA+D;IAC/D,IAAI,MAAM,GAAsB,IAAI,CAAA;IACpC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,uEAAuE;QACvE,sDAAsD;QACtD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,YAAY,CAAC;gBAC1B,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,WAAW,EAAE,KAAK;gBAClB,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,eAAe,EAAE,IAAI,CAAC,EAAE;gBACxB,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,GAAG,EAAE,OAAO,IAAI,GAAG,EAAE,EAAE,CAAA;QACrE,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAA;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;QAC9D,CAAC,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE;QACvB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAA;IAEhB,IAAI,CAAC;QACH,MAAM,CAAC,CAAC,QAAQ,CAAC;YACf,IAAI,EAAE,UAAW;YACjB,OAAO,EAAE,aAAc;YACvB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS;YACT,GAAG,CAAC,IAAI,CAAC,SAAS;gBAChB,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE;gBAC3D,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAA;IAC1D,CAAC;IAED,8BAA8B;IAC9B,IAAI,CAAC;QACH,MAAM,aAAa,CAAC;YAClB,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,KAAK;YAChB,SAAS;YACT,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;YACjC,WAAW,EAAE,UAAW;YACxB,SAAS,EAAE,IAAI,CAAC,EAAE;YAClB,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,gDAAgD;QAChD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,kBAAkB,GAAG,EAAE,OAAO,EAAE,EAAE,CAAA;IAC9F,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,CAAA;AACrD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,cAAc,EAAE,KAAK,IAAI,CAAA;AAClC,CAAC;AAED,8CAA8C;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAA;AAC5C,gDAAgD;AAChD,MAAM,CAAC,MAAM,sBAAsB,GAAG,gBAAgB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-ingester.d.ts","sourceRoot":"","sources":["../../core-gateway/mail-ingester.ts"],"names":[],"mappings":"AAsEA,wBAAgB,iBAAiB,IAAI,IAAI,CAiBxC;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
|