engine7 7.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/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # Engine 7
2
+
3
+ Self-hosted AI agent engine with multi-platform support (Discord/Feishu/WeChat), persistent memory, calendar, and voice chat.
4
+
5
+ ## Features
6
+
7
+ - 🤖 Multi-LLM provider support (Anthropic/OpenAI/Gemini compatible)
8
+ - 💾 Persistent memory system with semantic search
9
+ - 📅 Calendar with task management and reminders
10
+ - 🎤 Voice chat integration (WebRTC + TTS)
11
+ - 🔄 Cron jobs and background tasks
12
+ - 🌐 Cross-platform (Windows/Linux/macOS)
13
+
14
+ ## Requirements
15
+
16
+ - Node.js >= 22
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install -g engine7
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```bash
27
+ engine7 init
28
+ ```
29
+
30
+ This creates a workspace with templates for:
31
+ - Agent configuration (SOUL.md, AGENTS.md)
32
+ - Memory system (MEMORY.md, topics/)
33
+ - Session state tracking (SESSION-STATE.md)
34
+ - Heartbeat automation (HEARTBEAT.md)
35
+
36
+ ## Development
37
+
38
+ ```bash
39
+ # Build bundles
40
+ npm run build
41
+
42
+ # Start engine
43
+ npm start
44
+ ```
45
+
46
+ ## Architecture
47
+
48
+ - **Bundled with esbuild**: Three entry points (main, cli, engine-startup)
49
+ - **ESM native**: Modern JavaScript modules
50
+ - **SQLite storage**: Persistent sessions and memory
51
+ - **Plugin system**: Extendable tools and providers
52
+
53
+ ## License
54
+
55
+ MIT
package/bin/engine7 ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import('../dist/cli.mjs')
@@ -0,0 +1,2 @@
1
+ @echo off
2
+ node "%~dp0\..\dist\cli.mjs" %*
package/dist/cli.mjs ADDED
@@ -0,0 +1,424 @@
1
+ // src/cli-init.ts
2
+ import * as path from "node:path";
3
+ import * as fs from "node:fs";
4
+ import * as readline from "node:readline";
5
+ import { fileURLToPath } from "node:url";
6
+ var __filename = fileURLToPath(import.meta.url);
7
+ var __dirname = path.dirname(__filename);
8
+ var SCHEMA_VERSION = 1;
9
+ function parseArgs() {
10
+ const args = process.argv.slice(2);
11
+ let stateDir = "";
12
+ let quick = false;
13
+ let dryRun2 = false;
14
+ let startIdx = 0;
15
+ if (args[0] === "init") startIdx = 1;
16
+ for (let i = startIdx; i < args.length; i++) {
17
+ if (args[i] === "--state-dir" && args[i + 1]) {
18
+ stateDir = args[++i];
19
+ } else if (args[i] === "--quick") {
20
+ quick = true;
21
+ } else if (args[i] === "--dry-run") {
22
+ dryRun2 = true;
23
+ } else if (args[i] === "--help" || args[i] === "-h") {
24
+ printHelp();
25
+ process.exit(0);
26
+ }
27
+ }
28
+ if (!stateDir) {
29
+ console.error("\u274C \u7F3A\u5C11 --state-dir \u53C2\u6570");
30
+ console.error(" \u7528\u6CD5: engine7 init --state-dir <path>");
31
+ process.exit(1);
32
+ }
33
+ stateDir = path.resolve(stateDir);
34
+ return { stateDir, quick, dryRun: dryRun2 };
35
+ }
36
+ function printHelp() {
37
+ console.log(`
38
+ engine7 init \u2014 \u521D\u59CB\u5316 agent \u5DE5\u4F5C\u76EE\u5F55
39
+
40
+ \u7528\u6CD5:
41
+ engine7 init --state-dir <path> [\u9009\u9879]
42
+
43
+ \u9009\u9879:
44
+ --state-dir <path> agent \u6839\u76EE\u5F55\uFF08\u5FC5\u586B\uFF0C\u652F\u6301\u76F8\u5BF9\u8DEF\u5F84\uFF09
45
+ --quick \u7528\u9ED8\u8BA4\u914D\u7F6E\u8DF3\u8FC7\u4EA4\u4E92
46
+ --dry-run \u53EA\u663E\u793A\u4F1A\u521B\u5EFA\u4EC0\u4E48\uFF0C\u4E0D\u5B9E\u9645\u521B\u5EFA
47
+ -h, --help \u663E\u793A\u5E2E\u52A9
48
+
49
+ \u793A\u4F8B:
50
+ engine7 init --state-dir D:/my-agent
51
+ engine7 init --state-dir ./my-agent --quick
52
+ engine7 init --state-dir D:/my-agent --dry-run
53
+ `);
54
+ }
55
+ function createReadline() {
56
+ return readline.createInterface({
57
+ input: process.stdin,
58
+ output: process.stdout
59
+ });
60
+ }
61
+ async function ask(rl, question, defaultValue) {
62
+ const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
63
+ return new Promise((resolve2) => {
64
+ rl.question(prompt, (answer) => {
65
+ resolve2(answer.trim() || defaultValue || "");
66
+ });
67
+ });
68
+ }
69
+ async function askChoice(rl, question, choices, defaultIdx = 0) {
70
+ console.log(question);
71
+ choices.forEach((c, i) => console.log(` ${i + 1}. ${c}`));
72
+ const answer = await ask(rl, `\u9009\u62E9 (1-${choices.length})`, String(defaultIdx + 1));
73
+ const idx = parseInt(answer, 10) - 1;
74
+ return choices[idx >= 0 && idx < choices.length ? idx : defaultIdx];
75
+ }
76
+ function getDefaultValues(stateDir) {
77
+ return {
78
+ stateDir,
79
+ agentName: "Agent",
80
+ primaryProvider: "dashscope",
81
+ primaryApiKey: "",
82
+ primaryModel: "dashscope/qwen3.7-max",
83
+ visionModel: "dashscope/qwen3.7-max",
84
+ discordEnabled: false,
85
+ discordToken: "",
86
+ discordUserId: "",
87
+ feishuEnabled: false,
88
+ feishuAppId: "",
89
+ feishuAppSecret: "",
90
+ feishuOpenId: "",
91
+ apiPort: 16990
92
+ };
93
+ }
94
+ async function interactiveConfig(rl, defaults) {
95
+ const v = { ...defaults };
96
+ console.log("\n=== \u7B2C\u4E00\u6B65\uFF1A\u5FC5\u586B\u914D\u7F6E ===\n");
97
+ v.agentName = await ask(rl, "Agent \u540D\u79F0", defaults.agentName);
98
+ const providers = ["dashscope (\u901A\u4E49\u5343\u95EE)", "minimax (MiniMax)", "zhipu (\u667A\u8C31)", "deepseek (DeepSeek)"];
99
+ const providerMap = {
100
+ "dashscope (\u901A\u4E49\u5343\u95EE)": "dashscope",
101
+ "minimax (MiniMax)": "minimax",
102
+ "zhipu (\u667A\u8C31)": "zhipu",
103
+ "deepseek (DeepSeek)": "deepseek"
104
+ };
105
+ const chosenProvider = await askChoice(rl, "\u4E3B\u6A21\u578B Provider:", providers, 0);
106
+ v.primaryProvider = providerMap[chosenProvider] || "dashscope";
107
+ const keyMap = {
108
+ dashscope: "DashScope API Key",
109
+ minimax: "MiniMax API Key",
110
+ zhipu: "\u667A\u8C31 API Key",
111
+ deepseek: "DeepSeek API Key"
112
+ };
113
+ v.primaryApiKey = await ask(rl, keyMap[v.primaryProvider] || "API Key");
114
+ const modelMap = {
115
+ dashscope: ["dashscope/qwen3.7-max", "dashscope/qwen3.7-plus"],
116
+ minimax: ["minimax/MiniMax-M3", "minimax/MiniMax-M2.7"],
117
+ zhipu: ["zhipu/glm-5.1", "zhipu/glm-5v-turbo"],
118
+ deepseek: ["deepseek/deepseek-v4-pro", "deepseek/deepseek-v4-flash"]
119
+ };
120
+ const models = modelMap[v.primaryProvider] || ["dashscope/qwen3.7-max"];
121
+ v.primaryModel = await askChoice(rl, "\u4E3B\u6A21\u578B:", models, 0);
122
+ v.visionModel = v.primaryModel;
123
+ console.log("\n=== \u7B2C\u4E8C\u6B65\uFF1A\u53EF\u9009\u914D\u7F6E\uFF08\u56DE\u8F66\u8DF3\u8FC7\uFF09===\n");
124
+ const discordAns = await ask(rl, "\u542F\u7528 Discord? (y/n)", "n");
125
+ v.discordEnabled = discordAns.toLowerCase() === "y";
126
+ if (v.discordEnabled) {
127
+ v.discordToken = await ask(rl, "Discord Bot Token");
128
+ v.discordUserId = await ask(rl, "\u4F60\u7684 Discord User ID");
129
+ }
130
+ const feishuAns = await ask(rl, "\u542F\u7528\u98DE\u4E66? (y/n)", "n");
131
+ v.feishuEnabled = feishuAns.toLowerCase() === "y";
132
+ if (v.feishuEnabled) {
133
+ v.feishuAppId = await ask(rl, "\u98DE\u4E66 App ID");
134
+ v.feishuAppSecret = await ask(rl, "\u98DE\u4E66 App Secret");
135
+ v.feishuOpenId = await ask(rl, "\u4F60\u7684\u98DE\u4E66 open_id");
136
+ }
137
+ const portAns = await ask(rl, "API \u7AEF\u53E3", String(defaults.apiPort));
138
+ v.apiPort = parseInt(portAns, 10) || defaults.apiPort;
139
+ return v;
140
+ }
141
+ function generateConfig(v) {
142
+ const workspace = path.join(v.stateDir, "workspace").replace(/\\/g, "/");
143
+ const config = {
144
+ schemaVersion: SCHEMA_VERSION,
145
+ stateDir: v.stateDir.replace(/\\/g, "/"),
146
+ mediaDir: `${v.stateDir.replace(/\\/g, "/")}/media/inbound`,
147
+ models: {
148
+ providers: {}
149
+ },
150
+ tools: {},
151
+ session: { dmScope: "main", groupScope: "main" },
152
+ sandbox: { mode: "off" },
153
+ display: {
154
+ thinking: { enabled: false, emoji: "\u{1F4AD}", maxLen: 300 },
155
+ toolUse: { enabled: true, emoji: "\u{1F527}", displayMode: "raw", bashDisplayMode: "both" },
156
+ toolResult: { enabled: false },
157
+ reactions: { enabled: true, start: "\u{1F440}", done: "\u2705", error: "\u274C" },
158
+ preview: { enabled: true, agentName: v.agentName, color: "orange" },
159
+ typing: { enabled: true }
160
+ },
161
+ agents: {
162
+ defaults: {
163
+ workspace,
164
+ name: v.agentName,
165
+ model: {
166
+ primary: v.primaryModel,
167
+ vision: v.visionModel
168
+ },
169
+ features: {
170
+ filesystem: true,
171
+ shell: true,
172
+ webSearch: true,
173
+ webFetch: true,
174
+ memory: true,
175
+ topics: true,
176
+ "topic-extract": true,
177
+ "topic-recall": true,
178
+ cron: true,
179
+ agent: true,
180
+ agentTeams: true,
181
+ processOutput: "minimal",
182
+ interrupt: "command",
183
+ debounceMs: 5e3
184
+ }
185
+ }
186
+ },
187
+ channels: {
188
+ discord: {
189
+ enabled: v.discordEnabled,
190
+ dmPolicy: "pairing",
191
+ groupPolicy: "open",
192
+ accounts: { default: { token: v.discordToken } },
193
+ replyToMode: "all"
194
+ },
195
+ feishu: {
196
+ enabled: v.feishuEnabled,
197
+ appId: v.feishuAppId,
198
+ appSecret: v.feishuAppSecret,
199
+ connectionMode: "websocket",
200
+ dmPolicy: "pairing",
201
+ groupPolicy: "open"
202
+ }
203
+ },
204
+ api: { port: v.apiPort },
205
+ prompt: {
206
+ mode: "custom",
207
+ staticFiles: ["AGENTS.md", "USER.md", "MEMORY.md"],
208
+ order: ["soul", "AGENTS.md", "using-tools", "USER.md", "MEMORY.md", "auto-memory-instructions"]
209
+ },
210
+ compaction: {
211
+ enabled: true,
212
+ bufferTokens: 23e3,
213
+ maxOutputTokens: 16384,
214
+ minReductionRatio: 0.3,
215
+ ruleBased: { enabled: true, essentialFields: [] },
216
+ memoryFlush: { enabled: true, forceFlushTranscriptBytes: "2.0mb" }
217
+ }
218
+ };
219
+ const providerDefs = {
220
+ dashscope: {
221
+ baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
222
+ api: "openai-completions",
223
+ models: [
224
+ { id: "qwen3.7-max", name: "Qwen 3.7 Max", reasoning: true, input: ["text"], contextWindow: 1e6, maxTokens: 65536 },
225
+ { id: "qwen3.7-plus", name: "Qwen 3.7 Plus", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 8192 }
226
+ ]
227
+ },
228
+ minimax: {
229
+ baseUrl: "https://api.minimaxi.com/anthropic",
230
+ api: "anthropic",
231
+ models: [
232
+ { id: "MiniMax-M3", name: "MiniMax M3", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 64e3 },
233
+ { id: "MiniMax-M2.7", name: "MiniMax M2.7", reasoning: true, input: ["text"], contextWindow: 204800, maxTokens: 64e3 }
234
+ ]
235
+ },
236
+ zhipu: {
237
+ baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4 ",
238
+ api: "openai-completions",
239
+ models: [
240
+ { id: "glm-5.1", name: "GLM-5.1", reasoning: true, input: ["text"], contextWindow: 204800, maxTokens: 131072 },
241
+ { id: "glm-5v-turbo", name: "GLM-5V-Turbo", reasoning: true, input: ["text", "image"], contextWindow: 2e5, maxTokens: 128e3 }
242
+ ]
243
+ },
244
+ deepseek: {
245
+ baseUrl: "https://api.deepseek.com/anthropic",
246
+ api: "anthropic",
247
+ thinking: { enabled: true, budgetTokens: 8192 },
248
+ models: [
249
+ { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true, input: ["text"], contextWindow: 1e6, maxTokens: 384e3 },
250
+ { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: true, input: ["text"], contextWindow: 1e6, maxTokens: 384e3 }
251
+ ]
252
+ }
253
+ };
254
+ if (providerDefs[v.primaryProvider]) {
255
+ config.models.providers[v.primaryProvider] = {
256
+ ...providerDefs[v.primaryProvider],
257
+ apiKey: v.primaryApiKey
258
+ };
259
+ }
260
+ return config;
261
+ }
262
+ function buildDirTree(v) {
263
+ const d = v.stateDir;
264
+ const w = path.join(d, "workspace");
265
+ const now = (/* @__PURE__ */ new Date()).toLocaleString("sv-SE", { timeZone: "Asia/Shanghai", hour12: false });
266
+ return [
267
+ // 目录
268
+ { path: path.join(d, "configs"), type: "dir" },
269
+ { path: path.join(d, "state", "agents", "main", "sessions"), type: "dir" },
270
+ { path: path.join(w, "prompts"), type: "dir" },
271
+ { path: path.join(w, "memory", "daily"), type: "dir" },
272
+ { path: path.join(w, "docs", "research"), type: "dir" },
273
+ { path: path.join(w, "docs", "todo"), type: "dir" },
274
+ { path: path.join(w, "docs", "decisions"), type: "dir" },
275
+ { path: path.join(w, "docs", "knowledge"), type: "dir" },
276
+ { path: path.join(w, "docs", "sop"), type: "dir" },
277
+ { path: path.join(d, "logs"), type: "dir" },
278
+ { path: path.join(d, "media", "inbound"), type: "dir" },
279
+ // workspace 文件
280
+ {
281
+ path: path.join(w, "SESSION-STATE.md"),
282
+ type: "file",
283
+ content: readTemplate("workspace/SESSION-STATE.md").replace("{{CURRENT_TIME}}", now)
284
+ },
285
+ { path: path.join(w, "HEARTBEAT.md"), type: "file", content: readTemplate("workspace/HEARTBEAT.md") },
286
+ {
287
+ path: path.join(w, "SOUL.md"),
288
+ type: "file",
289
+ content: readTemplate("workspace/SOUL.md").replace(/\{\{AGENT_NAME\}\}/g, v.agentName)
290
+ },
291
+ { path: path.join(w, "AGENTS.md"), type: "file", content: readTemplate("workspace/AGENTS.md") },
292
+ {
293
+ path: path.join(w, "prompts", "contacts.md"),
294
+ type: "file",
295
+ content: readTemplate("workspace/prompts/contacts.md").replace("{{DISCORD_USER_ID}}", v.discordUserId || "YOUR_DISCORD_ID").replace("{{FEISHU_OPEN_ID}}", v.feishuOpenId || "YOUR_FEISHU_OPEN_ID")
296
+ },
297
+ { path: path.join(w, "MEMORY.md"), type: "file", content: "# MEMORY.md \u2014 \u8BB0\u5FC6\u6587\u4EF6\u7D22\u5F15\n\n> \u6700\u540E\u66F4\u65B0\uFF1A\u521D\u59CB\u5316\n" },
298
+ { path: path.join(w, "USER.md"), type: "file", content: "# USER.md \u2014 \u7528\u6237\u4FE1\u606F\n\n\uFF08\u5728\u8FD9\u91CC\u8BB0\u5F55\u7528\u6237\u7684\u504F\u597D\u3001\u80CC\u666F\u7B49\uFF09\n" },
299
+ // package.json — 让 agent 目录成为独立 npm 项目根,防止 npm hoisting
300
+ {
301
+ path: path.join(d, "package.json"),
302
+ type: "file",
303
+ content: JSON.stringify({
304
+ name: v.agentName.toLowerCase().replace(/[^a-z0-9-]/g, "-") || "agent",
305
+ version: "1.0.0",
306
+ private: true,
307
+ description: `Engine 7 agent: ${v.agentName}`
308
+ }, null, 2) + "\n"
309
+ },
310
+ // 启动脚本
311
+ {
312
+ path: path.join(d, "start.cmd"),
313
+ type: "file",
314
+ content: `@echo off
315
+ rem Engine 7 startup script
316
+ rem Auto-generated by engine7 init
317
+
318
+ cd /d "%~dp0"
319
+
320
+ echo [start] Killing existing engine for main7.json...
321
+ for %%F in ("configs\\main7.json") do set CONFIG_NAME=%%~nxF
322
+ powershell -Command "Get-WmiObject Win32_Process | Where-Object { $_.Name -eq 'node.exe' -and $_.CommandLine -like '*%CONFIG_NAME%*' -and $_.CommandLine -like '*dist*' } | ForEach-Object { Write-Host '[start] Killing PID' $_.ProcessId; Stop-Process -Id $_.ProcessId -Force }"
323
+
324
+ timeout /t 2 /nobreak >nul 2>&1
325
+
326
+ echo [start] Starting Engine 7...
327
+ echo [start] Config: configs\\main7.json
328
+ echo.
329
+
330
+ node node_modules/engine7/dist/main.mjs --engine-config configs/main7.json
331
+
332
+ if %ERRORLEVEL% NEQ 0 (
333
+ echo.
334
+ echo [start] Engine failed with error code: %ERRORLEVEL%
335
+ pause
336
+ )
337
+ `
338
+ }
339
+ ];
340
+ }
341
+ function readTemplate(relativePath) {
342
+ const candidates = [
343
+ path.join(__dirname, "..", "templates", relativePath),
344
+ path.join(__dirname, "..", "..", "templates", relativePath),
345
+ path.join(process.cwd(), "templates", relativePath)
346
+ ];
347
+ for (const p of candidates) {
348
+ if (fs.existsSync(p)) {
349
+ return fs.readFileSync(p, "utf-8");
350
+ }
351
+ }
352
+ throw new Error(`\u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${relativePath}\uFF08\u627E\u4E86: ${candidates.join(", ")}\uFF09`);
353
+ }
354
+ function dryRun(entries) {
355
+ console.log("\n[dry-run] \u5C06\u8981\u521B\u5EFA\uFF1A\n");
356
+ for (const e of entries) {
357
+ const prefix = e.type === "dir" ? "\u{1F4C1}" : "\u{1F4C4}";
358
+ console.log(` ${prefix} ${e.path}`);
359
+ }
360
+ }
361
+ function execute(entries) {
362
+ for (const e of entries) {
363
+ if (e.type === "dir") {
364
+ fs.mkdirSync(e.path, { recursive: true });
365
+ console.log(` \u{1F4C1} ${e.path}`);
366
+ } else {
367
+ fs.mkdirSync(path.dirname(e.path), { recursive: true });
368
+ fs.writeFileSync(e.path, e.content || "", "utf-8");
369
+ console.log(` \u{1F4C4} ${e.path}`);
370
+ }
371
+ }
372
+ }
373
+ async function main() {
374
+ const opts = parseArgs();
375
+ console.log(`
376
+ \u{1F680} engine7 init`);
377
+ console.log(` state-dir: ${opts.stateDir}`);
378
+ if (fs.existsSync(opts.stateDir) && !opts.dryRun) {
379
+ const files = fs.readdirSync(opts.stateDir);
380
+ if (files.length > 0) {
381
+ console.error(`
382
+ \u274C \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A: ${opts.stateDir}`);
383
+ console.error(` \u8BF7\u9009\u62E9\u7A7A\u76EE\u5F55\u6216\u5148\u5220\u9664\u73B0\u6709\u5185\u5BB9`);
384
+ process.exit(1);
385
+ }
386
+ }
387
+ let values;
388
+ if (opts.quick) {
389
+ values = getDefaultValues(opts.stateDir);
390
+ console.log(" \u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E\uFF08--quick\uFF09");
391
+ } else {
392
+ const rl = createReadline();
393
+ values = await interactiveConfig(rl, getDefaultValues(opts.stateDir));
394
+ rl.close();
395
+ }
396
+ const config = generateConfig(values);
397
+ const configPath = path.join(opts.stateDir, "configs", "main7.json");
398
+ const tree = buildDirTree(values);
399
+ tree.push({
400
+ path: configPath,
401
+ type: "file",
402
+ content: JSON.stringify(config, null, 2)
403
+ });
404
+ if (opts.dryRun) {
405
+ dryRun(tree);
406
+ } else {
407
+ console.log("\n\u{1F4E6} \u6B63\u5728\u521B\u5EFA\u76EE\u5F55\u548C\u6587\u4EF6...\n");
408
+ execute(tree);
409
+ console.log(`
410
+ \u2705 \u521D\u59CB\u5316\u5B8C\u6210\uFF01
411
+ `);
412
+ console.log(`\u4E0B\u4E00\u6B65\uFF1A`);
413
+ console.log(` 1. \u68C0\u67E5\u5E76\u4FEE\u6539 ${opts.stateDir}/configs/main7.json`);
414
+ console.log(` 2. \u542F\u52A8 Engine: start.cmd ${configPath}`);
415
+ console.log(` 3. \u67E5\u770B workspace: ${path.join(opts.stateDir, "workspace")}`);
416
+ }
417
+ }
418
+ main().catch((err) => {
419
+ console.error(`
420
+ \u274C \u5931\u8D25: ${err.message}`);
421
+ console.error(err.stack);
422
+ process.exit(1);
423
+ });
424
+ //# sourceMappingURL=cli.mjs.map