cortex-md 1.1.1
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 +172 -0
- package/bin/cortex.js +18 -0
- package/dist/electron/main.cjs +302 -0
- package/dist/electron/preload.cjs +15 -0
- package/dist/icons/icon-1024.png +0 -0
- package/dist/icons/icon-256.png +0 -0
- package/dist/icons/icon.ico +0 -0
- package/dist/icons/icon.png +0 -0
- package/dist/icons/icon.svg +15 -0
- package/dist/index.cjs +798 -0
- package/dist/public/assets/cortex-icon-BKk7C7Sc.png +0 -0
- package/dist/public/assets/index-B-Fl-W-j.css +1 -0
- package/dist/public/assets/index-BDi6a3Vh.js +121 -0
- package/dist/public/index.html +30 -0
- package/electron/icons/icon-1024.png +0 -0
- package/electron/icons/icon-256.png +0 -0
- package/electron/icons/icon.ico +0 -0
- package/electron/icons/icon.png +0 -0
- package/electron/icons/icon.svg +15 -0
- package/package.json +145 -0
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Cortex — Personal AI Operating System
|
|
2
|
+
|
|
3
|
+
[](https://github.com/angshuman/cortex/releases/latest)
|
|
4
|
+
[](https://github.com/angshuman/cortex/releases/latest)
|
|
5
|
+
[](https://github.com/angshuman/cortex/actions)
|
|
6
|
+
|
|
7
|
+
A local-first personal operating system with an AI reasoner, note-taking, task management, and browser automation via MCP. Built with Node.js, TypeScript, React, and Tailwind CSS.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Download
|
|
12
|
+
|
|
13
|
+
**[Go to the Releases page to download Cortex](https://github.com/angshuman/cortex/releases/latest)**
|
|
14
|
+
|
|
15
|
+
### Latest Release
|
|
16
|
+
|
|
17
|
+
| Platform | Architecture | Download |
|
|
18
|
+
|----------|-------------|----------|
|
|
19
|
+
| **Windows** | x64 / ARM64 | [Cortex-Setup.exe](https://github.com/angshuman/cortex/releases/latest/download/Cortex-Setup-1.1.1.exe) |
|
|
20
|
+
| **macOS** | Apple Silicon | [Cortex-arm64.dmg](https://github.com/angshuman/cortex/releases/latest/download/Cortex-1.1.1-arm64.dmg) |
|
|
21
|
+
| **macOS** | Intel | [Cortex.dmg](https://github.com/angshuman/cortex/releases/latest/download/Cortex-1.1.1.dmg) |
|
|
22
|
+
| **Linux** | x64 | [Cortex.AppImage](https://github.com/angshuman/cortex/releases/latest/download/Cortex-1.1.1.AppImage) |
|
|
23
|
+
| **Linux** | ARM64 | [Cortex-arm64.AppImage](https://github.com/angshuman/cortex/releases/latest/download/Cortex-1.1.1-arm64.AppImage) |
|
|
24
|
+
| **Linux** | x64 (deb) | [cortex_amd64.deb](https://github.com/angshuman/cortex/releases/latest/download/cortex_1.1.1_amd64.deb) |
|
|
25
|
+
| **Linux** | ARM64 (deb) | [cortex_arm64.deb](https://github.com/angshuman/cortex/releases/latest/download/cortex_1.1.1_arm64.deb) |
|
|
26
|
+
|
|
27
|
+
> On first launch, a setup dialog asks for an API key (OpenAI, Anthropic, xAI, or Google Gemini). No environment variables needed.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **AI Chat & Reasoning** — Converse with Claude, OpenAI, Grok, or Gemini. The agent reasons through problems, creates notes, manages tasks, and uses tools.
|
|
34
|
+
- **Notes** — Full markdown editor with image support (paste screenshots), folder hierarchy, inbox quick dump, and rendered preview.
|
|
35
|
+
- **Tasks** — Pure no-AI task management with kanban and list views, subtasks, priorities, due dates, and status tracking.
|
|
36
|
+
- **Search** — Unified search across notes, tasks, and chat history. Configurable for local keyword or OpenAI vector search.
|
|
37
|
+
- **Skills System** — Extensible tools the AI agent can use. Built-in skills for note-taking, task management, web search, and browser automation.
|
|
38
|
+
- **Browser Use (MCP)** — Connect a Playwright MCP server for real browser automation.
|
|
39
|
+
- **Multi-Vault Workspaces** — Separate vaults for work, personal, etc. Each with its own notes, tasks, and chats.
|
|
40
|
+
- **API Key Management** — Add, verify, and manage API keys from the UI. No env vars required.
|
|
41
|
+
- **Local Filesystem** — All data stored as plain JSON and Markdown files. Human-readable, navigable, portable.
|
|
42
|
+
- **Sync** — Point vault folders to OneDrive, Google Drive, USB, or any path for portability.
|
|
43
|
+
- **Desktop App** — Native Electron app with system tray, single-instance lock, and platform menus.
|
|
44
|
+
|
|
45
|
+
## Data Structure
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
.cortex-data/
|
|
49
|
+
├── vaults/
|
|
50
|
+
│ ├── personal/
|
|
51
|
+
│ │ ├── notes/ # Markdown files + index
|
|
52
|
+
│ │ ├── tasks/ # tasks.json
|
|
53
|
+
│ │ ├── chat/ # Session files
|
|
54
|
+
│ │ ├── skills/ # Skill definitions
|
|
55
|
+
│ │ └── files/ # Uploaded assets
|
|
56
|
+
│ └── work/
|
|
57
|
+
│ └── ...
|
|
58
|
+
├── vaults.json # Vault registry
|
|
59
|
+
└── config.json # Global settings + API keys
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start (Development)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://github.com/angshuman/cortex.git
|
|
66
|
+
cd cortex
|
|
67
|
+
npm install
|
|
68
|
+
npm run dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Open [http://localhost:5000](http://localhost:5000) — the API key setup dialog will appear on first launch.
|
|
72
|
+
|
|
73
|
+
You can also set keys via environment variables:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# macOS / Linux / Git Bash
|
|
77
|
+
export OPENAI_API_KEY=sk-...
|
|
78
|
+
|
|
79
|
+
# Windows PowerShell
|
|
80
|
+
$env:OPENAI_API_KEY = "sk-..."
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
> **Tip:** Create a `.env` file in the project root for persistent config:
|
|
84
|
+
> ```
|
|
85
|
+
> OPENAI_API_KEY=sk-...
|
|
86
|
+
> CORTEX_DATA_DIR=C:\Users\you\OneDrive\cortex-data
|
|
87
|
+
> ```
|
|
88
|
+
|
|
89
|
+
## Electron Desktop App
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Dev mode — build + launch
|
|
93
|
+
npm run electron:dev
|
|
94
|
+
|
|
95
|
+
# Package for your platform
|
|
96
|
+
npm run electron:pack:win # Windows .exe installer
|
|
97
|
+
npm run electron:pack:mac # macOS .dmg
|
|
98
|
+
npm run electron:pack:linux # Linux .AppImage + .deb
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Configuration
|
|
102
|
+
|
|
103
|
+
### Environment Variables
|
|
104
|
+
|
|
105
|
+
| Variable | Description |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `ANTHROPIC_API_KEY` | Claude API key |
|
|
108
|
+
| `OPENAI_API_KEY` | OpenAI API key |
|
|
109
|
+
| `GROK_API_KEY` | Grok/xAI API key |
|
|
110
|
+
| `GOOGLE_API_KEY` | Google Gemini API key |
|
|
111
|
+
| `CORTEX_DATA_DIR` | Custom data directory (default: `.cortex-data/`) |
|
|
112
|
+
|
|
113
|
+
### Settings UI
|
|
114
|
+
|
|
115
|
+
- **API Keys** — Add, verify, and manage provider keys from Settings > General.
|
|
116
|
+
- **AI Provider** — Auto-detected from first configured key. Override model per-vault.
|
|
117
|
+
- **Vector Search** — Local keyword or OpenAI embeddings.
|
|
118
|
+
- **MCP Servers** — Add Playwright or custom MCP servers for extended capabilities.
|
|
119
|
+
- **Agent Tuning** — Max turns, temperature, token limits, custom system prompt.
|
|
120
|
+
- **Data Directory** — Shown in settings. Sync vault folders for portability.
|
|
121
|
+
|
|
122
|
+
## Skills
|
|
123
|
+
|
|
124
|
+
Built-in skills (always available):
|
|
125
|
+
|
|
126
|
+
| Skill | Tools | Description |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| **note-taker** | `create_note`, `read_note`, `list_notes`, `update_note` | Manage notes |
|
|
129
|
+
| **task-manager** | `create_task`, `list_tasks`, `update_task`, `complete_task` | Manage tasks |
|
|
130
|
+
| **web-search** | `web_search` | Search the web |
|
|
131
|
+
| **browser-use** | `browser_navigate`, `browser_screenshot`, `browser_click`, `browser_type`, `browser_snapshot` | Browser automation via MCP |
|
|
132
|
+
|
|
133
|
+
Add custom skills via the Skills JSON in your vault's `skills/skills.json`.
|
|
134
|
+
|
|
135
|
+
## Tech Stack
|
|
136
|
+
|
|
137
|
+
- **Backend**: Express + TypeScript, filesystem storage, WebSocket for real-time chat
|
|
138
|
+
- **Frontend**: React + Vite + Tailwind CSS + shadcn/ui
|
|
139
|
+
- **Desktop**: Electron with system tray and native menus
|
|
140
|
+
- **AI**: Anthropic SDK, OpenAI SDK (also used for Grok and Gemini via compatible API)
|
|
141
|
+
- **Markdown**: `marked` for rendering
|
|
142
|
+
- **Icons**: `lucide-react`
|
|
143
|
+
- **Build**: Vite (client) + esbuild (server + Electron) + electron-builder (packaging)
|
|
144
|
+
- **CI/CD**: GitHub Actions — auto-builds Windows/macOS/Linux on tag push
|
|
145
|
+
|
|
146
|
+
## Architecture
|
|
147
|
+
|
|
148
|
+
- No database — everything is JSON/Markdown files on disk
|
|
149
|
+
- AI agent has a reasoning loop with tool use (configurable depth per message)
|
|
150
|
+
- WebSocket streams agent events (thoughts, tool calls, results, messages) to the UI
|
|
151
|
+
- Skills define tools the agent can invoke, with parameters and instructions
|
|
152
|
+
- MCP browser support is built into the skill system
|
|
153
|
+
- Electron wraps the Express server + React client into a native desktop app
|
|
154
|
+
|
|
155
|
+
## Code Signing
|
|
156
|
+
|
|
157
|
+
The binaries are currently unsigned. To enable code signing, add these GitHub repository secrets:
|
|
158
|
+
|
|
159
|
+
**Windows:**
|
|
160
|
+
- `WIN_CERT_P12_BASE64` — Base64-encoded .p12 certificate
|
|
161
|
+
- `WIN_CERT_PASSWORD` — Certificate password
|
|
162
|
+
|
|
163
|
+
**macOS:**
|
|
164
|
+
- `MAC_CERT_P12_BASE64` — Base64-encoded Developer ID Application .p12
|
|
165
|
+
- `MAC_CERT_PASSWORD` — Certificate password
|
|
166
|
+
- `APPLE_ID` — Apple ID for notarization
|
|
167
|
+
- `APPLE_APP_SPECIFIC_PASSWORD` — App-specific password
|
|
168
|
+
- `APPLE_TEAM_ID` — Apple Developer Team ID
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT
|
package/bin/cortex.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
|
|
10
|
+
const electronPath = require('electron');
|
|
11
|
+
const mainPath = path.join(__dirname, '..', 'dist', 'electron', 'main.cjs');
|
|
12
|
+
|
|
13
|
+
const child = spawn(electronPath, [mainPath], {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
windowsHide: false,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
child.on('close', (code) => process.exit(code ?? 0));
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// electron/main.ts
|
|
26
|
+
var import_electron = require("electron");
|
|
27
|
+
var import_path = __toESM(require("path"), 1);
|
|
28
|
+
var import_net = __toESM(require("net"), 1);
|
|
29
|
+
var gotLock = import_electron.app.requestSingleInstanceLock();
|
|
30
|
+
if (!gotLock) {
|
|
31
|
+
import_electron.app.quit();
|
|
32
|
+
}
|
|
33
|
+
var mainWindow = null;
|
|
34
|
+
var tray = null;
|
|
35
|
+
var serverPort = 0;
|
|
36
|
+
function getDataDir() {
|
|
37
|
+
if (process.env.CORTEX_DATA_DIR) {
|
|
38
|
+
return process.env.CORTEX_DATA_DIR;
|
|
39
|
+
}
|
|
40
|
+
return import_path.default.join(import_electron.app.getPath("userData"), ".cortex-data");
|
|
41
|
+
}
|
|
42
|
+
function findAvailablePort() {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const srv = import_net.default.createServer();
|
|
45
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
46
|
+
const addr = srv.address();
|
|
47
|
+
if (addr && typeof addr !== "string") {
|
|
48
|
+
const port = addr.port;
|
|
49
|
+
srv.close(() => resolve(port));
|
|
50
|
+
} else {
|
|
51
|
+
reject(new Error("Could not find available port"));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
srv.on("error", reject);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function startServer(port) {
|
|
58
|
+
process.env.NODE_ENV = "production";
|
|
59
|
+
process.env.PORT = String(port);
|
|
60
|
+
process.env.CORTEX_DATA_DIR = getDataDir();
|
|
61
|
+
process.env.ELECTRON = "1";
|
|
62
|
+
const serverPath = import_electron.app.isPackaged ? import_path.default.join(process.resourcesPath, "server", "index.cjs") : import_path.default.join(__dirname, "..", "index.cjs");
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
try {
|
|
65
|
+
console.log(`[Cortex] Loading server from: ${serverPath}`);
|
|
66
|
+
require(serverPath);
|
|
67
|
+
console.log("[Cortex] Server module loaded, waiting for port...");
|
|
68
|
+
let attempts = 0;
|
|
69
|
+
const maxAttempts = 300;
|
|
70
|
+
const check = setInterval(() => {
|
|
71
|
+
attempts++;
|
|
72
|
+
const testConn = import_net.default.createConnection({ port, host: "127.0.0.1" }, () => {
|
|
73
|
+
testConn.end();
|
|
74
|
+
clearInterval(check);
|
|
75
|
+
resolve();
|
|
76
|
+
});
|
|
77
|
+
testConn.on("error", () => {
|
|
78
|
+
if (attempts >= maxAttempts) {
|
|
79
|
+
clearInterval(check);
|
|
80
|
+
reject(new Error("Server did not start within 30 seconds"));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}, 100);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error("[Cortex] Failed to load server module:", err);
|
|
86
|
+
reject(err);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function createWindow() {
|
|
91
|
+
mainWindow = new import_electron.BrowserWindow({
|
|
92
|
+
width: 1280,
|
|
93
|
+
height: 800,
|
|
94
|
+
minWidth: 900,
|
|
95
|
+
minHeight: 600,
|
|
96
|
+
title: "Cortex",
|
|
97
|
+
icon: getIconPath(),
|
|
98
|
+
backgroundColor: "#0e1117",
|
|
99
|
+
show: false,
|
|
100
|
+
webPreferences: {
|
|
101
|
+
preload: import_path.default.join(__dirname, "preload.cjs"),
|
|
102
|
+
nodeIntegration: false,
|
|
103
|
+
contextIsolation: true,
|
|
104
|
+
spellcheck: true
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
mainWindow.loadURL(`http://127.0.0.1:${serverPort}`);
|
|
108
|
+
mainWindow.once("ready-to-show", () => {
|
|
109
|
+
mainWindow?.show();
|
|
110
|
+
});
|
|
111
|
+
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
112
|
+
if (url.startsWith("http://127.0.0.1") || url.startsWith("http://localhost")) {
|
|
113
|
+
return { action: "allow" };
|
|
114
|
+
}
|
|
115
|
+
import_electron.shell.openExternal(url);
|
|
116
|
+
return { action: "deny" };
|
|
117
|
+
});
|
|
118
|
+
mainWindow.on("close", (event) => {
|
|
119
|
+
if (process.platform === "darwin" && tray) {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
mainWindow?.hide();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
mainWindow.on("closed", () => {
|
|
125
|
+
mainWindow = null;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function getIconPath() {
|
|
129
|
+
if (import_electron.app.isPackaged) {
|
|
130
|
+
return import_path.default.join(process.resourcesPath, "icons", "icon.png");
|
|
131
|
+
}
|
|
132
|
+
const iconPath = import_path.default.join(__dirname, "..", "..", "electron", "icons", "icon.png");
|
|
133
|
+
return iconPath;
|
|
134
|
+
}
|
|
135
|
+
function createTray() {
|
|
136
|
+
const iconPath = getIconPath();
|
|
137
|
+
let trayIcon;
|
|
138
|
+
try {
|
|
139
|
+
trayIcon = import_electron.nativeImage.createFromPath(iconPath);
|
|
140
|
+
if (trayIcon.isEmpty()) {
|
|
141
|
+
trayIcon = import_electron.nativeImage.createEmpty();
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
trayIcon = import_electron.nativeImage.createEmpty();
|
|
145
|
+
}
|
|
146
|
+
if (!trayIcon.isEmpty()) {
|
|
147
|
+
trayIcon = trayIcon.resize({ width: 16, height: 16 });
|
|
148
|
+
}
|
|
149
|
+
tray = new import_electron.Tray(trayIcon);
|
|
150
|
+
tray.setToolTip("Cortex");
|
|
151
|
+
const contextMenu = import_electron.Menu.buildFromTemplate([
|
|
152
|
+
{
|
|
153
|
+
label: "Open Cortex",
|
|
154
|
+
click: () => {
|
|
155
|
+
if (mainWindow) {
|
|
156
|
+
mainWindow.show();
|
|
157
|
+
mainWindow.focus();
|
|
158
|
+
} else {
|
|
159
|
+
createWindow();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
{ type: "separator" },
|
|
164
|
+
{
|
|
165
|
+
label: `Data: ${getDataDir()}`,
|
|
166
|
+
enabled: false
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
label: "Open Data Folder",
|
|
170
|
+
click: () => {
|
|
171
|
+
import_electron.shell.openPath(getDataDir());
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
{ type: "separator" },
|
|
175
|
+
{
|
|
176
|
+
label: "Quit Cortex",
|
|
177
|
+
click: () => {
|
|
178
|
+
import_electron.app.quit();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
]);
|
|
182
|
+
tray.setContextMenu(contextMenu);
|
|
183
|
+
tray.on("double-click", () => {
|
|
184
|
+
if (mainWindow) {
|
|
185
|
+
mainWindow.show();
|
|
186
|
+
mainWindow.focus();
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function setupMenu() {
|
|
191
|
+
const template = [
|
|
192
|
+
{
|
|
193
|
+
label: import_electron.app.name,
|
|
194
|
+
submenu: [
|
|
195
|
+
{ role: "about" },
|
|
196
|
+
{ type: "separator" },
|
|
197
|
+
{
|
|
198
|
+
label: "Open Data Folder",
|
|
199
|
+
click: () => import_electron.shell.openPath(getDataDir())
|
|
200
|
+
},
|
|
201
|
+
{ type: "separator" },
|
|
202
|
+
{ role: "hide" },
|
|
203
|
+
{ role: "hideOthers" },
|
|
204
|
+
{ role: "unhide" },
|
|
205
|
+
{ type: "separator" },
|
|
206
|
+
{ role: "quit" }
|
|
207
|
+
]
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
label: "Edit",
|
|
211
|
+
submenu: [
|
|
212
|
+
{ role: "undo" },
|
|
213
|
+
{ role: "redo" },
|
|
214
|
+
{ type: "separator" },
|
|
215
|
+
{ role: "cut" },
|
|
216
|
+
{ role: "copy" },
|
|
217
|
+
{ role: "paste" },
|
|
218
|
+
{ role: "selectAll" }
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
label: "View",
|
|
223
|
+
submenu: [
|
|
224
|
+
{ role: "reload" },
|
|
225
|
+
{ role: "forceReload" },
|
|
226
|
+
{ role: "toggleDevTools" },
|
|
227
|
+
{ type: "separator" },
|
|
228
|
+
{ role: "resetZoom" },
|
|
229
|
+
{ role: "zoomIn" },
|
|
230
|
+
{ role: "zoomOut" },
|
|
231
|
+
{ type: "separator" },
|
|
232
|
+
{ role: "togglefullscreen" }
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
label: "Window",
|
|
237
|
+
submenu: [
|
|
238
|
+
{ role: "minimize" },
|
|
239
|
+
{ role: "zoom" },
|
|
240
|
+
{ type: "separator" },
|
|
241
|
+
{ role: "front" }
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
];
|
|
245
|
+
import_electron.Menu.setApplicationMenu(import_electron.Menu.buildFromTemplate(template));
|
|
246
|
+
}
|
|
247
|
+
import_electron.app.on("ready", async () => {
|
|
248
|
+
import_electron.ipcMain.handle("open-folder", async (_event, folderPath) => {
|
|
249
|
+
if (folderPath && typeof folderPath === "string") {
|
|
250
|
+
return import_electron.shell.openPath(folderPath);
|
|
251
|
+
}
|
|
252
|
+
return "Invalid path";
|
|
253
|
+
});
|
|
254
|
+
try {
|
|
255
|
+
serverPort = await findAvailablePort();
|
|
256
|
+
console.log(`[Cortex] Starting server on port ${serverPort}...`);
|
|
257
|
+
console.log(`[Cortex] Data directory: ${getDataDir()}`);
|
|
258
|
+
await startServer(serverPort);
|
|
259
|
+
console.log(`[Cortex] Server ready at http://127.0.0.1:${serverPort}`);
|
|
260
|
+
setupMenu();
|
|
261
|
+
createTray();
|
|
262
|
+
createWindow();
|
|
263
|
+
} catch (err) {
|
|
264
|
+
console.error("[Cortex] Failed to start:", err);
|
|
265
|
+
import_electron.dialog.showErrorBox(
|
|
266
|
+
"Cortex failed to start",
|
|
267
|
+
`Error: ${err instanceof Error ? err.message : String(err)}
|
|
268
|
+
|
|
269
|
+
Please check your configuration and try again.`
|
|
270
|
+
);
|
|
271
|
+
import_electron.app.quit();
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
import_electron.app.on("second-instance", () => {
|
|
275
|
+
if (mainWindow) {
|
|
276
|
+
if (mainWindow.isMinimized()) mainWindow.restore();
|
|
277
|
+
mainWindow.show();
|
|
278
|
+
mainWindow.focus();
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
import_electron.app.on("window-all-closed", () => {
|
|
282
|
+
if (process.platform !== "darwin") {
|
|
283
|
+
import_electron.app.quit();
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
import_electron.app.on("activate", () => {
|
|
287
|
+
if (mainWindow === null) {
|
|
288
|
+
createWindow();
|
|
289
|
+
} else {
|
|
290
|
+
mainWindow.show();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
import_electron.app.on("before-quit", () => {
|
|
294
|
+
if (mainWindow) {
|
|
295
|
+
mainWindow.removeAllListeners("close");
|
|
296
|
+
mainWindow.close();
|
|
297
|
+
}
|
|
298
|
+
if (tray) {
|
|
299
|
+
tray.destroy();
|
|
300
|
+
tray = null;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// electron/preload.ts
|
|
4
|
+
var import_electron = require("electron");
|
|
5
|
+
import_electron.contextBridge.exposeInMainWorld("cortexDesktop", {
|
|
6
|
+
platform: process.platform,
|
|
7
|
+
isElectron: true,
|
|
8
|
+
versions: {
|
|
9
|
+
electron: process.versions.electron,
|
|
10
|
+
node: process.versions.node,
|
|
11
|
+
chrome: process.versions.chrome
|
|
12
|
+
},
|
|
13
|
+
/** Open a folder in the native file manager (Explorer / Finder) */
|
|
14
|
+
openFolder: (folderPath) => import_electron.ipcRenderer.invoke("open-folder", folderPath)
|
|
15
|
+
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<rect width="512" height="512" rx="108" fill="#111318"/>
|
|
3
|
+
<g fill="none" stroke="#e5e7eb" stroke-linecap="round" stroke-linejoin="round">
|
|
4
|
+
<!-- C arc -->
|
|
5
|
+
<path d="M 320,126 C 270,90 210,80 165,100 C 100,128 66,190 66,256 C 66,322 100,384 165,412 C 210,432 270,422 320,386" stroke-width="36"/>
|
|
6
|
+
<!-- Three connector lines from the C opening -->
|
|
7
|
+
<line x1="320" y1="126" x2="380" y2="126" stroke-width="28"/>
|
|
8
|
+
<line x1="345" y1="256" x2="405" y2="256" stroke-width="28"/>
|
|
9
|
+
<line x1="320" y1="386" x2="380" y2="386" stroke-width="28"/>
|
|
10
|
+
</g>
|
|
11
|
+
<!-- Terminal dots -->
|
|
12
|
+
<circle cx="388" cy="126" r="18" fill="#e5e7eb"/>
|
|
13
|
+
<circle cx="413" cy="256" r="18" fill="#e5e7eb"/>
|
|
14
|
+
<circle cx="388" cy="386" r="18" fill="#e5e7eb"/>
|
|
15
|
+
</svg>
|