Commit 6270d00b authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

chore: update all changes

parent ff2e9767
...@@ -2,3 +2,9 @@ GA_MEASUREMENT_ID=G-NZW1X7RXTD ...@@ -2,3 +2,9 @@ GA_MEASUREMENT_ID=G-NZW1X7RXTD
GA_API_SECRET=cw05MUKjSWWlZ_NMf8n0Fw GA_API_SECRET=cw05MUKjSWWlZ_NMf8n0Fw
_AUTOCOMPLETE_API=https://anything-copilot.alib.workers.dev/v1/tab/completion _AUTOCOMPLETE_API=https://anything-copilot.alib.workers.dev/v1/tab/completion
AUTOCOMPLETE_API=http://localhost:8000/v1/tab/completion AUTOCOMPLETE_API=http://localhost:8000/v1/tab/completion
# n8n Configuration
N8N_API_URL=https://vuhoanganh1704.app.n8n.cloud
N8N_API_KEY=bgb
MCP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5MDg3MjUyMS1lMWU0LTQyOGItOGJkYi02Y2Y4MTZhM2QxYTkiLCJpc3MiOiJuOG4iLCJhdWQiOiJtY3Atc2VydmVyLWFwaSIsImp0aSI6ImUxNTRmOTU5LTdiZWItNDc0Ny1iZjQ2LTcxZDI5OGMxODNiMyIsImlhdCI6MTc3NzQ0MjQ2M30.Ll2wmRMUB0p3JtIMMSIVtrGVRvTWoNqVfWMSapC9xkA
MCP_PORT=3006
2026/04/29-11:42:37.489 610c Creating DB D:\cnf\n8n_ai_agent\.tmp\test-user-data-2\Default\Local Storage\leveldb since it was missing.
2026/04/29-11:42:37.512 610c Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data-2\Default\Local Storage\leveldb/MANIFEST-000001
2026/04/29-11:42:38.601 610c Creating DB D:\cnf\n8n_ai_agent\.tmp\test-user-data-2\Default\Session Storage since it was missing.
2026/04/29-11:42:38.612 610c Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data-2\Default\Session Storage/MANIFEST-000001
2026/04/29-11:42:37.590 4894 Creating DB D:\cnf\n8n_ai_agent\.tmp\test-user-data-2\Default\shared_proto_db since it was missing.
2026/04/29-11:42:37.602 4894 Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data-2\Default\shared_proto_db/MANIFEST-000001
2026/04/29-11:42:37.521 4894 Creating DB D:\cnf\n8n_ai_agent\.tmp\test-user-data-2\Default\shared_proto_db\metadata since it was missing.
2026/04/29-11:42:37.535 4894 Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data-2\Default\shared_proto_db\metadata/MANIFEST-000001
2026/04/29-11:37:15.538 6a70 Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\Local Storage\leveldb/MANIFEST-000001
2026/04/29-11:37:15.544 6a70 Recovering log #3
2026/04/29-11:37:15.545 6a70 Reusing old log D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\Local Storage\leveldb/000003.log
2026/04/29-11:13:37.267 66a8 Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\Local Storage\leveldb/MANIFEST-000001
2026/04/29-11:13:37.273 66a8 Recovering log #3
2026/04/29-11:13:37.275 66a8 Reusing old log D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\Local Storage\leveldb/000003.log
2026/04/29-11:38:16.248 6a70 Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\Session Storage/MANIFEST-000001
2026/04/29-11:38:16.248 6a70 Recovering log #3
2026/04/29-11:38:16.249 6a70 Reusing old log D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\Session Storage/000003.log
2026/04/29-11:14:37.269 48e0 Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\Session Storage/MANIFEST-000001
2026/04/29-11:14:37.270 48e0 Recovering log #3
2026/04/29-11:14:37.271 48e0 Reusing old log D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\Session Storage/000003.log
2026/04/29-11:37:15.649 3710 Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\shared_proto_db/MANIFEST-000001
2026/04/29-11:37:15.649 3710 Recovering log #3
2026/04/29-11:37:15.650 3710 Reusing old log D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\shared_proto_db/000003.log
2026/04/29-11:13:37.302 685c Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\shared_proto_db/MANIFEST-000001
2026/04/29-11:13:37.303 685c Recovering log #3
2026/04/29-11:13:37.303 685c Reusing old log D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\shared_proto_db/000003.log
2026/04/29-11:37:15.642 3710 Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\shared_proto_db\metadata/MANIFEST-000001
2026/04/29-11:37:15.643 3710 Recovering log #3
2026/04/29-11:37:15.643 3710 Reusing old log D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\shared_proto_db\metadata/000003.log
2026/04/29-11:13:37.300 685c Reusing MANIFEST D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\shared_proto_db\metadata/MANIFEST-000001
2026/04/29-11:13:37.300 685c Recovering log #3
2026/04/29-11:13:37.300 685c Reusing old log D:\cnf\n8n_ai_agent\.tmp\test-user-data\Default\shared_proto_db\metadata/000003.log
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
"devDependencies": { "devDependencies": {
"@crxjs/vite-plugin": "^2.0.0-beta.23", "@crxjs/vite-plugin": "^2.0.0-beta.23",
"@intlify/unplugin-vue-i18n": "^1.5.0", "@intlify/unplugin-vue-i18n": "^1.5.0",
"@playwright/test": "^1.59.1",
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@types/lodash-es": "^4.17.11", "@types/lodash-es": "^4.17.11",
"@types/node": "^18.18.5", "@types/node": "^18.18.5",
...@@ -1834,6 +1835,22 @@ ...@@ -1834,6 +1835,22 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@playwright/test": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz",
"integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.59.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@protobufjs/aspromise": { "node_modules/@protobufjs/aspromise": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
...@@ -6357,6 +6374,53 @@ ...@@ -6357,6 +6374,53 @@
"pathe": "^1.1.2" "pathe": "^1.1.2"
} }
}, },
"node_modules/playwright": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.59.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/playwright/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.38", "version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
......
...@@ -31,29 +31,31 @@ ALWAYS explicitly configure ALL parameters that control node behavior. ...@@ -31,29 +31,31 @@ ALWAYS explicitly configure ALL parameters that control node behavior.
## Available MCP Tools ## Available MCP Tools
Note: Depending on the connection mode (Local Proxy or Native), tools may or may not have the \`n8n_\` prefix. Use the exact tool names provided by the MCP server.
### Workflow Management ### Workflow Management
| Tool | Action | Method | | Tool | Action |
|------|--------|--------| |------|--------|
| n8n_list_workflows | List all workflows | GET /workflows | | [n8n_]list_workflows | List all workflows |
| n8n_get_workflow | Get workflow JSON (full/structure/minimal) | GET /workflows/{id} | | [n8n_]get_workflow | Get workflow JSON (full/structure/minimal) |
| n8n_create_workflow | Create new workflow | POST /workflows | | [n8n_]create_workflow | Create new workflow |
| n8n_update_workflow | Full workflow update (replace) | PUT /workflows/{id} | | [n8n_]update_workflow | Full workflow update (replace) |
| n8n_delete_workflow | Delete workflow permanently | DELETE /workflows/{id} | | [n8n_]delete_workflow | Delete workflow permanently |
| n8n_activate_workflow | Activate (start triggers) | POST /workflows/{id}/activate | | [n8n_]activate_workflow | Activate (start triggers) |
| n8n_deactivate_workflow | Deactivate (stop triggers) | POST /workflows/{id}/deactivate | | [n8n_]deactivate_workflow | Deactivate (stop triggers) |
### Execution Management ### Execution Management
| Tool | Action | | Tool | Action |
|------|--------| |------|--------|
| n8n_execute_workflow | Trigger workflow with input data | | [n8n_]execute_workflow | Trigger workflow with input data |
| n8n_list_executions | List recent executions (filter by workflow/status) | | [n8n_]list_executions | List recent executions (filter by workflow/status) |
| n8n_get_execution | Get execution details with node I/O data | | [n8n_]get_execution | Get execution details with node I/O data |
### System ### System
| Tool | Action | | Tool | Action |
|------|--------| |------|--------|
| n8n_list_credentials | List credential names/types (values never exposed) | | [n8n_]list_credentials | List credential names/types (values never exposed) |
| n8n_health_check | Check n8n API connectivity | | [n8n_]health_check | Check n8n API connectivity |
## Workflow Modification Process ## Workflow Modification Process
......
...@@ -86,7 +86,7 @@ async function openPipBackground(url: string) { ...@@ -86,7 +86,7 @@ async function openPipBackground(url: string) {
url: url, url: url,
mode: "write-html", mode: "write-html",
}, },
}) }).catch(err => console.debug("[bg] openPipBackground sendMessage failed:", err))
} }
async function pipLaunch(url: string) { async function pipLaunch(url: string) {
...@@ -98,7 +98,7 @@ async function pipLaunch(url: string) { ...@@ -98,7 +98,7 @@ async function pipLaunch(url: string) {
chrome.tabs.sendMessage(tab.id!, { chrome.tabs.sendMessage(tab.id!, {
type: MessageType.pipLaunch, type: MessageType.pipLaunch,
url: url, url: url,
}) }).catch(err => console.debug("[bg] pipLaunch sendMessage failed:", err))
} }
type UpdatePipWinOption = { type UpdatePipWinOption = {
...@@ -136,20 +136,23 @@ async function handleInvokeRequest( ...@@ -136,20 +136,23 @@ async function handleInvokeRequest(
currentSender = sender currentSender = sender
let res = await messageInvoke.handleReqMsg(message) let res = await messageInvoke.handleReqMsg(message)
if (!sender.tab?.id) {
console.error("sender tab id is undefined", sender)
}
if (res) { if (res) {
chrome.tabs.sendMessage(sender.tab?.id!, { if (sender.tab?.id) {
chrome.tabs.sendMessage(sender.tab.id, {
type: MessageType.invokeResponse,
...res,
}).catch(err => console.debug("[bg] handleInvokeRequest sendMessage failed:", err))
} else {
chrome.runtime.sendMessage({
type: MessageType.invokeResponse, type: MessageType.invokeResponse,
...res, ...res,
}) })
} }
}
} }
function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendResponse?: (response: any) => void) { function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendResponse?: (response: any) => void) {
console.log("[bg]: ", message.type, message, sender, Date.now()) console.log("[bg]: ", message?.type, message, sender, Date.now())
// ── SuperAssistant MCP Bridge ── // ── SuperAssistant MCP Bridge ──
if (message?.type === "n8n-mcp:call-tool") { if (message?.type === "n8n-mcp:call-tool") {
...@@ -169,6 +172,13 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendR ...@@ -169,6 +172,13 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendR
return true // Async response return true // Async response
} }
// ── MCP Connection Status ──
if (message?.type === "n8n-mcp:status") {
const status = mcpService.getStatus()
sendResponse?.({ connected: status === "connected", status })
return false
}
// ── MCP Config Management ── // ── MCP Config Management ──
if (message?.type === "n8n-mcp:get-config") { if (message?.type === "n8n-mcp:get-config") {
chrome.storage.local.get(["mcp_n8n_config"]).then((data) => { chrome.storage.local.get(["mcp_n8n_config"]).then((data) => {
...@@ -176,7 +186,8 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendR ...@@ -176,7 +186,8 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendR
sendResponse?.({ sendResponse?.({
proxyUrl: cfg.proxyUrl || "http://localhost:3006", proxyUrl: cfg.proxyUrl || "http://localhost:3006",
n8nApiUrl: cfg.n8nApiUrl || "", n8nApiUrl: cfg.n8nApiUrl || "",
n8nApiKey: cfg.n8nApiKey || "" n8nApiKey: cfg.n8nApiKey || "",
mcpAuthToken: cfg.mcpAuthToken || ""
}) })
}) })
return true return true
...@@ -250,11 +261,12 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendR ...@@ -250,11 +261,12 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendR
break break
case MessageType.forwardToTab: case MessageType.forwardToTab:
chrome.tabs.sendMessage(message.tabId, message.message) chrome.tabs.sendMessage(message.tabId, message.message)
.catch(err => console.debug("[bg] forwardToTab sendMessage failed:", err))
break break
case MessageType.invokeRequest: case MessageType.invokeRequest:
currentSender = sender currentSender = sender
messageInvoke.handleReqMsg(message, sender) messageInvoke.handleReqMsg(message, sender)
break return true
case MessageType.invokeResponse: case MessageType.invokeResponse:
messageInvoke.handleResMsg(message) messageInvoke.handleResMsg(message)
break break
...@@ -265,6 +277,12 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendR ...@@ -265,6 +277,12 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender, sendR
registerContentSidebar(sender.tab!.id!, message.info) registerContentSidebar(sender.tab!.id!, message.info)
break break
} }
// Call sendResponse for non-async paths to avoid port closure errors
if (sendResponse) {
sendResponse({ handled: true })
}
return false
} }
async function toggleMinimize() { async function toggleMinimize() {
......
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from "vue" import { ref, onMounted, computed, onUnmounted } from "vue"
import { openSidebar, getIsEdge } from "@/utils/ext"
// ─── State ────────────────────────────────────────────────────────── // ─── State ──────────────────────────────────────────────────────────
...@@ -15,6 +16,7 @@ const workflowLoaded = ref(false) ...@@ -15,6 +16,7 @@ const workflowLoaded = ref(false)
// MCP connection // MCP connection
const mcpConnected = ref(false) const mcpConnected = ref(false)
let statusInterval: any = null
// Platform selection // Platform selection
const selectedPlatform = ref<string | null>(null) const selectedPlatform = ref<string | null>(null)
...@@ -51,9 +53,9 @@ const platforms = [ ...@@ -51,9 +53,9 @@ const platforms = [
onMounted(async () => { onMounted(async () => {
try { try {
// 1. Check MCP connection // 1. Start status polling
const mcpStatus = await sendMessage({ type: "n8n-mcp:status" }) checkMcpStatus()
mcpConnected.value = mcpStatus?.connected || false statusInterval = setInterval(checkMcpStatus, 2000)
// 2. Check active tab // 2. Check active tab
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }) const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
...@@ -67,6 +69,23 @@ onMounted(async () => { ...@@ -67,6 +69,23 @@ onMounted(async () => {
} }
}) })
onUnmounted(() => {
if (statusInterval) clearInterval(statusInterval)
})
async function checkMcpStatus() {
const mcpStatus = await sendMessage({ type: "n8n-mcp:status" })
const newStatus = mcpStatus?.connected || false
// If connection status just became true and we don't have workflow info, re-detect
if (newStatus && !mcpConnected.value && isN8nPage.value && !workflowLoaded.value) {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
if (tab) detectN8nWorkflow(tab)
}
mcpConnected.value = newStatus
}
// ─── n8n Detection ────────────────────────────────────────────────── // ─── n8n Detection ──────────────────────────────────────────────────
async function detectN8nWorkflow(tab: chrome.tabs.Tab) { async function detectN8nWorkflow(tab: chrome.tabs.Tab) {
...@@ -158,11 +177,13 @@ async function launchPlatform(platform: typeof platforms[0]) { ...@@ -158,11 +177,13 @@ async function launchPlatform(platform: typeof platforms[0]) {
selectedPlatform.value = platform.id selectedPlatform.value = platform.id
try { try {
// Open new tab with the AI platform const isEdge = getIsEdge()
const newTab = await chrome.tabs.create({ url: platform.url })
// The content script (superassistant/index.ts) on the new tab will // Open AI platform in sidebar
// automatically fetch context from background and inject it into chat await openSidebar({
urls: [platform.url],
sidePanel: !isEdge, // Use Chrome SidePanel if available, otherwise content sidebar
})
// Close popup // Close popup
setTimeout(() => window.close(), 300) setTimeout(() => window.close(), 300)
......
...@@ -45,24 +45,39 @@ const showSettings = ref(false) ...@@ -45,24 +45,39 @@ const showSettings = ref(false)
const mcpConfig = reactive({ const mcpConfig = reactive({
proxyUrl: "http://localhost:3006", proxyUrl: "http://localhost:3006",
n8nApiUrl: "", n8nApiUrl: "",
n8nApiKey: "" mcpAuthToken: ""
}) })
const isSavingConfig = ref(false) const isSavingConfig = ref(false)
const saveMessage = ref("")
async function saveMcpConfig() { async function saveMcpConfig() {
isSavingConfig.value = true isSavingConfig.value = true
saveMessage.value = "⏳ Đang kết nối thử..."
await chrome.runtime.sendMessage({ await chrome.runtime.sendMessage({
type: "n8n-mcp:set-config", type: "n8n-mcp:set-config",
config: { config: {
proxyUrl: mcpConfig.proxyUrl, proxyUrl: mcpConfig.proxyUrl,
n8nApiUrl: mcpConfig.n8nApiUrl, n8nApiUrl: mcpConfig.n8nApiUrl,
n8nApiKey: mcpConfig.n8nApiKey mcpAuthToken: mcpConfig.mcpAuthToken
} }
}) })
// Wait a bit for connection attempt
setTimeout(async () => {
const status = await chrome.runtime.sendMessage({ type: "n8n-mcp:status" })
if (status?.connected) {
saveMessage.value = "✅ Kết nối thành công!"
setTimeout(() => { setTimeout(() => {
isSavingConfig.value = false isSavingConfig.value = false
showSettings.value = false showSettings.value = false
}, 500) saveMessage.value = ""
}, 1500)
} else {
saveMessage.value = "❌ Thất bại. Kiểm tra lại Token!"
isSavingConfig.value = false
}
}, 3000)
} }
const host = computed({ const host = computed({
...@@ -128,6 +143,7 @@ onMounted(() => { ...@@ -128,6 +143,7 @@ onMounted(() => {
mcpConfig.proxyUrl = res.proxyUrl || "http://localhost:3006" mcpConfig.proxyUrl = res.proxyUrl || "http://localhost:3006"
mcpConfig.n8nApiUrl = res.n8nApiUrl || "" mcpConfig.n8nApiUrl = res.n8nApiUrl || ""
mcpConfig.n8nApiKey = res.n8nApiKey || "" mcpConfig.n8nApiKey = res.n8nApiKey || ""
mcpConfig.mcpAuthToken = res.mcpAuthToken || ""
} }
}) })
...@@ -296,12 +312,12 @@ function showChatDocs() { ...@@ -296,12 +312,12 @@ function showChatDocs() {
/> />
</div> </div>
<div> <div>
<label class="text-xs opacity-60 mb-1 block">n8n API Key</label> <label class="text-xs opacity-60 mb-1 block">MCP Auth Token (Bearer)</label>
<input <input
v-model="mcpConfig.n8nApiKey" v-model="mcpConfig.mcpAuthToken"
type="password" type="password"
class="w-full bg-background border border-white/10 rounded px-2 py-1 text-sm outline-none focus:border-blue-500 transition-colors" class="w-full bg-background border border-white/10 rounded px-2 py-1 text-sm outline-none focus:border-blue-500 transition-colors"
placeholder="n8n_api_key_..." placeholder="eyJhbGci..."
/> />
</div> </div>
<button <button
...@@ -309,8 +325,11 @@ function showChatDocs() { ...@@ -309,8 +325,11 @@ function showChatDocs() {
:disabled="isSavingConfig" :disabled="isSavingConfig"
class="mt-1 w-full bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white rounded py-1.5 text-sm font-medium transition-colors" class="mt-1 w-full bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white rounded py-1.5 text-sm font-medium transition-colors"
> >
{{ isSavingConfig ? "Saving..." : "Save Settings" }} {{ isSavingConfig ? "Checking..." : "Save Settings" }}
</button> </button>
<div v-if="saveMessage" class="mt-2 text-center text-xs font-medium" :class="saveMessage.includes('✅') ? 'text-green-500' : 'text-amber-500'">
{{ saveMessage }}
</div>
</div> </div>
</div> </div>
......
This diff is collapsed.
...@@ -63,6 +63,34 @@ async function initialize(): Promise<void> { ...@@ -63,6 +63,34 @@ async function initialize(): Promise<void> {
logMessage(`SuperAssistant ready on ${adapter.name} ⚡`) logMessage(`SuperAssistant ready on ${adapter.name} ⚡`)
} }
// ─── System Prompt Fallback ─────────────────────────────────────────
const SYSTEM_PROMPT_FALLBACK = `You are an expert n8n workflow automation engineer with direct API access to a live n8n instance through MCP (Model Context Protocol) tools. Your role is to design, build, modify, validate, and execute n8n workflows with maximum accuracy and efficiency.
## Available MCP Tools
Note: Since you are connected directly to n8n, tools DO NOT have the 'n8n_' prefix.
- list_workflows: List all workflows
- get_workflow: Get workflow JSON (full/structure/minimal)
- create_workflow: Create new workflow
- update_workflow: Full workflow update (replace)
- execute_workflow: Trigger workflow with input data
- list_credentials: List credential names/types
## Workflow Modification Process
1. Read Current State (get_workflow)
2. Understand Structure (identify nodes and connections)
3. Plan Changes (tell user)
4. Build & Update (update_workflow with COMPLETE JSON)
5. Verify (get_workflow structure)
## Critical Warnings
- Use \`={{ $json.field }}\` syntax for expressions.
- ALWAYS use the LATEST stable typeVersion for each node.
- Connections use SOURCE NODE NAME as key.
Respond in the SAME LANGUAGE as the user's message.
`;
// ─── Context Injection ────────────────────────────────────────────── // ─── Context Injection ──────────────────────────────────────────────
/** /**
...@@ -84,18 +112,24 @@ async function injectWorkflowContext(adapter: any): Promise<void> { ...@@ -84,18 +112,24 @@ async function injectWorkflowContext(adapter: any): Promise<void> {
// Store context globally for bridge to auto-attach to tool calls // Store context globally for bridge to auto-attach to tool calls
;(window as any).__n8nWorkflowContext = ctx ;(window as any).__n8nWorkflowContext = ctx
// Fetch system prompt from MCP proxy // Fetch system prompt from MCP proxy (optional fallback)
let systemPrompt = "" let systemPrompt = SYSTEM_PROMPT_FALLBACK
try { try {
const mcpConfig = await sendMessage({ type: "n8n-mcp:get-config" }) const mcpConfig = await sendMessage({ type: "n8n-mcp:get-config" })
const proxyUrl = mcpConfig?.proxyUrl || "http://localhost:3006" const proxyUrl = mcpConfig?.proxyUrl || "http://localhost:3006"
// If using local proxy, try to fetch its custom prompt
if (proxyUrl.includes("localhost") || proxyUrl.includes("127.0.0.1")) {
const res = await fetch(`${proxyUrl}/system-prompt`) const res = await fetch(`${proxyUrl}/system-prompt`)
if (res.ok) { if (res.ok) {
systemPrompt = await res.text() systemPrompt = await res.text()
logger.info("✅ System prompt loaded from MCP proxy") logger.info("✅ System prompt loaded from local proxy")
}
} else {
logger.info("ℹ️ Native MCP detected — using embedded system prompt")
} }
} catch (err) { } catch (err) {
logger.info("⚠️ Could not fetch system prompt — using inline fallback") logger.info("⚠️ Using embedded system prompt fallback")
} }
// Build the final prompt: system prompt + workflow context // Build the final prompt: system prompt + workflow context
...@@ -108,6 +142,7 @@ async function injectWorkflowContext(adapter: any): Promise<void> { ...@@ -108,6 +142,7 @@ async function injectWorkflowContext(adapter: any): Promise<void> {
const inserted = await adapter.insertText(prompt) const inserted = await adapter.insertText(prompt)
if (inserted) { if (inserted) {
logger.info("✅ Workflow context injected into chat input") logger.info("✅ Workflow context injected into chat input")
// Trigger auto-send if the prompt is large (optional, but better for UX)
} else { } else {
logger.info("⚠️ Could not inject context — chat input not found (user may type manually)") logger.info("⚠️ Could not inject context — chat input not found (user may type manually)")
} }
...@@ -115,33 +150,29 @@ async function injectWorkflowContext(adapter: any): Promise<void> { ...@@ -115,33 +150,29 @@ async function injectWorkflowContext(adapter: any): Promise<void> {
function buildContextPrompt(ctx: any, systemPrompt: string): string { function buildContextPrompt(ctx: any, systemPrompt: string): string {
const contextBlock = [ const contextBlock = [
`I'm working on an n8n workflow.`, `--- WORKFLOW CONTEXT ---`,
`I'm working on this n8n workflow:`,
`ID: ${ctx.workflowId}`,
`Name: ${ctx.workflowName}`,
`URL: ${ctx.n8nBaseUrl}/workflow/${ctx.workflowId}`,
``, ``,
`**Workflow ID:** ${ctx.workflowId}`, `Current structure (JSON):`,
`**Name:** ${ctx.workflowName}`,
`**Instance:** ${ctx.n8nBaseUrl}`,
``,
`**Current structure:**`,
"```json", "```json",
ctx.workflowSummary, ctx.workflowSummary,
"```", "```",
`-----------------------`,
``, ``,
`Please help me modify this workflow. Use the n8n MCP tools ` + `I want you to help me with this workflow. Please use the MCP tools available to you to read or modify it.`,
`(n8n_get_workflow, n8n_update_workflow, etc.) targeting workflow ID \`${ctx.workflowId}\`.`,
].join("\n") ].join("\n")
// If system prompt available, prepend it (hidden in a system block)
if (systemPrompt) {
return [ return [
`<system>`, `# INSTRUCTIONS`,
systemPrompt, systemPrompt,
`</system>`,
``, ``,
contextBlock, contextBlock,
``,
`My request: `,
].join("\n") ].join("\n")
}
return contextBlock
} }
// ─── Helpers ──────────────────────────────────────────────────────── // ─── Helpers ────────────────────────────────────────────────────────
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment