@realtimex/email-automator 2.3.6 → 2.3.8

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/README.md CHANGED
@@ -60,7 +60,17 @@ npx @realtimex/email-automator-setup
60
60
  npx @realtimex/email-automator-deploy
61
61
 
62
62
  # Start Email Automator
63
- npx @realtimex/email-automator
63
+ npx @realtimex/email-automator --port 3004
64
+ ```
65
+
66
+ ### Option 1b: Global Install
67
+
68
+ ```bash
69
+ # Install globally
70
+ npm install -g @realtimex/email-automator
71
+
72
+ # Then run directly
73
+ email-automator --port 3004
64
74
  ```
65
75
 
66
76
  ### Option 2: Clone and Install
package/api/server.ts CHANGED
@@ -69,8 +69,8 @@ app.use('/api', routes);
69
69
 
70
70
  // Robust resolution for static assets (dist folder)
71
71
  function getDistPath() {
72
- // 1. Check environment variable override
73
- if (process.env.ELECTRON_STATIC_PATH && existsSync(process.env.ELECTRON_STATIC_PATH)) {
72
+ // 1. Check environment variable override (must contain index.html)
73
+ if (process.env.ELECTRON_STATIC_PATH && existsSync(join(process.env.ELECTRON_STATIC_PATH, 'index.html'))) {
74
74
  return process.env.ELECTRON_STATIC_PATH;
75
75
  }
76
76
 
@@ -1,5 +1,4 @@
1
1
  import OpenAI from 'openai';
2
- import Instructor from '@instructor-ai/instructor';
3
2
  import { z } from 'zod';
4
3
  import { config } from '../config/index.js';
5
4
  import { createLogger } from '../utils/logger.js';
@@ -48,14 +47,14 @@ export interface EmailContext {
48
47
  }
49
48
 
50
49
  export class IntelligenceService {
51
- private client: any;
52
- private model: string;
50
+ private client: OpenAI | null = null;
51
+ private model: string = 'gpt-4o-mini';
53
52
  private isConfigured: boolean = false;
54
53
 
55
54
  constructor(overrides?: { model?: string; baseUrl?: string; apiKey?: string }) {
56
55
  const apiKey = overrides?.apiKey || config.llm.apiKey;
57
56
  const baseUrl = overrides?.baseUrl || config.llm.baseUrl;
58
- this.model = overrides?.model || config.llm.model;
57
+ this.model = overrides?.model || config.llm.model || 'gpt-4o-mini';
59
58
 
60
59
  // Allow local LLM servers (LM Studio, Ollama) or custom endpoints that don't need API keys
61
60
  // We assume any custom baseUrl might be a local/private instance.
@@ -67,16 +66,11 @@ export class IntelligenceService {
67
66
  }
68
67
 
69
68
  try {
70
- const oai = new OpenAI({
69
+ this.client = new OpenAI({
71
70
  apiKey: apiKey || 'not-needed-for-local', // Placeholder for local LLMs
72
71
  baseURL: baseUrl,
73
72
  });
74
73
 
75
- this.client = Instructor({
76
- client: oai,
77
- mode: 'MD_JSON',
78
- });
79
-
80
74
  this.isConfigured = true;
81
75
  logger.info('Intelligence service initialized', { model: this.model, baseUrl: baseUrl || 'default' });
82
76
  } catch (error) {
@@ -117,12 +111,16 @@ Do NOT include any greetings, chatter, or special tokens like <|channel|> in you
117
111
  Return ONLY a valid JSON object.
118
112
 
119
113
  Definitions for Categories:
120
- - "important": Work-related, urgent, from known contacts
114
+ - "spam": Junk, suspicious, unwanted
115
+ - "newsletter": Subscribed content, digests
121
116
  - "promotional": Marketing, sales, discounts
122
117
  - "transactional": Receipts, shipping, confirmations
123
118
  - "social": LinkedIn, friends, social updates
124
- - "newsletter": Subscribed content
125
- - "spam": Junk, suspicious
119
+ - "support": Help desk, customer service
120
+ - "client": Business clients, customers
121
+ - "internal": Company internal communications
122
+ - "personal": Friends, family, personal matters
123
+ - "other": Anything else
126
124
 
127
125
  Context:
128
126
  - Current Date: ${new Date().toISOString()}
@@ -162,13 +160,14 @@ REQUIRED JSON STRUCTURE:
162
160
 
163
161
  let rawResponse = '';
164
162
  try {
165
- // Using raw completion call to handle garbage characters and strip tokens manually
166
- const response = await this.client.client.chat.completions.create({
163
+ // Request JSON response format for reliable parsing
164
+ const response = await this.client!.chat.completions.create({
167
165
  model: this.model,
168
166
  messages: [
169
167
  { role: 'system', content: systemPrompt },
170
168
  { role: 'user', content: cleanedContent || '[Empty email body]' },
171
169
  ],
170
+ response_format: { type: 'json_object' },
172
171
  temperature: 0.1,
173
172
  });
174
173
 
@@ -223,7 +222,7 @@ REQUIRED JSON STRUCTURE:
223
222
  }
224
223
 
225
224
  try {
226
- const response = await this.client.chat.completions.create({
225
+ const response = await this.client!.chat.completions.create({
227
226
  model: this.model,
228
227
  messages: [
229
228
  {
@@ -257,7 +256,7 @@ Please write a reply.`,
257
256
  }
258
257
 
259
258
  try {
260
- await this.client.chat.completions.create({
259
+ await this.client!.chat.completions.create({
261
260
  model: this.model,
262
261
  messages: [{ role: 'user', content: 'Say "Connection Successful"' }],
263
262
  max_tokens: 5,
@@ -58,8 +58,8 @@ app.use('/api', apiRateLimit);
58
58
  app.use('/api', routes);
59
59
  // Robust resolution for static assets (dist folder)
60
60
  function getDistPath() {
61
- // 1. Check environment variable override
62
- if (process.env.ELECTRON_STATIC_PATH && existsSync(process.env.ELECTRON_STATIC_PATH)) {
61
+ // 1. Check environment variable override (must contain index.html)
62
+ if (process.env.ELECTRON_STATIC_PATH && existsSync(join(process.env.ELECTRON_STATIC_PATH, 'index.html'))) {
63
63
  return process.env.ELECTRON_STATIC_PATH;
64
64
  }
65
65
  // 2. Try to find dist relative to packageRoot (from config)
@@ -1,5 +1,4 @@
1
1
  import OpenAI from 'openai';
2
- import Instructor from '@instructor-ai/instructor';
3
2
  import { z } from 'zod';
4
3
  import { config } from '../config/index.js';
5
4
  import { createLogger } from '../utils/logger.js';
@@ -26,13 +25,13 @@ export const EmailAnalysisSchema = z.object({
26
25
  .describe('Action items mentioned in the email'),
27
26
  });
28
27
  export class IntelligenceService {
29
- client;
30
- model;
28
+ client = null;
29
+ model = 'gpt-4o-mini';
31
30
  isConfigured = false;
32
31
  constructor(overrides) {
33
32
  const apiKey = overrides?.apiKey || config.llm.apiKey;
34
33
  const baseUrl = overrides?.baseUrl || config.llm.baseUrl;
35
- this.model = overrides?.model || config.llm.model;
34
+ this.model = overrides?.model || config.llm.model || 'gpt-4o-mini';
36
35
  // Allow local LLM servers (LM Studio, Ollama) or custom endpoints that don't need API keys
37
36
  // We assume any custom baseUrl might be a local/private instance.
38
37
  const isCustomEndpoint = baseUrl && !baseUrl.includes('api.openai.com');
@@ -41,14 +40,10 @@ export class IntelligenceService {
41
40
  return;
42
41
  }
43
42
  try {
44
- const oai = new OpenAI({
43
+ this.client = new OpenAI({
45
44
  apiKey: apiKey || 'not-needed-for-local', // Placeholder for local LLMs
46
45
  baseURL: baseUrl,
47
46
  });
48
- this.client = Instructor({
49
- client: oai,
50
- mode: 'MD_JSON',
51
- });
52
47
  this.isConfigured = true;
53
48
  logger.info('Intelligence service initialized', { model: this.model, baseUrl: baseUrl || 'default' });
54
49
  }
@@ -89,12 +84,16 @@ Do NOT include any greetings, chatter, or special tokens like <|channel|> in you
89
84
  Return ONLY a valid JSON object.
90
85
 
91
86
  Definitions for Categories:
92
- - "important": Work-related, urgent, from known contacts
87
+ - "spam": Junk, suspicious, unwanted
88
+ - "newsletter": Subscribed content, digests
93
89
  - "promotional": Marketing, sales, discounts
94
90
  - "transactional": Receipts, shipping, confirmations
95
91
  - "social": LinkedIn, friends, social updates
96
- - "newsletter": Subscribed content
97
- - "spam": Junk, suspicious
92
+ - "support": Help desk, customer service
93
+ - "client": Business clients, customers
94
+ - "internal": Company internal communications
95
+ - "personal": Friends, family, personal matters
96
+ - "other": Anything else
98
97
 
99
98
  Context:
100
99
  - Current Date: ${new Date().toISOString()}
@@ -133,13 +132,14 @@ REQUIRED JSON STRUCTURE:
133
132
  }
134
133
  let rawResponse = '';
135
134
  try {
136
- // Using raw completion call to handle garbage characters and strip tokens manually
137
- const response = await this.client.client.chat.completions.create({
135
+ // Request JSON response format for reliable parsing
136
+ const response = await this.client.chat.completions.create({
138
137
  model: this.model,
139
138
  messages: [
140
139
  { role: 'system', content: systemPrompt },
141
140
  { role: 'user', content: cleanedContent || '[Empty email body]' },
142
141
  ],
142
+ response_format: { type: 'json_object' },
143
143
  temperature: 0.1,
144
144
  });
145
145
  rawResponse = response.choices[0]?.message?.content || '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realtimex/email-automator",
3
- "version": "2.3.6",
3
+ "version": "2.3.8",
4
4
  "type": "module",
5
5
  "main": "dist/api/server.js",
6
6
  "bin": {
@@ -69,7 +69,6 @@
69
69
  },
70
70
  "dependencies": {
71
71
  "@azure/msal-node": "^3.8.5",
72
- "@instructor-ai/instructor": "^1.7.0",
73
72
  "@radix-ui/react-alert-dialog": "^1.1.15",
74
73
  "@radix-ui/react-dialog": "^1.1.15",
75
74
  "@radix-ui/react-label": "^2.1.8",