@bradtech/sales-skills 1.0.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/.env.dist +18 -0
- package/AGENT.md +58 -0
- package/HOWTO.md +72 -0
- package/LICENSE +8 -0
- package/LLM.md +37 -0
- package/README.md +100 -0
- package/bin/activate_skills.sh +54 -0
- package/bin/lm_studio_agent.ts +172 -0
- package/bin/publish-prepare.cjs +69 -0
- package/bun.lock +262 -0
- package/package.json +35 -0
- package/skills/actions/sync-meetings-to-odoo/SKILL.md +97 -0
- package/skills/actions/sync-meetings-to-odoo/scripts/sync_meetings_cli.ts +235 -0
- package/skills/vendor/brevo/SKILL.md +83 -0
- package/skills/vendor/brevo/cli.ts +167 -0
- package/skills/vendor/google/SKILL.md +66 -0
- package/skills/vendor/google/calendar.ts +150 -0
- package/skills/vendor/google/cli.ts +74 -0
- package/skills/vendor/odoo/SKILL.md +116 -0
- package/skills/vendor/odoo/cli.ts +566 -0
- package/tsconfig.json +24 -0
package/.env.dist
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Rename this file to '.env' at the root of the repository
|
|
2
|
+
|
|
3
|
+
# Odoo ERP Configuration
|
|
4
|
+
ODOO_URL="https://your-instance.odoo.com"
|
|
5
|
+
ODOO_DB="your_database_name"
|
|
6
|
+
ODOO_USER="your_email@company.com"
|
|
7
|
+
ODOO_PASSWORD="your_password_or_api_key"
|
|
8
|
+
|
|
9
|
+
# Brevo API Configuration - Retrieve your v3 API key from your Brevo account
|
|
10
|
+
BREVO_API_KEY="your_brevo_v3_api_key"
|
|
11
|
+
|
|
12
|
+
# Path to the Google Service Account key file (optional if placed at root as 'service_account.json')
|
|
13
|
+
GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service_account.json"
|
|
14
|
+
|
|
15
|
+
# Local LM Studio Configuration (optional)
|
|
16
|
+
LM_STUDIO_URL="http://localhost:1234/v1"
|
|
17
|
+
LM_STUDIO_MODEL="qwen2.5-coder-7b-instruct"
|
|
18
|
+
|
package/AGENT.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# LLM Agent Instructions: Sales Skills Guidelines
|
|
2
|
+
|
|
3
|
+
This repository follows the core guidelines and styling conventions of the crapougnax ecosystem.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🧠Base Guidelines & Personal Rules
|
|
8
|
+
|
|
9
|
+
For base development principles, coding standards, and project rules, all agent actions must comply with the rules defined in:
|
|
10
|
+
👉 **[Gist: Personal Gemini Rules & Instructions](https://gist.github.com/crapougnax/47971b85aa73dd702f4372a89858111c)**
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 🛠How to Add a New Vendor Skill
|
|
15
|
+
|
|
16
|
+
Vendor skills represent atomic wrappers around third-party APIs. To add a new vendor integration:
|
|
17
|
+
|
|
18
|
+
1. **Create the Vendor Directory**:
|
|
19
|
+
- Location: `skills/vendor/<vendor_name>/`
|
|
20
|
+
2. **Implement Client and CLI Runner**:
|
|
21
|
+
- Create scripts inside the vendor directory (e.g., `client.ts` for logic, `cli.ts` for Commander-based runner).
|
|
22
|
+
- Use standard `@quatrain` core libraries for logging, HTTP clients, and prompt helpers:
|
|
23
|
+
- `@quatrain/log` for output reporting.
|
|
24
|
+
- `@quatrain/cli` for interactive prompts.
|
|
25
|
+
- `@quatrain/api-client` (REST) or `@quatrain/api-xmlrpc` (XML-RPC).
|
|
26
|
+
3. **Prevent Subcommand Collision**:
|
|
27
|
+
- Ensure the command parsing in the main CLI entrypoint (`cli.ts`) is conditionally executed:
|
|
28
|
+
```typescript
|
|
29
|
+
if (import.meta.main) {
|
|
30
|
+
main().catch(...)
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
4. **Create the Skill Definition**:
|
|
34
|
+
- Add `skills/vendor/<vendor_name>/SKILL.md` containing YAML frontmatter (`name` and `description`) and detailed instruction triggers.
|
|
35
|
+
5. **Activate the Skill**:
|
|
36
|
+
- Link the directory into the agent customization workspace folder:
|
|
37
|
+
```bash
|
|
38
|
+
ln -s ../../skills/vendor/<vendor_name> .agents/skills/<vendor_name>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 🚀 How to Add a New Composite Action
|
|
44
|
+
|
|
45
|
+
Composite actions orchestrate workflows across multiple vendors. To add a new composite action:
|
|
46
|
+
|
|
47
|
+
1. **Create the Action Directory**:
|
|
48
|
+
- Location: `skills/actions/<action_name>/`
|
|
49
|
+
2. **Implement the Action Orchestrator**:
|
|
50
|
+
- Create scripts inside a `scripts/` subdirectory, e.g., `skills/actions/<action_name>/scripts/<action_name>_cli.ts`.
|
|
51
|
+
- Import vendor client instances from `../../../vendor/` relative paths.
|
|
52
|
+
3. **Create the Skill Definition**:
|
|
53
|
+
- Add `skills/actions/<action_name>/SKILL.md` containing YAML frontmatter (`name` and `description`) and instructions defining the workflow steps.
|
|
54
|
+
4. **Activate the Action**:
|
|
55
|
+
- Link the directory into the agent customization workspace folder:
|
|
56
|
+
```bash
|
|
57
|
+
ln -s ../../skills/actions/<action_name> .agents/skills/<action_name>
|
|
58
|
+
```
|
package/HOWTO.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# How-To Guide: Running Sales Skills Commands
|
|
2
|
+
|
|
3
|
+
This document contains standard recipes and commands for testing, verifying, and running the sales-skills suite. Make sure you have run `bun install` and loaded your `.env` before executing these commands.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Odoo ERP Integration
|
|
8
|
+
|
|
9
|
+
### Search Partners
|
|
10
|
+
Search Odoo contacts by query string (checks names and emails):
|
|
11
|
+
```bash
|
|
12
|
+
bun run skills/vendor/odoo/cli.ts search-partners --query "infotel" --limit 5 --output ".log/partners.json"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Create Contact
|
|
16
|
+
Create a new partner inside Odoo:
|
|
17
|
+
```bash
|
|
18
|
+
bun run skills/vendor/odoo/cli.ts create-contact --name "John Doe" --email "john.doe@example.com" --output ".log/new_contact.json"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Create Meeting
|
|
22
|
+
Schedule a calendar meeting inside Odoo (attendees list expects Odoo partner IDs):
|
|
23
|
+
```bash
|
|
24
|
+
bun run skills/vendor/odoo/cli.ts create-meeting --name "Introduction Call" --start "2026-06-25 14:00:00" --duration 1.5 --attendees 365940,365941 --output ".log/new_meeting.json"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. Brevo Email Marketing
|
|
30
|
+
|
|
31
|
+
### List Contacts
|
|
32
|
+
Retrieve contacts from your Brevo mailing list:
|
|
33
|
+
```bash
|
|
34
|
+
bun run skills/vendor/brevo/cli.ts list-contacts --limit 10 --output ".log/brevo_list.json"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 3. Google Calendar
|
|
40
|
+
|
|
41
|
+
### List Events
|
|
42
|
+
List upcoming calendar events for a specific user:
|
|
43
|
+
```bash
|
|
44
|
+
bun run skills/vendor/google/cli.ts list-events --calendar-id "olivier@brad.ag" --limit 5 --output ".log/google_events.json"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Create Event
|
|
48
|
+
Schedule an event in Google Calendar:
|
|
49
|
+
```bash
|
|
50
|
+
bun run skills/vendor/google/cli.ts create-event --summary "Sync with Partner" --start "2026-06-25 15:30:00" --duration 1.0 --description "Introductory talk" --calendar-id "olivier@brad.ag" --output ".log/event_created.json"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Delete Event
|
|
54
|
+
Delete an event by its unique Google Calendar ID:
|
|
55
|
+
```bash
|
|
56
|
+
bun run skills/vendor/google/cli.ts delete-event --event-id "_example_id" --calendar-id "olivier@brad.ag" --output ".log/event_deleted.json"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 4. Google Calendar to Odoo CRM Sync (Composite Action)
|
|
62
|
+
|
|
63
|
+
Sync Google Calendar events for a specific date to OdooCRM, prompting for missing contact creation:
|
|
64
|
+
```bash
|
|
65
|
+
bun run skills/actions/sync-meetings-to-odoo/scripts/sync_meetings_cli.ts --calendar-id "olivier@brad.ag" --date "2026-06-25" --output ".log/sync_run.json"
|
|
66
|
+
```
|
|
67
|
+
During execution, the CLI will:
|
|
68
|
+
1. Load calendar events.
|
|
69
|
+
2. Present list of proposals.
|
|
70
|
+
3. Wait for `(Y/n)` keyboard confirmations to create contacts, update contacts, and insert meetings.
|
|
71
|
+
4. Execute confirmed operations.
|
|
72
|
+
5. Log a final summary of all successful creations to `.log/sync_run.json`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# LICENSE
|
|
2
|
+
|
|
3
|
+
This project is licensed under the **GNU Affero General Public License v3.0 (AGPL v3)**.
|
|
4
|
+
|
|
5
|
+
Copyright © 2026 Brad Technology SAS. All Rights Reserved.
|
|
6
|
+
|
|
7
|
+
For full license details, please refer to the official GNU AGPL v3 text at:
|
|
8
|
+
https://www.gnu.org/licenses/agpl-3.0.txt
|
package/LLM.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# LLM Agent Skill Activation
|
|
2
|
+
|
|
3
|
+
Activate and update all integration skills for your local or global LLM agents with a single command:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun run setup-skills
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What the Script Sets Up
|
|
12
|
+
|
|
13
|
+
### 1. Gemini Coding Assistant (Customization Roots)
|
|
14
|
+
- Links all workspace integrations under `skills/` to the local customization directory `.agents/skills/`.
|
|
15
|
+
- Registers rule definitions (`AGENT.md`) to `.agents/AGENTS.md`.
|
|
16
|
+
- Automatically links skills globally to `~/.gemini/config/skills/` if the global config directory exists.
|
|
17
|
+
|
|
18
|
+
### 2. Claude-based Assistants (Cline, Roo Code, etc.)
|
|
19
|
+
- Exposes workspace guidelines by linking `AGENT.md` directly to `.clinerules` and `.roomodes` at the root.
|
|
20
|
+
- The assistant automatically parses these files at startup to understand available commands.
|
|
21
|
+
|
|
22
|
+
### 3. Local Models via LM Studio (Agent Runner)
|
|
23
|
+
You can run this skill set with local models (e.g. Qwen-2.5-Coder, Llama-3) hosted via LM Studio:
|
|
24
|
+
1. Turn on the local server in LM Studio (defaults to `http://localhost:1234/v1`).
|
|
25
|
+
2. Run the agent using `local-agent`:
|
|
26
|
+
```bash
|
|
27
|
+
bun run local-agent "Search Odoo for partners at infotel"
|
|
28
|
+
```
|
|
29
|
+
- Environment variables `LM_STUDIO_URL` and `LM_STUDIO_MODEL` can be defined in your `.env` to override the defaults.
|
|
30
|
+
|
|
31
|
+
### 4. Other Assistants (OpenAI / Custom GPTs)
|
|
32
|
+
- Register the TS CLI runner commands under your Custom Actions / Functions.
|
|
33
|
+
- Example execute command:
|
|
34
|
+
```bash
|
|
35
|
+
bun run skills/actions/sync-meetings-to-odoo/scripts/sync_meetings_cli.ts --calendar-id <email> --date <date> --output <output_path>
|
|
36
|
+
```
|
|
37
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Brad Sales Skills
|
|
2
|
+
|
|
3
|
+
A suite of TypeScript/Bun integration tools designed to connect Odoo ERP, Brevo, and Google Calendar into a cohesive automated sales assistant.
|
|
4
|
+
|
|
5
|
+
This repository leverages the `@quatrain` ecosystem for unified logging, API clients (both REST and XML-RPC), and interactive prompt helper utilities.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
### 1. Atomic Vendor Clients (`skills/vendor/`)
|
|
12
|
+
- **Odoo CRM (`odoo-integration`)**: Search partners, update contacts, create new contacts, and manage calendar events in Odoo using a fast XML-RPC connector (`@quatrain/api-xmlrpc`).
|
|
13
|
+
- **Brevo Email Marketing (`brevo-integration`)**: Query, insert, and update newsletter and CRM contacts via the Brevo REST API client.
|
|
14
|
+
- **Google Calendar (`google-calendar`)**: Access Google Calendars using service account credentials. Supports listing, creating, and deleting events.
|
|
15
|
+
|
|
16
|
+
### 2. Composite Sync Actions (`skills/actions/`)
|
|
17
|
+
- **Google Calendar to Odoo Meetings Sync (`sync-meetings-to-odoo`)**: An interactive command-line orchestrator that:
|
|
18
|
+
1. Pulls events from Google Calendar for a target date.
|
|
19
|
+
2. Resolves attendees' emails against Odoo partners.
|
|
20
|
+
3. Prompts the user interactively (using inquirer-based helpers) to create missing contacts, update incomplete names, and create Odoo calendar meetings linked to those partners.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Directory Structure
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
├── .env.dist # Template for environment configuration
|
|
28
|
+
├── package.json # Project dependencies (linked local @quatrain workspaces)
|
|
29
|
+
├── tsconfig.json # Path mappings for direct Bun execution
|
|
30
|
+
├── service_account.json # (Optional) Google Service Account key file
|
|
31
|
+
├── skills/ # Skill Definitions & Implementations
|
|
32
|
+
│ ├── actions/ # Composite orchestrators
|
|
33
|
+
│ │ └── sync-meetings-to-odoo/
|
|
34
|
+
│ │ ├── SKILL.md # LLM integration triggers for composite sync
|
|
35
|
+
│ │ └── scripts/
|
|
36
|
+
│ │ └── sync_meetings_cli.ts
|
|
37
|
+
│ └── vendor/ # CLI entry points and definitions for atomic vendors
|
|
38
|
+
│ ├── google/
|
|
39
|
+
│ │ ├── SKILL.md # LLM integration triggers for Google Calendar
|
|
40
|
+
│ │ ├── calendar.ts # Google calendar client
|
|
41
|
+
│ │ └── cli.ts # CLI runner
|
|
42
|
+
│ ├── odoo/
|
|
43
|
+
│ │ ├── SKILL.md # LLM integration triggers for Odoo
|
|
44
|
+
│ │ └── cli.ts # Odoo client & CLI runner
|
|
45
|
+
│ └── brevo/
|
|
46
|
+
│ ├── SKILL.md # LLM integration triggers for Brevo
|
|
47
|
+
│ └── cli.ts # Brevo client & CLI runner
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
Duplicate `.env.dist` as `.env` and fill in the credentials:
|
|
56
|
+
|
|
57
|
+
```ini
|
|
58
|
+
# Odoo ERP Configuration
|
|
59
|
+
ODOO_URL="https://your-instance.odoo.com"
|
|
60
|
+
ODOO_DB="your_database_name"
|
|
61
|
+
ODOO_USER="your_email@company.com"
|
|
62
|
+
ODOO_PASSWORD="your_password_or_api_key"
|
|
63
|
+
|
|
64
|
+
# Brevo API Configuration
|
|
65
|
+
BREVO_API_KEY="your_brevo_v3_api_key"
|
|
66
|
+
|
|
67
|
+
# Path to the Google Service Account key file (optional if placed at root as 'service_account.json')
|
|
68
|
+
GOOGLE_APPLICATION_CREDENTIALS="service_account.json"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Google Service Account
|
|
72
|
+
Download your Google Cloud Service Account key file and:
|
|
73
|
+
1. Place it at the root of the repository as `service_account.json`, OR
|
|
74
|
+
2. Place it anywhere and set its absolute path in the `GOOGLE_APPLICATION_CREDENTIALS` environment variable inside `.env`.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## LLM Activation
|
|
79
|
+
|
|
80
|
+
This repository is structured as a **Gemini/Custom Agent Skill Set**. To activate these skills and allow your LLM coding assistant to automatically discover, read, and run these commands, configure customization roots:
|
|
81
|
+
|
|
82
|
+
### 1. Workspace-Specific Activation (Recommended)
|
|
83
|
+
Create a directory named `.agents` at the root of your workspace, and create/link the skill definitions:
|
|
84
|
+
```bash
|
|
85
|
+
mkdir -p .agents/skills
|
|
86
|
+
ln -s ../../skills/vendor/google .agents/skills/google-calendar
|
|
87
|
+
ln -s ../../skills/vendor/odoo .agents/skills/odoo-integration
|
|
88
|
+
ln -s ../../skills/vendor/brevo .agents/skills/brevo-integration
|
|
89
|
+
ln -s ../../skills/actions/sync-meetings-to-odoo .agents/skills/sync-meetings-to-odoo
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 2. Global Activation
|
|
93
|
+
Alternatively, copy or link these directories into your global configuration directory (usually located at `~/.gemini/config/skills/`):
|
|
94
|
+
```bash
|
|
95
|
+
cp -r skills/vendor/* ~/.gemini/config/skills/
|
|
96
|
+
cp -r skills/actions/* ~/.gemini/config/skills/
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Once linked/copied, the LLM agent will detect the frontmatter name and description in each `SKILL.md` (e.g. `google-calendar`, `odoo-integration`), matching user requests to the respective CLI tools and executing actions autonomously.
|
|
100
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Exit immediately if a command exits with a non-zero status
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
WORKSPACE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
7
|
+
cd "$WORKSPACE_ROOT"
|
|
8
|
+
|
|
9
|
+
echo "=== Sales Skills LLM Activation & Update Script ==="
|
|
10
|
+
|
|
11
|
+
# 1. Local Workspace-Specific Customizations (.agents)
|
|
12
|
+
echo "Setting up local Gemini agent workspace customizations..."
|
|
13
|
+
mkdir -p .agents/skills
|
|
14
|
+
|
|
15
|
+
# Helper to link if target doesn't exist, or update/force symlink
|
|
16
|
+
link_skill() {
|
|
17
|
+
local src="$1"
|
|
18
|
+
local dest=".agents/skills/$2"
|
|
19
|
+
echo "Linking skill '$2' -> '$src'..."
|
|
20
|
+
ln -sfh "../../$src" "$dest"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
link_skill "skills/vendor/google" "google-calendar"
|
|
24
|
+
link_skill "skills/vendor/odoo" "odoo-integration"
|
|
25
|
+
link_skill "skills/vendor/brevo" "brevo-integration"
|
|
26
|
+
link_skill "skills/actions/sync-meetings-to-odoo" "sync-meetings-to-odoo"
|
|
27
|
+
|
|
28
|
+
# Link AGENT.md as workspace customizations rule AGENTS.md
|
|
29
|
+
echo "Linking AGENT.md -> .agents/AGENTS.md..."
|
|
30
|
+
ln -sf "../AGENT.md" .agents/AGENTS.md
|
|
31
|
+
|
|
32
|
+
# 2. Cline & Roo Code Rule Files
|
|
33
|
+
echo "Setting up rules for Cline and Roo Code assistants..."
|
|
34
|
+
ln -sf "AGENT.md" .clinerules
|
|
35
|
+
ln -sf "AGENT.md" .roomodes
|
|
36
|
+
|
|
37
|
+
# 3. Global Gemini Assistant Skills (Optional)
|
|
38
|
+
GLOBAL_GEMINI_SKILLS_DIR="$HOME/.gemini/config/skills"
|
|
39
|
+
if [ -d "$HOME/.gemini/config" ]; then
|
|
40
|
+
echo "Global Gemini config found. Setting up global skills..."
|
|
41
|
+
mkdir -p "$GLOBAL_GEMINI_SKILLS_DIR"
|
|
42
|
+
|
|
43
|
+
# Link/update skills globally
|
|
44
|
+
ln -sfh "$WORKSPACE_ROOT/skills/vendor/google" "$GLOBAL_GEMINI_SKILLS_DIR/google-calendar"
|
|
45
|
+
ln -sfh "$WORKSPACE_ROOT/skills/vendor/odoo" "$GLOBAL_GEMINI_SKILLS_DIR/odoo-integration"
|
|
46
|
+
ln -sfh "$WORKSPACE_ROOT/skills/vendor/brevo" "$GLOBAL_GEMINI_SKILLS_DIR/brevo-integration"
|
|
47
|
+
ln -sfh "$WORKSPACE_ROOT/skills/actions/sync-meetings-to-odoo" "$GLOBAL_GEMINI_SKILLS_DIR/sync-meetings-to-odoo"
|
|
48
|
+
|
|
49
|
+
echo "Global skills successfully symlinked to $GLOBAL_GEMINI_SKILLS_DIR"
|
|
50
|
+
else
|
|
51
|
+
echo "Global Gemini config path not found. Skipping global registration."
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
echo "Success! Skills are linked, activated, and updated."
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { $ } from "bun";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const LM_STUDIO_URL = process.env.LM_STUDIO_URL || "http://localhost:1234/v1";
|
|
6
|
+
const MODEL_NAME = process.env.LM_STUDIO_MODEL || "qwen2.5-coder-7b-instruct"; // Default fallback model name
|
|
7
|
+
|
|
8
|
+
console.log(`=== LM Studio Local Agent Runner ===`);
|
|
9
|
+
console.log(`Connecting to LM Studio at: ${LM_STUDIO_URL}`);
|
|
10
|
+
console.log(`Using model: ${MODEL_NAME}`);
|
|
11
|
+
|
|
12
|
+
// Define tools according to OpenAI / LM Studio specification
|
|
13
|
+
const TOOLS = [
|
|
14
|
+
{
|
|
15
|
+
type: "function",
|
|
16
|
+
function: {
|
|
17
|
+
name: "search_odoo_partners",
|
|
18
|
+
description: "Search for contacts or companies in Odoo ERP matching a query string.",
|
|
19
|
+
parameters: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
query: { type: "string", description: "Search query (email, name, or domain)" },
|
|
23
|
+
limit: { type: "integer", description: "Maximum number of partners to return", default: 5 }
|
|
24
|
+
},
|
|
25
|
+
required: ["query"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: "function",
|
|
31
|
+
function: {
|
|
32
|
+
name: "list_google_events",
|
|
33
|
+
description: "List upcoming calendar events from Google Calendar.",
|
|
34
|
+
parameters: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
calendarId: { type: "string", description: "Email address of the calendar owner", default: "primary" },
|
|
38
|
+
limit: { type: "integer", description: "Maximum number of events to fetch", default: 10 }
|
|
39
|
+
},
|
|
40
|
+
required: ["calendarId"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: "function",
|
|
46
|
+
function: {
|
|
47
|
+
name: "sync_meetings_to_odoo",
|
|
48
|
+
description: "Sync meetings from Google Calendar to Odoo CRM interactively, confirming contacts and meetings creation.",
|
|
49
|
+
parameters: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
calendarId: { type: "string", description: "Google Calendar owner email (defaults to Odoo login)" },
|
|
53
|
+
date: { type: "string", description: "Target date to sync in YYYY-MM-DD format (defaults to today)" }
|
|
54
|
+
},
|
|
55
|
+
required: ["calendarId"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Helper to execute local CLI scripts and return the JSON output
|
|
62
|
+
async function executeTool(name: string, args: any): Promise<any> {
|
|
63
|
+
const logDir = join(process.cwd(), ".log");
|
|
64
|
+
await $`mkdir -p ${logDir}`;
|
|
65
|
+
const outputPath = join(logDir, `lm_studio_${name}_result.json`);
|
|
66
|
+
|
|
67
|
+
console.log(`\n[Executing Tool: ${name} with arguments:`, args, "]");
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
if (name === "search_odoo_partners") {
|
|
71
|
+
const limit = args.limit || 5;
|
|
72
|
+
await $`bun run skills/vendor/odoo/cli.ts search-partners --query ${args.query} --limit ${limit} --output ${outputPath}`;
|
|
73
|
+
} else if (name === "list_google_events") {
|
|
74
|
+
const limit = args.limit || 10;
|
|
75
|
+
await $`bun run skills/vendor/google/cli.ts list-events --calendar-id ${args.calendarId} --limit ${limit} --output ${outputPath}`;
|
|
76
|
+
} else if (name === "sync_meetings_to_odoo") {
|
|
77
|
+
const dateOption = args.date ? ["--date", args.date] : [];
|
|
78
|
+
// Note: This script is interactive and uses stdin. We spawn it inheriting parent terminal I/O.
|
|
79
|
+
await $`bun run skills/actions/sync-meetings-to-odoo/scripts/sync_meetings_cli.ts --calendar-id ${args.calendarId} ${dateOption} --output ${outputPath}`.inherit();
|
|
80
|
+
} else {
|
|
81
|
+
throw new Error(`Unknown tool name: ${name}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (existsSync(outputPath)) {
|
|
85
|
+
const content = readFileSync(outputPath, "utf8");
|
|
86
|
+
return JSON.parse(content);
|
|
87
|
+
}
|
|
88
|
+
return { status: "success", info: "Command ran successfully, but no output JSON was produced." };
|
|
89
|
+
} catch (err: any) {
|
|
90
|
+
console.error(`Tool execution failed: ${err.message}`);
|
|
91
|
+
return { error: err.message };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Simple agent loop
|
|
96
|
+
async function runAgent(prompt: string) {
|
|
97
|
+
const messages: any[] = [
|
|
98
|
+
{
|
|
99
|
+
role: "system",
|
|
100
|
+
content: "You are a helpful sales assistant with access to Odoo CRM, Google Calendar, and Brevo tools. Run actions whenever requested."
|
|
101
|
+
},
|
|
102
|
+
{ role: "user", content: prompt }
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
let loop = true;
|
|
106
|
+
while (loop) {
|
|
107
|
+
try {
|
|
108
|
+
const response = await fetch(`${LM_STUDIO_URL}/chat/completions`, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "Content-Type": "application/json" },
|
|
111
|
+
body: JSON.stringify({
|
|
112
|
+
model: MODEL_NAME,
|
|
113
|
+
messages,
|
|
114
|
+
tools: TOOLS,
|
|
115
|
+
tool_choice: "auto"
|
|
116
|
+
})
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
throw new Error(`HTTP error ${response.status}: ${await response.text()}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const result = await response.json();
|
|
124
|
+
const choice = result.choices?.[0];
|
|
125
|
+
const message = choice?.message;
|
|
126
|
+
|
|
127
|
+
if (!message) {
|
|
128
|
+
console.log("No response message received.");
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add assistant response to history
|
|
133
|
+
messages.push(message);
|
|
134
|
+
|
|
135
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
136
|
+
for (const toolCall of message.tool_calls) {
|
|
137
|
+
const functionName = toolCall.function.name;
|
|
138
|
+
const functionArgs = JSON.parse(toolCall.function.arguments);
|
|
139
|
+
|
|
140
|
+
const resultPayload = await executeTool(functionName, functionArgs);
|
|
141
|
+
|
|
142
|
+
// Add tool result to history
|
|
143
|
+
messages.push({
|
|
144
|
+
role: "tool",
|
|
145
|
+
tool_call_id: toolCall.id,
|
|
146
|
+
name: functionName,
|
|
147
|
+
content: JSON.stringify(resultPayload)
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
// Model returned final text answer
|
|
152
|
+
console.log(`\n[Assistant Response]:\n${message.content}`);
|
|
153
|
+
loop = false;
|
|
154
|
+
}
|
|
155
|
+
} catch (err: any) {
|
|
156
|
+
console.error(`Agent loop error: ${err.message}`);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Run using prompt argument or exit
|
|
163
|
+
const userPrompt = process.argv.slice(2).join(" ");
|
|
164
|
+
if (!userPrompt) {
|
|
165
|
+
console.log("Usage: bun run bin/lm_studio_agent.ts <your prompt here>");
|
|
166
|
+
console.log("Example: bun run bin/lm_studio_agent.ts 'Search Odoo for contacts at infotel'");
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
runAgent(userPrompt).catch((err) => {
|
|
171
|
+
console.error("Agent execution failed:", err);
|
|
172
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { execSync } = require('node:child_process');
|
|
4
|
+
|
|
5
|
+
const pkgPath = './package.json';
|
|
6
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
7
|
+
|
|
8
|
+
console.log('[PREPARE] Resolving workspace dependencies for CI install...');
|
|
9
|
+
|
|
10
|
+
// Remove workspaces since they don't exist in CI
|
|
11
|
+
delete pkg.workspaces;
|
|
12
|
+
|
|
13
|
+
let changed = false;
|
|
14
|
+
for (const depType of ['dependencies', 'devDependencies', 'peerDependencies']) {
|
|
15
|
+
if (pkg[depType]) {
|
|
16
|
+
for (const [name, val] of Object.entries(pkg[depType])) {
|
|
17
|
+
if (typeof val === 'string' && (val === 'workspace:*' || val.startsWith('workspace:'))) {
|
|
18
|
+
try {
|
|
19
|
+
// Query the version from the registry
|
|
20
|
+
console.log(`[PREPARE] Querying registry version for ${name}...`);
|
|
21
|
+
const latestVersion = execSync(`npm view ${name} version`, { encoding: 'utf8' }).trim();
|
|
22
|
+
pkg[depType][name] = `^${latestVersion}`;
|
|
23
|
+
console.log(`[PREPARE] Resolved ${name} dependency to ^${latestVersion}`);
|
|
24
|
+
changed = true;
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.warn(`[WARNING] Failed to resolve version for ${name} from registry: ${err.message}`);
|
|
27
|
+
|
|
28
|
+
// Try local workspace fallback if present
|
|
29
|
+
let resolvedLocal = false;
|
|
30
|
+
const corePkgsDir = path.resolve(process.cwd(), '../../QUATRAIN/Core/packages');
|
|
31
|
+
if (fs.existsSync(corePkgsDir)) {
|
|
32
|
+
try {
|
|
33
|
+
const dirs = fs.readdirSync(corePkgsDir);
|
|
34
|
+
for (const dir of dirs) {
|
|
35
|
+
const localPkgJsonPath = path.join(corePkgsDir, dir, 'package.json');
|
|
36
|
+
if (fs.existsSync(localPkgJsonPath)) {
|
|
37
|
+
const localPkgJson = JSON.parse(fs.readFileSync(localPkgJsonPath, 'utf8'));
|
|
38
|
+
if (localPkgJson.name === name) {
|
|
39
|
+
pkg[depType][name] = `^${localPkgJson.version}`;
|
|
40
|
+
console.log(`[PREPARE] Resolved ${name} from local workspace to ^${localPkgJson.version}`);
|
|
41
|
+
changed = true;
|
|
42
|
+
resolvedLocal = true;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!resolvedLocal) {
|
|
53
|
+
// Fallback to '*' if it is not published yet and local workspace not found
|
|
54
|
+
pkg[depType][name] = '*';
|
|
55
|
+
console.log(`[PREPARE] Fallback resolved ${name} dependency to *`);
|
|
56
|
+
changed = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (changed || pkg.hasOwnProperty('workspaces')) {
|
|
65
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
66
|
+
console.log('[PREPARE] package.json updated successfully.');
|
|
67
|
+
} else {
|
|
68
|
+
console.log('[PREPARE] No changes made to package.json.');
|
|
69
|
+
}
|