engine7 7.1.7 → 7.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +655 -118
- package/dist/engine-startup.mjs +198 -108
- package/dist/main.mjs +198 -108
- package/package.json +10 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,10 +1,494 @@
|
|
|
1
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/cli-travel.ts
|
|
12
|
+
var cli_travel_exports = {};
|
|
13
|
+
__export(cli_travel_exports, {
|
|
14
|
+
doExport: () => doExport,
|
|
15
|
+
doImport: () => doImport,
|
|
16
|
+
loadTravelConfig: () => loadTravelConfig,
|
|
17
|
+
saveTravelConfig: () => saveTravelConfig
|
|
18
|
+
});
|
|
2
19
|
import * as path from "node:path";
|
|
3
20
|
import * as fs from "node:fs";
|
|
21
|
+
import * as os from "node:os";
|
|
22
|
+
import { execSync } from "node:child_process";
|
|
23
|
+
function loadTravelConfig() {
|
|
24
|
+
try {
|
|
25
|
+
if (!fs.existsSync(CONFIG_PATH)) return null;
|
|
26
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function saveTravelConfig(cfg) {
|
|
32
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2));
|
|
33
|
+
}
|
|
34
|
+
function sanitizeContent(content, dirs) {
|
|
35
|
+
let result = content;
|
|
36
|
+
for (const { placeholder, getOriginal } of PATH_PLACEHOLDERS) {
|
|
37
|
+
const original = getOriginal(dirs);
|
|
38
|
+
if (original) {
|
|
39
|
+
result = result.split(original).join(placeholder);
|
|
40
|
+
result = result.split(original.replace(/\//g, "\\")).join(placeholder);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function restoreContent(content, dirs) {
|
|
46
|
+
let result = content;
|
|
47
|
+
for (const { placeholder, getOriginal } of PATH_PLACEHOLDERS) {
|
|
48
|
+
const target = getOriginal(dirs);
|
|
49
|
+
if (target) {
|
|
50
|
+
result = result.split(placeholder).join(target);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
function isTextFile(filePath) {
|
|
56
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
57
|
+
return TEXT_EXTENSIONS.has(ext);
|
|
58
|
+
}
|
|
59
|
+
function shouldExclude(name) {
|
|
60
|
+
for (const pattern of WORKSPACE_EXCLUDE) {
|
|
61
|
+
if (pattern.startsWith("*")) {
|
|
62
|
+
if (name.includes(pattern.slice(1).replace("*", ""))) return true;
|
|
63
|
+
} else if (name === pattern) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
function collectFiles(rootDir, includeSet, optionalSet) {
|
|
70
|
+
const files = [];
|
|
71
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (shouldExclude(entry.name)) continue;
|
|
74
|
+
const fullPath = path.join(rootDir, entry.name);
|
|
75
|
+
const realPath = fs.realpathSync(fullPath);
|
|
76
|
+
if (realPath !== fullPath) {
|
|
77
|
+
try {
|
|
78
|
+
const lstat = fs.lstatSync(fullPath);
|
|
79
|
+
if (lstat.isSymbolicLink()) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const isInclude = includeSet.has(entry.name) || optionalSet.has(entry.name);
|
|
86
|
+
if (!isInclude) continue;
|
|
87
|
+
if (entry.isDirectory()) {
|
|
88
|
+
const subFiles = collectAllFiles(fullPath);
|
|
89
|
+
files.push(...subFiles);
|
|
90
|
+
} else {
|
|
91
|
+
files.push(fullPath);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return files;
|
|
95
|
+
}
|
|
96
|
+
function collectAllFiles(dir) {
|
|
97
|
+
const files = [];
|
|
98
|
+
try {
|
|
99
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
if (shouldExclude(entry.name)) continue;
|
|
102
|
+
const fullPath = path.join(dir, entry.name);
|
|
103
|
+
try {
|
|
104
|
+
if (fs.lstatSync(fullPath).isSymbolicLink()) continue;
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
if (entry.isDirectory()) {
|
|
108
|
+
files.push(...collectAllFiles(fullPath));
|
|
109
|
+
} else {
|
|
110
|
+
files.push(fullPath);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
return files;
|
|
116
|
+
}
|
|
117
|
+
function collectRecentSessions(agentsDir, days = 7) {
|
|
118
|
+
const files = [];
|
|
119
|
+
if (!fs.existsSync(agentsDir)) return files;
|
|
120
|
+
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
121
|
+
try {
|
|
122
|
+
const entries = fs.readdirSync(agentsDir, { withFileTypes: true });
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
const fullPath = path.join(agentsDir, entry.name);
|
|
125
|
+
if (entry.isDirectory()) {
|
|
126
|
+
const sub = collectAllFiles(fullPath);
|
|
127
|
+
for (const f of sub) {
|
|
128
|
+
if (!f.endsWith(".jsonl")) continue;
|
|
129
|
+
const stat = fs.statSync(f);
|
|
130
|
+
if (stat.mtimeMs > cutoff) files.push(f);
|
|
131
|
+
}
|
|
132
|
+
} else if (entry.name.endsWith(".jsonl")) {
|
|
133
|
+
const stat = fs.statSync(fullPath);
|
|
134
|
+
if (stat.mtimeMs > cutoff) files.push(fullPath);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
139
|
+
return files;
|
|
140
|
+
}
|
|
141
|
+
async function doExport(opts) {
|
|
142
|
+
const { agentName, stateDir, note, dryRun: dryRun2 } = opts;
|
|
143
|
+
const workspace = opts.workspace || path.join(stateDir, "workspace");
|
|
144
|
+
const engineHome = path.join(os.homedir(), ".openclaw");
|
|
145
|
+
console.log(`\u{1F4E6} engine7 export`);
|
|
146
|
+
console.log(` agent: ${agentName}`);
|
|
147
|
+
console.log(` state: ${stateDir}`);
|
|
148
|
+
console.log(` workspace: ${workspace}`);
|
|
149
|
+
if (!fs.existsSync(workspace)) {
|
|
150
|
+
console.error(`\u274C workspace \u4E0D\u5B58\u5728: ${workspace}`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
const dirs = { workspace, stateDir, engineHome };
|
|
154
|
+
const wsFiles = collectFiles(workspace, WORKSPACE_INCLUDE, WORKSPACE_OPTIONAL);
|
|
155
|
+
const sessionFiles = collectRecentSessions(path.join(stateDir, "agents"), 7);
|
|
156
|
+
console.log(` workspace \u6587\u4EF6: ${wsFiles.length}`);
|
|
157
|
+
console.log(` session jsonl: ${sessionFiles.length} (\u6700\u8FD17\u5929)`);
|
|
158
|
+
if (dryRun2) {
|
|
159
|
+
console.log(`
|
|
160
|
+
[DRY RUN] \u4F1A\u6253\u5305\u4EE5\u4E0B\u6587\u4EF6:`);
|
|
161
|
+
wsFiles.slice(0, 20).forEach((f) => console.log(` ${path.relative(workspace, f)}`));
|
|
162
|
+
if (wsFiles.length > 20) console.log(` ... \u8FD8\u6709 ${wsFiles.length - 20} \u4E2A\u6587\u4EF6`);
|
|
163
|
+
console.log(`
|
|
164
|
+
[DRY RUN] \u4E0D\u5B9E\u9645\u6253\u5305`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const now = /* @__PURE__ */ new Date();
|
|
168
|
+
const version = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
|
|
169
|
+
const tmpDir = path.join(os.tmpdir(), `engine7-export-${agentName}-${version}`);
|
|
170
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
171
|
+
const stagingDir = path.join(tmpDir, agentName);
|
|
172
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
173
|
+
let fileCount = 0;
|
|
174
|
+
let totalSize = 0;
|
|
175
|
+
for (const srcFile of wsFiles) {
|
|
176
|
+
const relPath = path.relative(workspace, srcFile);
|
|
177
|
+
const destFile = path.join(stagingDir, "workspace", relPath);
|
|
178
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
179
|
+
if (isTextFile(srcFile)) {
|
|
180
|
+
const content = fs.readFileSync(srcFile, "utf-8");
|
|
181
|
+
fs.writeFileSync(destFile, sanitizeContent(content, dirs));
|
|
182
|
+
} else {
|
|
183
|
+
fs.copyFileSync(srcFile, destFile);
|
|
184
|
+
}
|
|
185
|
+
fileCount++;
|
|
186
|
+
totalSize += fs.statSync(srcFile).size;
|
|
187
|
+
}
|
|
188
|
+
for (const srcFile of sessionFiles) {
|
|
189
|
+
const relPath = path.relative(stateDir, srcFile);
|
|
190
|
+
const destFile = path.join(stagingDir, relPath);
|
|
191
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
192
|
+
fs.copyFileSync(srcFile, destFile);
|
|
193
|
+
fileCount++;
|
|
194
|
+
totalSize += fs.statSync(srcFile).size;
|
|
195
|
+
}
|
|
196
|
+
const manifest = {
|
|
197
|
+
agentName,
|
|
198
|
+
version,
|
|
199
|
+
createdAt: now.toISOString(),
|
|
200
|
+
engineVersion: "7.x",
|
|
201
|
+
originalPaths: {
|
|
202
|
+
WORKSPACE: workspace,
|
|
203
|
+
STATE_DIR: stateDir,
|
|
204
|
+
ENGINE_HOME: engineHome
|
|
205
|
+
},
|
|
206
|
+
fileCount,
|
|
207
|
+
totalSize,
|
|
208
|
+
note
|
|
209
|
+
};
|
|
210
|
+
fs.writeFileSync(path.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
211
|
+
const tarFile = path.join(tmpDir, `${agentName}-${version}.tar.gz`);
|
|
212
|
+
console.log(`
|
|
213
|
+
\u{1F5DC}\uFE0F \u6253\u5305\u4E2D...`);
|
|
214
|
+
if (process.platform === "win32") {
|
|
215
|
+
const zipFile = tarFile.replace(".tar.gz", ".zip");
|
|
216
|
+
const stagingParent = path.dirname(stagingDir);
|
|
217
|
+
const childName = path.basename(stagingDir);
|
|
218
|
+
execSync(`powershell -Command "Compress-Archive -Path '${stagingParent}/${childName}/*' -DestinationPath '${zipFile}' -Force"`, { stdio: "pipe" });
|
|
219
|
+
fs.renameSync(zipFile, tarFile);
|
|
220
|
+
} else {
|
|
221
|
+
execSync(`tar -czf "${tarFile}" -C "${tmpDir}" ${agentName}`, { stdio: "pipe" });
|
|
222
|
+
}
|
|
223
|
+
const tarSize = fs.statSync(tarFile).size;
|
|
224
|
+
console.log(`\u2705 \u6253\u5305\u5B8C\u6210: ${tarFile} (${(tarSize / 1024 / 1024).toFixed(1)} MB, ${fileCount} \u6587\u4EF6)`);
|
|
225
|
+
const cfg = loadTravelConfig();
|
|
226
|
+
if (!cfg || !cfg.githubToken) {
|
|
227
|
+
console.log(`
|
|
228
|
+
\u26A0\uFE0F \u672A\u914D\u7F6E GitHub\uFF0Ctar.gz \u5DF2\u751F\u6210: ${tarFile}`);
|
|
229
|
+
console.log(` \u914D\u7F6E GitHub: \u7F16\u8F91 ${CONFIG_PATH}`);
|
|
230
|
+
console.log(` \u6216\u624B\u52A8\u4E0A\u4F20\u5230 GitHub private repo release`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
await uploadToGitHub(cfg, tarFile, agentName, version, manifest);
|
|
234
|
+
}
|
|
235
|
+
async function doImport(opts) {
|
|
236
|
+
const { agentName, stateDir, version, dryRun: dryRun2 } = opts;
|
|
237
|
+
console.log(`\u{1F4E5} engine7 import`);
|
|
238
|
+
console.log(` agent: ${agentName}`);
|
|
239
|
+
console.log(` target: ${stateDir}`);
|
|
240
|
+
const cfg = loadTravelConfig();
|
|
241
|
+
if (!cfg || !cfg.githubToken) {
|
|
242
|
+
console.error(`\u274C \u672A\u914D\u7F6E GitHub\uFF0C\u65E0\u6CD5\u4E0B\u8F7D`);
|
|
243
|
+
console.error(` \u914D\u7F6E: \u7F16\u8F91 ${CONFIG_PATH}`);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
const tmpDir = path.join(os.tmpdir(), `engine7-import-${agentName}-${Date.now()}`);
|
|
247
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
248
|
+
const tarFile = await downloadFromGitHub(cfg, agentName, version, tmpDir);
|
|
249
|
+
console.log(`\u2705 \u4E0B\u8F7D\u5B8C\u6210: ${tarFile}`);
|
|
250
|
+
if (dryRun2) {
|
|
251
|
+
console.log(`
|
|
252
|
+
[DRY RUN] \u53EA\u4E0B\u8F7D\u4E0D\u6062\u590D`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
console.log(`\u{1F4C2} \u89E3\u5305\u4E2D...`);
|
|
256
|
+
if (process.platform === "win32") {
|
|
257
|
+
execSync(`powershell -Command "Expand-Archive -Path '${tarFile}' -DestinationPath '${tmpDir}' -Force"`, { stdio: "pipe" });
|
|
258
|
+
} else {
|
|
259
|
+
execSync(`tar -xzf "${tarFile}" -C "${tmpDir}"`, { stdio: "pipe" });
|
|
260
|
+
}
|
|
261
|
+
const stagingDir = path.join(tmpDir, agentName);
|
|
262
|
+
const manifestPath = path.join(stagingDir, "manifest.json");
|
|
263
|
+
if (!fs.existsSync(manifestPath)) {
|
|
264
|
+
console.error(`\u274C manifest.json \u4E0D\u5B58\u5728\uFF0C\u6587\u4EF6\u53EF\u80FD\u635F\u574F`);
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
268
|
+
const workspace = path.join(stateDir, "workspace");
|
|
269
|
+
const engineHome = path.join(os.homedir(), ".openclaw");
|
|
270
|
+
const dirs = { workspace, stateDir, engineHome };
|
|
271
|
+
console.log(` \u7248\u672C: ${manifest.version}`);
|
|
272
|
+
console.log(` \u521B\u5EFA: ${manifest.createdAt}`);
|
|
273
|
+
console.log(` \u6587\u4EF6\u6570: ${manifest.fileCount}`);
|
|
274
|
+
const wsStaging = path.join(stagingDir, "workspace");
|
|
275
|
+
let restoredCount = 0;
|
|
276
|
+
if (fs.existsSync(wsStaging)) {
|
|
277
|
+
const allFiles = collectAllFiles(wsStaging);
|
|
278
|
+
for (const srcFile of allFiles) {
|
|
279
|
+
const relPath = path.relative(wsStaging, srcFile);
|
|
280
|
+
const destFile = path.join(workspace, relPath);
|
|
281
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
282
|
+
if (isTextFile(srcFile)) {
|
|
283
|
+
const content = fs.readFileSync(srcFile, "utf-8");
|
|
284
|
+
fs.writeFileSync(destFile, restoreContent(content, dirs));
|
|
285
|
+
} else {
|
|
286
|
+
fs.copyFileSync(srcFile, destFile);
|
|
287
|
+
}
|
|
288
|
+
restoredCount++;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const agentsStaging = path.join(stagingDir, "agents");
|
|
292
|
+
if (fs.existsSync(agentsStaging)) {
|
|
293
|
+
const sessionFiles = collectAllFiles(agentsStaging);
|
|
294
|
+
for (const srcFile of sessionFiles) {
|
|
295
|
+
const relPath = path.relative(stagingDir, srcFile);
|
|
296
|
+
const destFile = path.join(stateDir, relPath);
|
|
297
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
298
|
+
fs.copyFileSync(srcFile, destFile);
|
|
299
|
+
restoredCount++;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
console.log(`\u2705 \u6062\u590D\u5B8C\u6210: ${restoredCount} \u6587\u4EF6 \u2192 ${stateDir}`);
|
|
303
|
+
console.log(`
|
|
304
|
+
\u{1F4A1} \u4E0B\u4E00\u6B65: engine7 start --config <path>`);
|
|
305
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
306
|
+
}
|
|
307
|
+
async function uploadToGitHub(cfg, tarFile, agentName, version, manifest) {
|
|
308
|
+
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
309
|
+
const tag = `${agentName}-${version}`;
|
|
310
|
+
console.log(`
|
|
311
|
+
\u{1F4E4} \u4E0A\u4F20\u5230 GitHub: ${repo} release ${tag}`);
|
|
312
|
+
try {
|
|
313
|
+
const releaseBody = Object.entries(manifest).map(([k, v]) => `- **${k}**: ${typeof v === "object" ? JSON.stringify(v) : v}`).join("\n");
|
|
314
|
+
const createRes = await fetch(`https://api.github.com/repos/${repo}/releases`, {
|
|
315
|
+
method: "POST",
|
|
316
|
+
headers: {
|
|
317
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
318
|
+
"Accept": "application/vnd.github+json",
|
|
319
|
+
"Content-Type": "application/json"
|
|
320
|
+
},
|
|
321
|
+
body: JSON.stringify({
|
|
322
|
+
tag_name: tag,
|
|
323
|
+
name: `${agentName} ${version}`,
|
|
324
|
+
body: releaseBody,
|
|
325
|
+
prerelease: false,
|
|
326
|
+
make_latest: "true"
|
|
327
|
+
})
|
|
328
|
+
});
|
|
329
|
+
if (!createRes.ok) {
|
|
330
|
+
const err = await createRes.text();
|
|
331
|
+
throw new Error(`\u521B\u5EFA release \u5931\u8D25: ${createRes.status} ${err}`);
|
|
332
|
+
}
|
|
333
|
+
const release = await createRes.json();
|
|
334
|
+
const uploadUrl = release.upload_url.replace("{?name,label}", "");
|
|
335
|
+
const fileBuffer = fs.readFileSync(tarFile);
|
|
336
|
+
const fileName = path.basename(tarFile);
|
|
337
|
+
const uploadRes = await fetch(`${uploadUrl}?name=${fileName}`, {
|
|
338
|
+
method: "POST",
|
|
339
|
+
headers: {
|
|
340
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
341
|
+
"Accept": "application/vnd.github+json",
|
|
342
|
+
"Content-Type": "application/gzip",
|
|
343
|
+
"Content-Length": String(fileBuffer.length)
|
|
344
|
+
},
|
|
345
|
+
body: fileBuffer
|
|
346
|
+
});
|
|
347
|
+
if (!uploadRes.ok) {
|
|
348
|
+
const err = await uploadRes.text();
|
|
349
|
+
throw new Error(`\u4E0A\u4F20 asset \u5931\u8D25: ${uploadRes.status} ${err}`);
|
|
350
|
+
}
|
|
351
|
+
console.log(`\u2705 \u4E0A\u4F20\u6210\u529F!`);
|
|
352
|
+
console.log(` release: ${release.html_url}`);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.error(`\u274C GitHub \u4E0A\u4F20\u5931\u8D25: ${e.message}`);
|
|
355
|
+
console.error(` tar.gz \u4ECD\u5728: ${tarFile}`);
|
|
356
|
+
throw e;
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
fs.rmSync(path.dirname(tarFile), { recursive: true, force: true });
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async function downloadFromGitHub(cfg, agentName, version, destDir) {
|
|
364
|
+
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
365
|
+
const tag = version || await getLatestReleaseTag(cfg, agentName);
|
|
366
|
+
console.log(`\u2B07\uFE0F \u4E0B\u8F7D: ${repo} release ${tag}`);
|
|
367
|
+
const res = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${tag}`, {
|
|
368
|
+
headers: {
|
|
369
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
370
|
+
"Accept": "application/vnd.github+json"
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
if (!res.ok) {
|
|
374
|
+
throw new Error(`\u83B7\u53D6 release \u5931\u8D25: ${res.status}`);
|
|
375
|
+
}
|
|
376
|
+
const release = await res.json();
|
|
377
|
+
const asset = release.assets?.find((a) => a.name.endsWith(".tar.gz"));
|
|
378
|
+
if (!asset) {
|
|
379
|
+
throw new Error(`release ${tag} \u6CA1\u6709 tar.gz asset`);
|
|
380
|
+
}
|
|
381
|
+
const downloadRes = await fetch(asset.url, {
|
|
382
|
+
headers: {
|
|
383
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
384
|
+
"Accept": "application/octet-stream"
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
if (!downloadRes.ok) {
|
|
388
|
+
throw new Error(`\u4E0B\u8F7D asset \u5931\u8D25: ${downloadRes.status}`);
|
|
389
|
+
}
|
|
390
|
+
const buffer = Buffer.from(await downloadRes.arrayBuffer());
|
|
391
|
+
const tarFile = path.join(destDir, asset.name);
|
|
392
|
+
fs.writeFileSync(tarFile, buffer);
|
|
393
|
+
return tarFile;
|
|
394
|
+
}
|
|
395
|
+
async function getLatestReleaseTag(cfg, agentName) {
|
|
396
|
+
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
397
|
+
const res = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, {
|
|
398
|
+
headers: {
|
|
399
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
400
|
+
"Accept": "application/vnd.github+json"
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
if (!res.ok) {
|
|
404
|
+
throw new Error(`\u83B7\u53D6 release \u5217\u8868\u5931\u8D25: ${res.status}`);
|
|
405
|
+
}
|
|
406
|
+
const releases = await res.json();
|
|
407
|
+
const match = releases.find((r) => r.tag_name?.startsWith(`${agentName}-`));
|
|
408
|
+
if (!match) {
|
|
409
|
+
throw new Error(`\u6CA1\u6709\u627E\u5230 ${agentName} \u7684 release`);
|
|
410
|
+
}
|
|
411
|
+
return match.tag_name;
|
|
412
|
+
}
|
|
413
|
+
var CONFIG_PATH, PATH_PLACEHOLDERS, WORKSPACE_INCLUDE, WORKSPACE_OPTIONAL, WORKSPACE_EXCLUDE, TEXT_EXTENSIONS;
|
|
414
|
+
var init_cli_travel = __esm({
|
|
415
|
+
"src/cli-travel.ts"() {
|
|
416
|
+
"use strict";
|
|
417
|
+
CONFIG_PATH = path.join(os.homedir(), ".engine7-travel.json");
|
|
418
|
+
PATH_PLACEHOLDERS = [
|
|
419
|
+
{ placeholder: "{{WORKSPACE}}", getOriginal: (d) => d.workspace },
|
|
420
|
+
{ placeholder: "{{ENGINE_HOME}}", getOriginal: (d) => d.engineHome },
|
|
421
|
+
{ placeholder: "{{STATE_DIR}}", getOriginal: (d) => d.stateDir }
|
|
422
|
+
];
|
|
423
|
+
WORKSPACE_INCLUDE = /* @__PURE__ */ new Set([
|
|
424
|
+
// 核心文件
|
|
425
|
+
"AGENTS.md",
|
|
426
|
+
"SOUL.md",
|
|
427
|
+
"MEMORY.md",
|
|
428
|
+
"USER.md",
|
|
429
|
+
"HEARTBEAT.md",
|
|
430
|
+
"INDEX.md",
|
|
431
|
+
"SESSION-STATE.md",
|
|
432
|
+
// 核心目录
|
|
433
|
+
"prompts",
|
|
434
|
+
"topics",
|
|
435
|
+
"memory",
|
|
436
|
+
"inner-voice",
|
|
437
|
+
"docs",
|
|
438
|
+
"skills",
|
|
439
|
+
"voice-chat",
|
|
440
|
+
"scripts",
|
|
441
|
+
"selfie",
|
|
442
|
+
"moodboard",
|
|
443
|
+
// 状态文件
|
|
444
|
+
"calendar.db",
|
|
445
|
+
"nudge-state.json"
|
|
446
|
+
]);
|
|
447
|
+
WORKSPACE_OPTIONAL = /* @__PURE__ */ new Set([
|
|
448
|
+
"images",
|
|
449
|
+
"tools"
|
|
450
|
+
]);
|
|
451
|
+
WORKSPACE_EXCLUDE = /* @__PURE__ */ new Set([
|
|
452
|
+
"livestream",
|
|
453
|
+
"content-library",
|
|
454
|
+
"tmp",
|
|
455
|
+
".git",
|
|
456
|
+
"node_modules",
|
|
457
|
+
"memory_runs",
|
|
458
|
+
"workspace",
|
|
459
|
+
"prompt-archive",
|
|
460
|
+
"aim-archive",
|
|
461
|
+
"test-agent",
|
|
462
|
+
"nul",
|
|
463
|
+
"*.bak*",
|
|
464
|
+
"*.bak-*"
|
|
465
|
+
]);
|
|
466
|
+
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
467
|
+
".md",
|
|
468
|
+
".json",
|
|
469
|
+
".txt",
|
|
470
|
+
".js",
|
|
471
|
+
".ts",
|
|
472
|
+
".mjs",
|
|
473
|
+
".py",
|
|
474
|
+
".yaml",
|
|
475
|
+
".yml",
|
|
476
|
+
".cmd",
|
|
477
|
+
".bat",
|
|
478
|
+
".sh",
|
|
479
|
+
".csv",
|
|
480
|
+
".html"
|
|
481
|
+
]);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// src/cli-init.ts
|
|
486
|
+
import * as path2 from "node:path";
|
|
487
|
+
import * as fs2 from "node:fs";
|
|
4
488
|
import * as readline from "node:readline";
|
|
5
489
|
import { fileURLToPath } from "node:url";
|
|
6
490
|
var __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
var __dirname =
|
|
491
|
+
var __dirname = path2.dirname(__filename);
|
|
8
492
|
var SCHEMA_VERSION = 1;
|
|
9
493
|
function parseArgs() {
|
|
10
494
|
const args = process.argv.slice(2);
|
|
@@ -26,10 +510,10 @@ function parseArgs() {
|
|
|
26
510
|
}
|
|
27
511
|
}
|
|
28
512
|
if (!stateDir) {
|
|
29
|
-
stateDir =
|
|
513
|
+
stateDir = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
30
514
|
console.log(` \u4F7F\u7528\u9ED8\u8BA4\u76EE\u5F55: ${stateDir}`);
|
|
31
515
|
}
|
|
32
|
-
stateDir =
|
|
516
|
+
stateDir = path2.resolve(stateDir);
|
|
33
517
|
return { stateDir, quick, dryRun: dryRun2 };
|
|
34
518
|
}
|
|
35
519
|
function printHelp() {
|
|
@@ -41,6 +525,8 @@ Engine 7 \u2014 Self-hosted AI agent engine
|
|
|
41
525
|
engine7 start [--config <path>] \u542F\u52A8 Engine
|
|
42
526
|
engine7 restart [--config <path>] \u91CD\u542F Engine\uFF08\u6740\u65E7\u8FDB\u7A0B+\u542F\u52A8\uFF09
|
|
43
527
|
engine7 service install|uninstall|status \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
528
|
+
engine7 export [--state-dir <path>] \u6253\u5305 agent workspace \u2192 GitHub release
|
|
529
|
+
engine7 import --state-dir <path> \u4ECE GitHub release \u6062\u590D agent
|
|
44
530
|
|
|
45
531
|
init \u9009\u9879:
|
|
46
532
|
--state-dir <path> agent \u6839\u76EE\u5F55\uFF08\u5FC5\u586B\uFF0C\u652F\u6301\u76F8\u5BF9\u8DEF\u5F84\uFF09
|
|
@@ -50,11 +536,25 @@ init \u9009\u9879:
|
|
|
50
536
|
start \u9009\u9879:
|
|
51
537
|
--config <path> config \u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4: configs/main7.json\uFF09
|
|
52
538
|
|
|
539
|
+
export \u9009\u9879:
|
|
540
|
+
--state-dir <path> agent \u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4: \u5F53\u524D\u76EE\u5F55\u4E0A\u7EA7\uFF09
|
|
541
|
+
--agent <name> agent \u540D\u79F0\uFF08\u9ED8\u8BA4: state-dir basename\uFF09
|
|
542
|
+
--note <text> \u7248\u672C\u5907\u6CE8
|
|
543
|
+
--dry-run \u53EA\u5217\u51FA\u4F1A\u6253\u5305\u4EC0\u4E48\uFF0C\u4E0D\u5B9E\u9645\u4E0A\u4F20
|
|
544
|
+
|
|
545
|
+
import \u9009\u9879:
|
|
546
|
+
--state-dir <path> \u76EE\u6807 agent \u6839\u76EE\u5F55\uFF08\u5FC5\u586B\uFF09
|
|
547
|
+
--agent <name> agent \u540D\u79F0
|
|
548
|
+
--version <tag> \u6307\u5B9A\u7248\u672C\uFF08\u9ED8\u8BA4: \u6700\u65B0\uFF09
|
|
549
|
+
--dry-run \u53EA\u4E0B\u8F7D\u4E0D\u6062\u590D
|
|
550
|
+
|
|
53
551
|
\u793A\u4F8B:
|
|
54
552
|
engine7 init --state-dir D:/my-agent
|
|
55
553
|
engine7 init --state-dir ./my-agent --quick
|
|
56
554
|
engine7 start
|
|
57
555
|
engine7 start --config configs/xiaowen.json
|
|
556
|
+
engine7 export --state-dir D:/xiaoke --agent xiaoke --note "travel test"
|
|
557
|
+
engine7 import --state-dir D:/xiaoke --agent xiaoke
|
|
58
558
|
`);
|
|
59
559
|
}
|
|
60
560
|
function createReadline() {
|
|
@@ -166,7 +666,7 @@ async function interactiveConfig(rl, defaults) {
|
|
|
166
666
|
return v;
|
|
167
667
|
}
|
|
168
668
|
function generateConfig(v) {
|
|
169
|
-
const workspace =
|
|
669
|
+
const workspace = path2.join(v.stateDir, "workspace").replace(/\\/g, "/");
|
|
170
670
|
const config = {
|
|
171
671
|
schemaVersion: SCHEMA_VERSION,
|
|
172
672
|
stateDir: v.stateDir.replace(/\\/g, "/"),
|
|
@@ -292,45 +792,45 @@ function generateConfig(v) {
|
|
|
292
792
|
}
|
|
293
793
|
function buildDirTree(v) {
|
|
294
794
|
const d = v.stateDir;
|
|
295
|
-
const w =
|
|
795
|
+
const w = path2.join(d, "workspace");
|
|
296
796
|
const now = (/* @__PURE__ */ new Date()).toLocaleString("sv-SE", { timeZone: "Asia/Shanghai", hour12: false });
|
|
297
797
|
return [
|
|
298
798
|
// 目录
|
|
299
|
-
{ path:
|
|
300
|
-
{ path:
|
|
301
|
-
{ path:
|
|
302
|
-
{ path:
|
|
303
|
-
{ path:
|
|
304
|
-
{ path:
|
|
305
|
-
{ path:
|
|
306
|
-
{ path:
|
|
307
|
-
{ path:
|
|
308
|
-
{ path:
|
|
309
|
-
{ path:
|
|
799
|
+
{ path: path2.join(d, "configs"), type: "dir" },
|
|
800
|
+
{ path: path2.join(d, "state", "agents", "main", "sessions"), type: "dir" },
|
|
801
|
+
{ path: path2.join(w, "prompts"), type: "dir" },
|
|
802
|
+
{ path: path2.join(w, "memory", "daily"), type: "dir" },
|
|
803
|
+
{ path: path2.join(w, "docs", "research"), type: "dir" },
|
|
804
|
+
{ path: path2.join(w, "docs", "todo"), type: "dir" },
|
|
805
|
+
{ path: path2.join(w, "docs", "decisions"), type: "dir" },
|
|
806
|
+
{ path: path2.join(w, "docs", "knowledge"), type: "dir" },
|
|
807
|
+
{ path: path2.join(w, "docs", "sop"), type: "dir" },
|
|
808
|
+
{ path: path2.join(d, "logs"), type: "dir" },
|
|
809
|
+
{ path: path2.join(d, "media", "inbound"), type: "dir" },
|
|
310
810
|
// workspace 文件
|
|
311
811
|
{
|
|
312
|
-
path:
|
|
812
|
+
path: path2.join(w, "SESSION-STATE.md"),
|
|
313
813
|
type: "file",
|
|
314
814
|
content: readTemplate("workspace/SESSION-STATE.md").replace("{{CURRENT_TIME}}", now)
|
|
315
815
|
},
|
|
316
|
-
{ path:
|
|
816
|
+
{ path: path2.join(w, "HEARTBEAT.md"), type: "file", content: readTemplate("workspace/HEARTBEAT.md") },
|
|
317
817
|
{
|
|
318
|
-
path:
|
|
818
|
+
path: path2.join(w, "SOUL.md"),
|
|
319
819
|
type: "file",
|
|
320
820
|
content: readTemplate("workspace/SOUL.md").replace(/\{\{AGENT_NAME\}\}/g, v.agentName)
|
|
321
821
|
},
|
|
322
|
-
{ path:
|
|
822
|
+
{ path: path2.join(w, "AGENTS.md"), type: "file", content: readTemplate("workspace/AGENTS.md") },
|
|
323
823
|
{
|
|
324
|
-
path:
|
|
824
|
+
path: path2.join(w, "prompts", "contacts.md"),
|
|
325
825
|
type: "file",
|
|
326
826
|
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")
|
|
327
827
|
},
|
|
328
|
-
{ path:
|
|
329
|
-
{ path:
|
|
330
|
-
{ path:
|
|
828
|
+
{ path: path2.join(w, "prompts", "auto-memory-instructions.md"), type: "file", content: readTemplate("workspace/prompts/auto-memory-instructions.md") },
|
|
829
|
+
{ path: path2.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" },
|
|
830
|
+
{ path: path2.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" },
|
|
331
831
|
// package.json — 让 agent 目录成为独立 npm 项目根,防止 npm hoisting
|
|
332
832
|
{
|
|
333
|
-
path:
|
|
833
|
+
path: path2.join(d, "package.json"),
|
|
334
834
|
type: "file",
|
|
335
835
|
content: JSON.stringify({
|
|
336
836
|
name: v.agentName.toLowerCase().replace(/[^a-z0-9-]/g, "-") || "agent",
|
|
@@ -341,7 +841,7 @@ function buildDirTree(v) {
|
|
|
341
841
|
},
|
|
342
842
|
// 启动脚本(根据 OS 生成)
|
|
343
843
|
...process.platform === "win32" ? [{
|
|
344
|
-
path:
|
|
844
|
+
path: path2.join(d, "start.cmd"),
|
|
345
845
|
type: "file",
|
|
346
846
|
content: `@echo off
|
|
347
847
|
rem Engine 7 startup script
|
|
@@ -373,7 +873,7 @@ if %ERRORLEVEL% NEQ 0 (
|
|
|
373
873
|
)
|
|
374
874
|
`
|
|
375
875
|
}] : [{
|
|
376
|
-
path:
|
|
876
|
+
path: path2.join(d, "start.sh"),
|
|
377
877
|
type: "file",
|
|
378
878
|
content: `#!/bin/bash
|
|
379
879
|
# Engine 7 startup script
|
|
@@ -406,13 +906,13 @@ node "$ENGINE7_BIN" --engine-config configs/main7.json
|
|
|
406
906
|
}
|
|
407
907
|
function readTemplate(relativePath) {
|
|
408
908
|
const candidates = [
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
909
|
+
path2.join(__dirname, "..", "templates", relativePath),
|
|
910
|
+
path2.join(__dirname, "..", "..", "templates", relativePath),
|
|
911
|
+
path2.join(process.cwd(), "templates", relativePath)
|
|
412
912
|
];
|
|
413
913
|
for (const p of candidates) {
|
|
414
|
-
if (
|
|
415
|
-
return
|
|
914
|
+
if (fs2.existsSync(p)) {
|
|
915
|
+
return fs2.readFileSync(p, "utf-8");
|
|
416
916
|
}
|
|
417
917
|
}
|
|
418
918
|
throw new Error(`\u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${relativePath}\uFF08\u627E\u4E86: ${candidates.join(", ")}\uFF09`);
|
|
@@ -427,11 +927,11 @@ function dryRun(entries) {
|
|
|
427
927
|
function execute(entries) {
|
|
428
928
|
for (const e of entries) {
|
|
429
929
|
if (e.type === "dir") {
|
|
430
|
-
|
|
930
|
+
fs2.mkdirSync(e.path, { recursive: true });
|
|
431
931
|
console.log(` \u{1F4C1} ${e.path}`);
|
|
432
932
|
} else {
|
|
433
|
-
|
|
434
|
-
|
|
933
|
+
fs2.mkdirSync(path2.dirname(e.path), { recursive: true });
|
|
934
|
+
fs2.writeFileSync(e.path, e.content || "", "utf-8");
|
|
435
935
|
console.log(` \u{1F4C4} ${e.path}`);
|
|
436
936
|
}
|
|
437
937
|
}
|
|
@@ -447,6 +947,43 @@ async function main() {
|
|
|
447
947
|
printHelp();
|
|
448
948
|
process.exit(0);
|
|
449
949
|
}
|
|
950
|
+
if (subcommand === "export") {
|
|
951
|
+
const { doExport: doExport2 } = await Promise.resolve().then(() => (init_cli_travel(), cli_travel_exports));
|
|
952
|
+
let exportStateDir = "";
|
|
953
|
+
let agentName = "";
|
|
954
|
+
let note = "";
|
|
955
|
+
let dryRun2 = false;
|
|
956
|
+
for (let i = 1; i < args.length; i++) {
|
|
957
|
+
if (args[i] === "--state-dir" && args[i + 1]) exportStateDir = args[++i];
|
|
958
|
+
else if (args[i] === "--agent" && args[i + 1]) agentName = args[++i];
|
|
959
|
+
else if (args[i] === "--note" && args[i + 1]) note = args[++i];
|
|
960
|
+
else if (args[i] === "--dry-run") dryRun2 = true;
|
|
961
|
+
}
|
|
962
|
+
if (!exportStateDir) exportStateDir = path2.resolve(process.cwd());
|
|
963
|
+
if (!agentName) agentName = path2.basename(exportStateDir);
|
|
964
|
+
await doExport2({ agentName, stateDir: exportStateDir, note, dryRun: dryRun2 });
|
|
965
|
+
process.exit(0);
|
|
966
|
+
}
|
|
967
|
+
if (subcommand === "import") {
|
|
968
|
+
const { doImport: doImport2 } = await Promise.resolve().then(() => (init_cli_travel(), cli_travel_exports));
|
|
969
|
+
let importStateDir = "";
|
|
970
|
+
let agentName = "";
|
|
971
|
+
let version = "";
|
|
972
|
+
let dryRun2 = false;
|
|
973
|
+
for (let i = 1; i < args.length; i++) {
|
|
974
|
+
if (args[i] === "--state-dir" && args[i + 1]) importStateDir = args[++i];
|
|
975
|
+
else if (args[i] === "--agent" && args[i + 1]) agentName = args[++i];
|
|
976
|
+
else if (args[i] === "--version" && args[i + 1]) version = args[++i];
|
|
977
|
+
else if (args[i] === "--dry-run") dryRun2 = true;
|
|
978
|
+
}
|
|
979
|
+
if (!importStateDir) {
|
|
980
|
+
console.error("\u274C import \u9700\u8981 --state-dir <path>");
|
|
981
|
+
process.exit(1);
|
|
982
|
+
}
|
|
983
|
+
if (!agentName) agentName = path2.basename(importStateDir);
|
|
984
|
+
await doImport2({ agentName, stateDir: importStateDir, version, dryRun: dryRun2 });
|
|
985
|
+
process.exit(0);
|
|
986
|
+
}
|
|
450
987
|
if (subcommand === "start" || subcommand === "restart") {
|
|
451
988
|
const label = subcommand === "restart" ? "\u{1F504} engine7 restart" : "\u{1F680} engine7 start";
|
|
452
989
|
let configPath2 = "";
|
|
@@ -468,18 +1005,18 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
468
1005
|
}
|
|
469
1006
|
}
|
|
470
1007
|
if (!configPath2) {
|
|
471
|
-
const defaultCfg =
|
|
472
|
-
const homeCfg =
|
|
473
|
-
if (
|
|
1008
|
+
const defaultCfg = path2.join("configs", "main7.json");
|
|
1009
|
+
const homeCfg = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7", "configs", "main7.json");
|
|
1010
|
+
if (fs2.existsSync(defaultCfg)) {
|
|
474
1011
|
configPath2 = defaultCfg;
|
|
475
|
-
} else if (
|
|
1012
|
+
} else if (fs2.existsSync(homeCfg)) {
|
|
476
1013
|
configPath2 = homeCfg;
|
|
477
1014
|
console.log(` \u4F7F\u7528\u9ED8\u8BA4 config: ${homeCfg}`);
|
|
478
1015
|
} else {
|
|
479
1016
|
const ptr = readHomePointer();
|
|
480
1017
|
if (ptr?.stateDir) {
|
|
481
|
-
const ptrCfg =
|
|
482
|
-
if (
|
|
1018
|
+
const ptrCfg = path2.join(ptr.stateDir, "configs", "main7.json");
|
|
1019
|
+
if (fs2.existsSync(ptrCfg)) {
|
|
483
1020
|
configPath2 = ptrCfg;
|
|
484
1021
|
console.log(` \u4F7F\u7528 config: ${ptrCfg}`);
|
|
485
1022
|
}
|
|
@@ -492,21 +1029,21 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
492
1029
|
}
|
|
493
1030
|
}
|
|
494
1031
|
}
|
|
495
|
-
const { execSync, spawn } = await import("node:child_process");
|
|
1032
|
+
const { execSync: execSync2, spawn } = await import("node:child_process");
|
|
496
1033
|
let enginePath;
|
|
497
|
-
const localPath =
|
|
498
|
-
if (
|
|
1034
|
+
const localPath = path2.join("node_modules", "engine7", "dist", "main.mjs");
|
|
1035
|
+
if (fs2.existsSync(localPath)) {
|
|
499
1036
|
enginePath = localPath;
|
|
500
1037
|
} else {
|
|
501
|
-
const cliPath =
|
|
502
|
-
const distDir =
|
|
503
|
-
const candidate =
|
|
504
|
-
if (
|
|
1038
|
+
const cliPath = path2.resolve(process.argv[1]);
|
|
1039
|
+
const distDir = path2.dirname(cliPath);
|
|
1040
|
+
const candidate = path2.join(distDir, "main.mjs");
|
|
1041
|
+
if (fs2.existsSync(candidate)) {
|
|
505
1042
|
enginePath = candidate;
|
|
506
1043
|
} else {
|
|
507
1044
|
try {
|
|
508
|
-
const globalRoot =
|
|
509
|
-
enginePath =
|
|
1045
|
+
const globalRoot = execSync2("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
1046
|
+
enginePath = path2.join(globalRoot, "engine7", "dist", "main.mjs");
|
|
510
1047
|
} catch {
|
|
511
1048
|
console.error("\u274C \u627E\u4E0D\u5230 engine7 main.mjs");
|
|
512
1049
|
process.exit(1);
|
|
@@ -516,17 +1053,17 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
516
1053
|
console.log(label);
|
|
517
1054
|
console.log(` config: ${configPath2}`);
|
|
518
1055
|
console.log(` engine: ${enginePath}`);
|
|
519
|
-
const configName =
|
|
1056
|
+
const configName = path2.basename(configPath2);
|
|
520
1057
|
const myPid = process.pid;
|
|
521
1058
|
try {
|
|
522
1059
|
if (process.platform === "win32") {
|
|
523
|
-
|
|
1060
|
+
execSync2(`powershell -Command "Get-WmiObject Win32_Process | Where-Object { $_.Name -eq 'node.exe' -and $_.CommandLine -like '*${configName}*' -and $_.CommandLine -like '*dist*' -and $_.ProcessId -ne ${myPid} } | ForEach-Object { Write-Host '[start] Killing PID' $_.ProcessId; Stop-Process -Id $_.ProcessId -Force }"`, { stdio: "inherit" });
|
|
524
1061
|
} else {
|
|
525
|
-
const pids =
|
|
1062
|
+
const pids = execSync2(`pgrep -f "dist/main.mjs.*${configName}" 2>/dev/null || true`, { encoding: "utf-8" }).trim();
|
|
526
1063
|
const otherPids = pids.split("\n").filter((p) => p && p.trim() !== String(myPid)).join("\n").trim();
|
|
527
1064
|
if (otherPids) {
|
|
528
1065
|
console.log(`[start] Killing existing engine (PID: ${otherPids})...`);
|
|
529
|
-
|
|
1066
|
+
execSync2(`echo "${otherPids}" | xargs kill -9 2>/dev/null || true`);
|
|
530
1067
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
531
1068
|
}
|
|
532
1069
|
}
|
|
@@ -564,18 +1101,18 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
564
1101
|
else if (args[i] === "--cwd" && args[i + 1]) cwd = args[++i];
|
|
565
1102
|
}
|
|
566
1103
|
if (!configPath2) {
|
|
567
|
-
const defaultCfg =
|
|
568
|
-
const homeCfg =
|
|
569
|
-
if (
|
|
1104
|
+
const defaultCfg = path2.join(cwd, "configs", "main7.json");
|
|
1105
|
+
const homeCfg = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7", "configs", "main7.json");
|
|
1106
|
+
if (fs2.existsSync(defaultCfg)) {
|
|
570
1107
|
configPath2 = defaultCfg;
|
|
571
|
-
} else if (
|
|
1108
|
+
} else if (fs2.existsSync(homeCfg)) {
|
|
572
1109
|
configPath2 = homeCfg;
|
|
573
|
-
cwd =
|
|
1110
|
+
cwd = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
574
1111
|
} else {
|
|
575
1112
|
const ptr = readHomePointer();
|
|
576
1113
|
if (ptr?.stateDir) {
|
|
577
|
-
const ptrCfg =
|
|
578
|
-
if (
|
|
1114
|
+
const ptrCfg = path2.join(ptr.stateDir, "configs", "main7.json");
|
|
1115
|
+
if (fs2.existsSync(ptrCfg)) {
|
|
579
1116
|
configPath2 = ptrCfg;
|
|
580
1117
|
cwd = ptr.stateDir;
|
|
581
1118
|
}
|
|
@@ -588,22 +1125,22 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
588
1125
|
}
|
|
589
1126
|
}
|
|
590
1127
|
}
|
|
591
|
-
const { execSync } = await import("node:child_process");
|
|
1128
|
+
const { execSync: execSync2 } = await import("node:child_process");
|
|
592
1129
|
const engine7Bin = process.argv[1];
|
|
593
1130
|
if (action === "install") {
|
|
594
1131
|
if (process.platform === "win32") {
|
|
595
1132
|
const taskName = "Engine7";
|
|
596
1133
|
const nodeExe = process.execPath;
|
|
597
|
-
const cliMjs =
|
|
598
|
-
const absConfig =
|
|
599
|
-
const wrapperPath =
|
|
1134
|
+
const cliMjs = path2.resolve(engine7Bin);
|
|
1135
|
+
const absConfig = path2.resolve(configPath2);
|
|
1136
|
+
const wrapperPath = path2.join(cwd, "engine7-start.cmd");
|
|
600
1137
|
const wrapperContent = `@echo off\r
|
|
601
1138
|
cd /d "${cwd}"\r
|
|
602
1139
|
"${nodeExe}" "${cliMjs}" start --config "${absConfig}"\r
|
|
603
1140
|
`;
|
|
604
|
-
|
|
1141
|
+
fs2.writeFileSync(wrapperPath, wrapperContent);
|
|
605
1142
|
try {
|
|
606
|
-
|
|
1143
|
+
execSync2(`schtasks /create /tn "${taskName}" /tr "${wrapperPath}" /sc onlogon /rl highest /f`, { stdio: "inherit", shell: true });
|
|
607
1144
|
console.log(`\u2705 Windows \u8BA1\u5212\u4EFB\u52A1 "${taskName}" \u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
608
1145
|
console.log(` wrapper: ${wrapperPath}`);
|
|
609
1146
|
} catch {
|
|
@@ -612,11 +1149,11 @@ cd /d "${cwd}"\r
|
|
|
612
1149
|
}
|
|
613
1150
|
} else if (process.platform === "darwin") {
|
|
614
1151
|
const label = "com.engine7.agent";
|
|
615
|
-
const plistDir =
|
|
616
|
-
|
|
617
|
-
const plistPath =
|
|
618
|
-
const localCli =
|
|
619
|
-
const cliMjs =
|
|
1152
|
+
const plistDir = path2.join(process.env.HOME, "Library", "LaunchAgents");
|
|
1153
|
+
fs2.mkdirSync(plistDir, { recursive: true });
|
|
1154
|
+
const plistPath = path2.join(plistDir, `${label}.plist`);
|
|
1155
|
+
const localCli = path2.join("node_modules", "engine7", "dist", "cli.mjs");
|
|
1156
|
+
const cliMjs = fs2.existsSync(localCli) ? path2.resolve(localCli) : path2.join(path2.dirname(path2.dirname(engine7Bin)), "lib", "node_modules", "engine7", "dist", "cli.mjs");
|
|
620
1157
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
621
1158
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
622
1159
|
<plist version="1.0">
|
|
@@ -628,16 +1165,16 @@ cd /d "${cwd}"\r
|
|
|
628
1165
|
<string>${cliMjs}</string>
|
|
629
1166
|
<string>start</string>
|
|
630
1167
|
<string>--config</string>
|
|
631
|
-
<string>${
|
|
1168
|
+
<string>${path2.resolve(configPath2)}</string>
|
|
632
1169
|
</array>
|
|
633
1170
|
<key>WorkingDirectory</key><string>${cwd}</string>
|
|
634
1171
|
<key>RunAtLoad</key><true/>
|
|
635
1172
|
<key>KeepAlive</key><true/>
|
|
636
1173
|
</dict>
|
|
637
1174
|
</plist>`;
|
|
638
|
-
|
|
1175
|
+
fs2.writeFileSync(plistPath, plist);
|
|
639
1176
|
try {
|
|
640
|
-
|
|
1177
|
+
execSync2(`launchctl load "${plistPath}"`, { stdio: "inherit" });
|
|
641
1178
|
console.log(`\u2705 Mac launchd \u670D\u52A1\u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
642
1179
|
console.log(` ${plistPath}`);
|
|
643
1180
|
} catch {
|
|
@@ -646,9 +1183,9 @@ cd /d "${cwd}"\r
|
|
|
646
1183
|
}
|
|
647
1184
|
} else {
|
|
648
1185
|
const svcName = "engine7";
|
|
649
|
-
const svcDir =
|
|
650
|
-
|
|
651
|
-
const svcPath =
|
|
1186
|
+
const svcDir = path2.join(process.env.HOME, ".config", "systemd", "user");
|
|
1187
|
+
fs2.mkdirSync(svcDir, { recursive: true });
|
|
1188
|
+
const svcPath = path2.join(svcDir, `${svcName}.service`);
|
|
652
1189
|
const svc = `[Unit]
|
|
653
1190
|
Description=Engine 7 Agent
|
|
654
1191
|
After=network.target
|
|
@@ -656,16 +1193,16 @@ After=network.target
|
|
|
656
1193
|
[Service]
|
|
657
1194
|
Type=simple
|
|
658
1195
|
WorkingDirectory=${cwd}
|
|
659
|
-
ExecStart=${process.execPath} ${engine7Bin} start --config ${
|
|
1196
|
+
ExecStart=${process.execPath} ${engine7Bin} start --config ${path2.resolve(configPath2)}
|
|
660
1197
|
Restart=on-failure
|
|
661
1198
|
RestartSec=10
|
|
662
1199
|
|
|
663
1200
|
[Install]
|
|
664
1201
|
WantedBy=default.target`;
|
|
665
|
-
|
|
1202
|
+
fs2.writeFileSync(svcPath, svc);
|
|
666
1203
|
try {
|
|
667
|
-
|
|
668
|
-
|
|
1204
|
+
execSync2(`systemctl --user daemon-reload`, { stdio: "inherit" });
|
|
1205
|
+
execSync2(`systemctl --user enable ${svcName}`, { stdio: "inherit" });
|
|
669
1206
|
console.log(`\u2705 systemd \u7528\u6237\u670D\u52A1\u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
670
1207
|
console.log(` ${svcPath}`);
|
|
671
1208
|
} catch {
|
|
@@ -676,35 +1213,35 @@ WantedBy=default.target`;
|
|
|
676
1213
|
} else if (action === "uninstall") {
|
|
677
1214
|
if (process.platform === "win32") {
|
|
678
1215
|
try {
|
|
679
|
-
|
|
1216
|
+
execSync2(`schtasks /delete /tn "Engine7" /f`, { stdio: "inherit", shell: true });
|
|
680
1217
|
console.log(`\u2705 Windows \u8BA1\u5212\u4EFB\u52A1\u5DF2\u5220\u9664`);
|
|
681
1218
|
} catch {
|
|
682
1219
|
console.error("\u274C \u5220\u9664\u5931\u8D25\uFF0C\u4EFB\u52A1\u53EF\u80FD\u4E0D\u5B58\u5728");
|
|
683
1220
|
process.exit(1);
|
|
684
1221
|
}
|
|
685
1222
|
} else if (process.platform === "darwin") {
|
|
686
|
-
const plistPath =
|
|
1223
|
+
const plistPath = path2.join(process.env.HOME, "Library", "LaunchAgents", "com.engine7.agent.plist");
|
|
687
1224
|
try {
|
|
688
|
-
|
|
1225
|
+
execSync2(`launchctl unload "${plistPath}"`, { stdio: "inherit" });
|
|
689
1226
|
} catch {
|
|
690
1227
|
}
|
|
691
1228
|
try {
|
|
692
|
-
|
|
1229
|
+
fs2.unlinkSync(plistPath);
|
|
693
1230
|
} catch {
|
|
694
1231
|
}
|
|
695
1232
|
console.log(`\u2705 Mac launchd \u670D\u52A1\u5DF2\u5220\u9664`);
|
|
696
1233
|
} else {
|
|
697
1234
|
try {
|
|
698
|
-
|
|
1235
|
+
execSync2(`systemctl --user disable engine7`, { stdio: "inherit" });
|
|
699
1236
|
} catch {
|
|
700
1237
|
}
|
|
701
|
-
const svcPath =
|
|
1238
|
+
const svcPath = path2.join(process.env.HOME, ".config", "systemd", "user", "engine7.service");
|
|
702
1239
|
try {
|
|
703
|
-
|
|
1240
|
+
fs2.unlinkSync(svcPath);
|
|
704
1241
|
} catch {
|
|
705
1242
|
}
|
|
706
1243
|
try {
|
|
707
|
-
|
|
1244
|
+
execSync2(`systemctl --user daemon-reload`, { stdio: "inherit" });
|
|
708
1245
|
} catch {
|
|
709
1246
|
}
|
|
710
1247
|
console.log(`\u2705 systemd \u7528\u6237\u670D\u52A1\u5DF2\u5220\u9664`);
|
|
@@ -712,20 +1249,20 @@ WantedBy=default.target`;
|
|
|
712
1249
|
} else if (action === "status") {
|
|
713
1250
|
if (process.platform === "win32") {
|
|
714
1251
|
try {
|
|
715
|
-
|
|
1252
|
+
execSync2(`schtasks /query /tn "Engine7"`, { stdio: "inherit", shell: true });
|
|
716
1253
|
} catch {
|
|
717
1254
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
718
1255
|
}
|
|
719
1256
|
} else if (process.platform === "darwin") {
|
|
720
|
-
const plistPath =
|
|
721
|
-
if (
|
|
1257
|
+
const plistPath = path2.join(process.env.HOME, "Library", "LaunchAgents", "com.engine7.agent.plist");
|
|
1258
|
+
if (fs2.existsSync(plistPath)) {
|
|
722
1259
|
console.log("\u2705 \u5DF2\u5B89\u88C5\uFF08launchd\uFF09");
|
|
723
1260
|
} else {
|
|
724
1261
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
725
1262
|
}
|
|
726
1263
|
} else {
|
|
727
1264
|
try {
|
|
728
|
-
|
|
1265
|
+
execSync2(`systemctl --user is-enabled engine7`, { stdio: "inherit" });
|
|
729
1266
|
} catch {
|
|
730
1267
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
731
1268
|
}
|
|
@@ -741,8 +1278,8 @@ WantedBy=default.target`;
|
|
|
741
1278
|
console.log(`
|
|
742
1279
|
\u{1F680} engine7 init`);
|
|
743
1280
|
console.log(` state-dir: ${opts.stateDir}`);
|
|
744
|
-
if (
|
|
745
|
-
const files =
|
|
1281
|
+
if (fs2.existsSync(opts.stateDir) && !opts.dryRun) {
|
|
1282
|
+
const files = fs2.readdirSync(opts.stateDir);
|
|
746
1283
|
if (files.length > 0) {
|
|
747
1284
|
console.error(`
|
|
748
1285
|
\u274C \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A: ${opts.stateDir}`);
|
|
@@ -760,7 +1297,7 @@ WantedBy=default.target`;
|
|
|
760
1297
|
rl.close();
|
|
761
1298
|
}
|
|
762
1299
|
const config = generateConfig(values);
|
|
763
|
-
const configPath =
|
|
1300
|
+
const configPath = path2.join(opts.stateDir, "configs", "main7.json");
|
|
764
1301
|
const tree = buildDirTree(values);
|
|
765
1302
|
tree.push({
|
|
766
1303
|
path: configPath,
|
|
@@ -771,9 +1308,9 @@ WantedBy=default.target`;
|
|
|
771
1308
|
dryRun(tree);
|
|
772
1309
|
const skillsSrc = findTemplateDir("skills");
|
|
773
1310
|
if (skillsSrc) {
|
|
774
|
-
const skills =
|
|
1311
|
+
const skills = fs2.readdirSync(skillsSrc);
|
|
775
1312
|
for (const s of skills) {
|
|
776
|
-
console.log(` \u{1F4C1} ${
|
|
1313
|
+
console.log(` \u{1F4C1} ${path2.join(opts.stateDir, "workspace", "skills", s)}`);
|
|
777
1314
|
}
|
|
778
1315
|
}
|
|
779
1316
|
} else {
|
|
@@ -781,55 +1318,55 @@ WantedBy=default.target`;
|
|
|
781
1318
|
execute(tree);
|
|
782
1319
|
const skillsSrc = findTemplateDir("skills");
|
|
783
1320
|
if (skillsSrc) {
|
|
784
|
-
const skillsDest =
|
|
785
|
-
|
|
786
|
-
const skills =
|
|
1321
|
+
const skillsDest = path2.join(opts.stateDir, "workspace", "skills");
|
|
1322
|
+
fs2.mkdirSync(skillsDest, { recursive: true });
|
|
1323
|
+
const skills = fs2.readdirSync(skillsSrc);
|
|
787
1324
|
for (const s of skills) {
|
|
788
|
-
copyDirRecursive(
|
|
1325
|
+
copyDirRecursive(path2.join(skillsSrc, s), path2.join(skillsDest, s));
|
|
789
1326
|
console.log(` \u{1F4E6} skill: ${s}`);
|
|
790
1327
|
}
|
|
791
1328
|
}
|
|
792
1329
|
console.log(`
|
|
793
1330
|
\u2705 \u521D\u59CB\u5316\u5B8C\u6210\uFF01
|
|
794
1331
|
`);
|
|
795
|
-
const homePointer =
|
|
1332
|
+
const homePointer = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
796
1333
|
const pointerData = { stateDir: opts.stateDir };
|
|
797
|
-
|
|
1334
|
+
fs2.writeFileSync(homePointer, JSON.stringify(pointerData, null, 2) + "\n");
|
|
798
1335
|
console.log(` \u{1F4C4} ${homePointer}\uFF08\u4ECE\u4EFB\u610F\u76EE\u5F55\u90FD\u80FD\u627E\u5230\u6B64 agent\uFF09`);
|
|
799
1336
|
console.log(`\u4E0B\u4E00\u6B65\uFF1A`);
|
|
800
1337
|
console.log(` 1. \u68C0\u67E5\u5E76\u4FEE\u6539 ${opts.stateDir}/configs/main7.json`);
|
|
801
1338
|
console.log(` 2. \u542F\u52A8 Engine: engine7 start\uFF08\u5728\u4EFB\u610F\u76EE\u5F55\u90FD\u884C\uFF09`);
|
|
802
1339
|
console.log(` 3. \u5F00\u673A\u81EA\u542F: engine7 service install\uFF08\u5728\u4EFB\u610F\u76EE\u5F55\u90FD\u884C\uFF09`);
|
|
803
|
-
console.log(` 4. \u67E5\u770B workspace: ${
|
|
1340
|
+
console.log(` 4. \u67E5\u770B workspace: ${path2.join(opts.stateDir, "workspace")}`);
|
|
804
1341
|
}
|
|
805
1342
|
}
|
|
806
1343
|
function copyDirRecursive(src, dest) {
|
|
807
|
-
|
|
808
|
-
for (const entry of
|
|
809
|
-
const srcPath =
|
|
810
|
-
const destPath =
|
|
1344
|
+
fs2.mkdirSync(dest, { recursive: true });
|
|
1345
|
+
for (const entry of fs2.readdirSync(src, { withFileTypes: true })) {
|
|
1346
|
+
const srcPath = path2.join(src, entry.name);
|
|
1347
|
+
const destPath = path2.join(dest, entry.name);
|
|
811
1348
|
if (entry.isDirectory()) {
|
|
812
1349
|
copyDirRecursive(srcPath, destPath);
|
|
813
1350
|
} else {
|
|
814
|
-
|
|
1351
|
+
fs2.copyFileSync(srcPath, destPath);
|
|
815
1352
|
}
|
|
816
1353
|
}
|
|
817
1354
|
}
|
|
818
1355
|
function findTemplateDir(name) {
|
|
819
1356
|
const candidates = [
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1357
|
+
path2.join(__dirname, "..", "templates", name),
|
|
1358
|
+
path2.join(__dirname, "..", "..", "templates", name),
|
|
1359
|
+
path2.join(process.cwd(), "templates", name)
|
|
823
1360
|
];
|
|
824
1361
|
for (const p of candidates) {
|
|
825
|
-
if (
|
|
1362
|
+
if (fs2.existsSync(p)) return p;
|
|
826
1363
|
}
|
|
827
1364
|
return null;
|
|
828
1365
|
}
|
|
829
1366
|
function readHomePointer() {
|
|
830
|
-
const ptrPath =
|
|
1367
|
+
const ptrPath = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
831
1368
|
try {
|
|
832
|
-
return JSON.parse(
|
|
1369
|
+
return JSON.parse(fs2.readFileSync(ptrPath, "utf-8"));
|
|
833
1370
|
} catch {
|
|
834
1371
|
return null;
|
|
835
1372
|
}
|