engine7 7.1.7 → 7.1.9
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 +661 -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,500 @@
|
|
|
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 archiveFile = path.join(tmpDir, `${agentName}-${version}.zip`);
|
|
212
|
+
console.log(`
|
|
213
|
+
\u{1F5DC}\uFE0F \u6253\u5305\u4E2D...`);
|
|
214
|
+
if (process.platform === "win32") {
|
|
215
|
+
const stagingParent = path.dirname(stagingDir).replace(/\\/g, "/");
|
|
216
|
+
const childName = path.basename(stagingDir);
|
|
217
|
+
execSync(`powershell -Command "Compress-Archive -Path '${stagingParent}/${childName}' -DestinationPath '${archiveFile}' -Force"`, { stdio: "pipe" });
|
|
218
|
+
} else {
|
|
219
|
+
execSync(`cd "${tmpDir}" && zip -r -q "${archiveFile}" ${agentName}`, { stdio: "pipe" });
|
|
220
|
+
}
|
|
221
|
+
const archiveSize = fs.statSync(archiveFile).size;
|
|
222
|
+
console.log(`\u2705 \u6253\u5305\u5B8C\u6210: ${archiveFile} (${(archiveSize / 1024 / 1024).toFixed(1)} MB, ${fileCount} \u6587\u4EF6)`);
|
|
223
|
+
const cfg = loadTravelConfig();
|
|
224
|
+
if (!cfg || !cfg.githubToken) {
|
|
225
|
+
console.log(`
|
|
226
|
+
\u26A0\uFE0F \u672A\u914D\u7F6E GitHub\uFF0Ctar.gz \u5DF2\u751F\u6210: ${tarFile}`);
|
|
227
|
+
console.log(` \u914D\u7F6E GitHub: \u7F16\u8F91 ${CONFIG_PATH}`);
|
|
228
|
+
console.log(` \u6216\u624B\u52A8\u4E0A\u4F20\u5230 GitHub private repo release`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
await uploadToGitHub(cfg, archiveFile, agentName, version, manifest);
|
|
232
|
+
}
|
|
233
|
+
async function doImport(opts) {
|
|
234
|
+
const { agentName, stateDir, version, dryRun: dryRun2 } = opts;
|
|
235
|
+
console.log(`\u{1F4E5} engine7 import`);
|
|
236
|
+
console.log(` agent: ${agentName}`);
|
|
237
|
+
console.log(` target: ${stateDir}`);
|
|
238
|
+
const cfg = loadTravelConfig();
|
|
239
|
+
if (!cfg || !cfg.githubToken) {
|
|
240
|
+
console.error(`\u274C \u672A\u914D\u7F6E GitHub\uFF0C\u65E0\u6CD5\u4E0B\u8F7D`);
|
|
241
|
+
console.error(` \u914D\u7F6E: \u7F16\u8F91 ${CONFIG_PATH}`);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
const tmpDir = path.join(os.tmpdir(), `engine7-import-${agentName}-${Date.now()}`);
|
|
245
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
246
|
+
const archiveFile = await downloadFromGitHub(cfg, agentName, version, tmpDir);
|
|
247
|
+
console.log(`\u2705 \u4E0B\u8F7D\u5B8C\u6210: ${archiveFile}`);
|
|
248
|
+
if (dryRun2) {
|
|
249
|
+
console.log(`
|
|
250
|
+
[DRY RUN] \u53EA\u4E0B\u8F7D\u4E0D\u6062\u590D`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
console.log(`\u{1F4C2} \u89E3\u5305\u4E2D...`);
|
|
254
|
+
if (archiveFile.endsWith(".tar.gz")) {
|
|
255
|
+
if (process.platform === "win32") {
|
|
256
|
+
execSync(`tar -xzf "${archiveFile}" -C "${tmpDir}"`, { stdio: "pipe" });
|
|
257
|
+
} else {
|
|
258
|
+
execSync(`tar -xzf "${archiveFile}" -C "${tmpDir}"`, { stdio: "pipe" });
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
if (process.platform === "win32") {
|
|
262
|
+
execSync(`powershell -Command "Expand-Archive -Path '${archiveFile}' -DestinationPath '${tmpDir}' -Force"`, { stdio: "pipe" });
|
|
263
|
+
} else {
|
|
264
|
+
execSync(`unzip -q -o "${archiveFile}" -d "${tmpDir}"`, { stdio: "pipe" });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const stagingDir = path.join(tmpDir, agentName);
|
|
268
|
+
const manifestPath = path.join(stagingDir, "manifest.json");
|
|
269
|
+
if (!fs.existsSync(manifestPath)) {
|
|
270
|
+
console.error(`\u274C manifest.json \u4E0D\u5B58\u5728\uFF0C\u6587\u4EF6\u53EF\u80FD\u635F\u574F`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
274
|
+
const workspace = path.join(stateDir, "workspace");
|
|
275
|
+
const engineHome = path.join(os.homedir(), ".openclaw");
|
|
276
|
+
const dirs = { workspace, stateDir, engineHome };
|
|
277
|
+
console.log(` \u7248\u672C: ${manifest.version}`);
|
|
278
|
+
console.log(` \u521B\u5EFA: ${manifest.createdAt}`);
|
|
279
|
+
console.log(` \u6587\u4EF6\u6570: ${manifest.fileCount}`);
|
|
280
|
+
const wsStaging = path.join(stagingDir, "workspace");
|
|
281
|
+
let restoredCount = 0;
|
|
282
|
+
if (fs.existsSync(wsStaging)) {
|
|
283
|
+
const allFiles = collectAllFiles(wsStaging);
|
|
284
|
+
for (const srcFile of allFiles) {
|
|
285
|
+
const relPath = path.relative(wsStaging, srcFile);
|
|
286
|
+
const destFile = path.join(workspace, relPath);
|
|
287
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
288
|
+
if (isTextFile(srcFile)) {
|
|
289
|
+
const content = fs.readFileSync(srcFile, "utf-8");
|
|
290
|
+
fs.writeFileSync(destFile, restoreContent(content, dirs));
|
|
291
|
+
} else {
|
|
292
|
+
fs.copyFileSync(srcFile, destFile);
|
|
293
|
+
}
|
|
294
|
+
restoredCount++;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const agentsStaging = path.join(stagingDir, "agents");
|
|
298
|
+
if (fs.existsSync(agentsStaging)) {
|
|
299
|
+
const sessionFiles = collectAllFiles(agentsStaging);
|
|
300
|
+
for (const srcFile of sessionFiles) {
|
|
301
|
+
const relPath = path.relative(stagingDir, srcFile);
|
|
302
|
+
const destFile = path.join(stateDir, relPath);
|
|
303
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
304
|
+
fs.copyFileSync(srcFile, destFile);
|
|
305
|
+
restoredCount++;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
console.log(`\u2705 \u6062\u590D\u5B8C\u6210: ${restoredCount} \u6587\u4EF6 \u2192 ${stateDir}`);
|
|
309
|
+
console.log(`
|
|
310
|
+
\u{1F4A1} \u4E0B\u4E00\u6B65: engine7 start --config <path>`);
|
|
311
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
312
|
+
}
|
|
313
|
+
async function uploadToGitHub(cfg, archiveFile, agentName, version, manifest) {
|
|
314
|
+
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
315
|
+
const tag = `${agentName}-${version}`;
|
|
316
|
+
console.log(`
|
|
317
|
+
\u{1F4E4} \u4E0A\u4F20\u5230 GitHub: ${repo} release ${tag}`);
|
|
318
|
+
try {
|
|
319
|
+
const releaseBody = Object.entries(manifest).map(([k, v]) => `- **${k}**: ${typeof v === "object" ? JSON.stringify(v) : v}`).join("\n");
|
|
320
|
+
const createRes = await fetch(`https://api.github.com/repos/${repo}/releases`, {
|
|
321
|
+
method: "POST",
|
|
322
|
+
headers: {
|
|
323
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
324
|
+
"Accept": "application/vnd.github+json",
|
|
325
|
+
"Content-Type": "application/json"
|
|
326
|
+
},
|
|
327
|
+
body: JSON.stringify({
|
|
328
|
+
tag_name: tag,
|
|
329
|
+
name: `${agentName} ${version}`,
|
|
330
|
+
body: releaseBody,
|
|
331
|
+
prerelease: false,
|
|
332
|
+
make_latest: "true"
|
|
333
|
+
})
|
|
334
|
+
});
|
|
335
|
+
if (!createRes.ok) {
|
|
336
|
+
const err = await createRes.text();
|
|
337
|
+
throw new Error(`\u521B\u5EFA release \u5931\u8D25: ${createRes.status} ${err}`);
|
|
338
|
+
}
|
|
339
|
+
const release = await createRes.json();
|
|
340
|
+
const uploadUrl = release.upload_url.replace("{?name,label}", "");
|
|
341
|
+
const fileBuffer = fs.readFileSync(archiveFile);
|
|
342
|
+
const fileName = path.basename(archiveFile);
|
|
343
|
+
const uploadRes = await fetch(`${uploadUrl}?name=${fileName}`, {
|
|
344
|
+
method: "POST",
|
|
345
|
+
headers: {
|
|
346
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
347
|
+
"Accept": "application/vnd.github+json",
|
|
348
|
+
"Content-Type": "application/zip",
|
|
349
|
+
"Content-Length": String(fileBuffer.length)
|
|
350
|
+
},
|
|
351
|
+
body: fileBuffer
|
|
352
|
+
});
|
|
353
|
+
if (!uploadRes.ok) {
|
|
354
|
+
const err = await uploadRes.text();
|
|
355
|
+
throw new Error(`\u4E0A\u4F20 asset \u5931\u8D25: ${uploadRes.status} ${err}`);
|
|
356
|
+
}
|
|
357
|
+
console.log(`\u2705 \u4E0A\u4F20\u6210\u529F!`);
|
|
358
|
+
console.log(` release: ${release.html_url}`);
|
|
359
|
+
} catch (e) {
|
|
360
|
+
console.error(`\u274C GitHub \u4E0A\u4F20\u5931\u8D25: ${e.message}`);
|
|
361
|
+
console.error(` archive \u4ECD\u5728: ${archiveFile}`);
|
|
362
|
+
throw e;
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
fs.rmSync(path.dirname(archiveFile), { recursive: true, force: true });
|
|
366
|
+
} catch {
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
async function downloadFromGitHub(cfg, agentName, version, destDir) {
|
|
370
|
+
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
371
|
+
const tag = version || await getLatestReleaseTag(cfg, agentName);
|
|
372
|
+
console.log(`\u2B07\uFE0F \u4E0B\u8F7D: ${repo} release ${tag}`);
|
|
373
|
+
const res = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${tag}`, {
|
|
374
|
+
headers: {
|
|
375
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
376
|
+
"Accept": "application/vnd.github+json"
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
if (!res.ok) {
|
|
380
|
+
throw new Error(`\u83B7\u53D6 release \u5931\u8D25: ${res.status}`);
|
|
381
|
+
}
|
|
382
|
+
const release = await res.json();
|
|
383
|
+
const asset = release.assets?.find((a) => a.name.endsWith(".zip") || a.name.endsWith(".tar.gz"));
|
|
384
|
+
if (!asset) {
|
|
385
|
+
throw new Error(`release ${tag} \u6CA1\u6709 zip/tar.gz asset`);
|
|
386
|
+
}
|
|
387
|
+
const downloadRes = await fetch(asset.url, {
|
|
388
|
+
headers: {
|
|
389
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
390
|
+
"Accept": "application/octet-stream"
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
if (!downloadRes.ok) {
|
|
394
|
+
throw new Error(`\u4E0B\u8F7D asset \u5931\u8D25: ${downloadRes.status}`);
|
|
395
|
+
}
|
|
396
|
+
const buffer = Buffer.from(await downloadRes.arrayBuffer());
|
|
397
|
+
const archiveFile = path.join(destDir, asset.name);
|
|
398
|
+
fs.writeFileSync(archiveFile, buffer);
|
|
399
|
+
return archiveFile;
|
|
400
|
+
}
|
|
401
|
+
async function getLatestReleaseTag(cfg, agentName) {
|
|
402
|
+
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
403
|
+
const res = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, {
|
|
404
|
+
headers: {
|
|
405
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
406
|
+
"Accept": "application/vnd.github+json"
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
if (!res.ok) {
|
|
410
|
+
throw new Error(`\u83B7\u53D6 release \u5217\u8868\u5931\u8D25: ${res.status}`);
|
|
411
|
+
}
|
|
412
|
+
const releases = await res.json();
|
|
413
|
+
const match = releases.find((r) => r.tag_name?.startsWith(`${agentName}-`));
|
|
414
|
+
if (!match) {
|
|
415
|
+
throw new Error(`\u6CA1\u6709\u627E\u5230 ${agentName} \u7684 release`);
|
|
416
|
+
}
|
|
417
|
+
return match.tag_name;
|
|
418
|
+
}
|
|
419
|
+
var CONFIG_PATH, PATH_PLACEHOLDERS, WORKSPACE_INCLUDE, WORKSPACE_OPTIONAL, WORKSPACE_EXCLUDE, TEXT_EXTENSIONS;
|
|
420
|
+
var init_cli_travel = __esm({
|
|
421
|
+
"src/cli-travel.ts"() {
|
|
422
|
+
"use strict";
|
|
423
|
+
CONFIG_PATH = path.join(os.homedir(), ".engine7-travel.json");
|
|
424
|
+
PATH_PLACEHOLDERS = [
|
|
425
|
+
{ placeholder: "{{WORKSPACE}}", getOriginal: (d) => d.workspace },
|
|
426
|
+
{ placeholder: "{{ENGINE_HOME}}", getOriginal: (d) => d.engineHome },
|
|
427
|
+
{ placeholder: "{{STATE_DIR}}", getOriginal: (d) => d.stateDir }
|
|
428
|
+
];
|
|
429
|
+
WORKSPACE_INCLUDE = /* @__PURE__ */ new Set([
|
|
430
|
+
// 核心文件
|
|
431
|
+
"AGENTS.md",
|
|
432
|
+
"SOUL.md",
|
|
433
|
+
"MEMORY.md",
|
|
434
|
+
"USER.md",
|
|
435
|
+
"HEARTBEAT.md",
|
|
436
|
+
"INDEX.md",
|
|
437
|
+
"SESSION-STATE.md",
|
|
438
|
+
// 核心目录
|
|
439
|
+
"prompts",
|
|
440
|
+
"topics",
|
|
441
|
+
"memory",
|
|
442
|
+
"inner-voice",
|
|
443
|
+
"docs",
|
|
444
|
+
"skills",
|
|
445
|
+
"voice-chat",
|
|
446
|
+
"scripts",
|
|
447
|
+
"selfie",
|
|
448
|
+
"moodboard",
|
|
449
|
+
// 状态文件
|
|
450
|
+
"calendar.db",
|
|
451
|
+
"nudge-state.json"
|
|
452
|
+
]);
|
|
453
|
+
WORKSPACE_OPTIONAL = /* @__PURE__ */ new Set([
|
|
454
|
+
"images",
|
|
455
|
+
"tools"
|
|
456
|
+
]);
|
|
457
|
+
WORKSPACE_EXCLUDE = /* @__PURE__ */ new Set([
|
|
458
|
+
"livestream",
|
|
459
|
+
"content-library",
|
|
460
|
+
"tmp",
|
|
461
|
+
".git",
|
|
462
|
+
"node_modules",
|
|
463
|
+
"memory_runs",
|
|
464
|
+
"workspace",
|
|
465
|
+
"prompt-archive",
|
|
466
|
+
"aim-archive",
|
|
467
|
+
"test-agent",
|
|
468
|
+
"nul",
|
|
469
|
+
"*.bak*",
|
|
470
|
+
"*.bak-*"
|
|
471
|
+
]);
|
|
472
|
+
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
473
|
+
".md",
|
|
474
|
+
".json",
|
|
475
|
+
".txt",
|
|
476
|
+
".js",
|
|
477
|
+
".ts",
|
|
478
|
+
".mjs",
|
|
479
|
+
".py",
|
|
480
|
+
".yaml",
|
|
481
|
+
".yml",
|
|
482
|
+
".cmd",
|
|
483
|
+
".bat",
|
|
484
|
+
".sh",
|
|
485
|
+
".csv",
|
|
486
|
+
".html"
|
|
487
|
+
]);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// src/cli-init.ts
|
|
492
|
+
import * as path2 from "node:path";
|
|
493
|
+
import * as fs2 from "node:fs";
|
|
4
494
|
import * as readline from "node:readline";
|
|
5
495
|
import { fileURLToPath } from "node:url";
|
|
6
496
|
var __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
var __dirname =
|
|
497
|
+
var __dirname = path2.dirname(__filename);
|
|
8
498
|
var SCHEMA_VERSION = 1;
|
|
9
499
|
function parseArgs() {
|
|
10
500
|
const args = process.argv.slice(2);
|
|
@@ -26,10 +516,10 @@ function parseArgs() {
|
|
|
26
516
|
}
|
|
27
517
|
}
|
|
28
518
|
if (!stateDir) {
|
|
29
|
-
stateDir =
|
|
519
|
+
stateDir = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
30
520
|
console.log(` \u4F7F\u7528\u9ED8\u8BA4\u76EE\u5F55: ${stateDir}`);
|
|
31
521
|
}
|
|
32
|
-
stateDir =
|
|
522
|
+
stateDir = path2.resolve(stateDir);
|
|
33
523
|
return { stateDir, quick, dryRun: dryRun2 };
|
|
34
524
|
}
|
|
35
525
|
function printHelp() {
|
|
@@ -41,6 +531,8 @@ Engine 7 \u2014 Self-hosted AI agent engine
|
|
|
41
531
|
engine7 start [--config <path>] \u542F\u52A8 Engine
|
|
42
532
|
engine7 restart [--config <path>] \u91CD\u542F Engine\uFF08\u6740\u65E7\u8FDB\u7A0B+\u542F\u52A8\uFF09
|
|
43
533
|
engine7 service install|uninstall|status \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
534
|
+
engine7 export [--state-dir <path>] \u6253\u5305 agent workspace \u2192 GitHub release
|
|
535
|
+
engine7 import --state-dir <path> \u4ECE GitHub release \u6062\u590D agent
|
|
44
536
|
|
|
45
537
|
init \u9009\u9879:
|
|
46
538
|
--state-dir <path> agent \u6839\u76EE\u5F55\uFF08\u5FC5\u586B\uFF0C\u652F\u6301\u76F8\u5BF9\u8DEF\u5F84\uFF09
|
|
@@ -50,11 +542,25 @@ init \u9009\u9879:
|
|
|
50
542
|
start \u9009\u9879:
|
|
51
543
|
--config <path> config \u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4: configs/main7.json\uFF09
|
|
52
544
|
|
|
545
|
+
export \u9009\u9879:
|
|
546
|
+
--state-dir <path> agent \u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4: \u5F53\u524D\u76EE\u5F55\u4E0A\u7EA7\uFF09
|
|
547
|
+
--agent <name> agent \u540D\u79F0\uFF08\u9ED8\u8BA4: state-dir basename\uFF09
|
|
548
|
+
--note <text> \u7248\u672C\u5907\u6CE8
|
|
549
|
+
--dry-run \u53EA\u5217\u51FA\u4F1A\u6253\u5305\u4EC0\u4E48\uFF0C\u4E0D\u5B9E\u9645\u4E0A\u4F20
|
|
550
|
+
|
|
551
|
+
import \u9009\u9879:
|
|
552
|
+
--state-dir <path> \u76EE\u6807 agent \u6839\u76EE\u5F55\uFF08\u5FC5\u586B\uFF09
|
|
553
|
+
--agent <name> agent \u540D\u79F0
|
|
554
|
+
--version <tag> \u6307\u5B9A\u7248\u672C\uFF08\u9ED8\u8BA4: \u6700\u65B0\uFF09
|
|
555
|
+
--dry-run \u53EA\u4E0B\u8F7D\u4E0D\u6062\u590D
|
|
556
|
+
|
|
53
557
|
\u793A\u4F8B:
|
|
54
558
|
engine7 init --state-dir D:/my-agent
|
|
55
559
|
engine7 init --state-dir ./my-agent --quick
|
|
56
560
|
engine7 start
|
|
57
561
|
engine7 start --config configs/xiaowen.json
|
|
562
|
+
engine7 export --state-dir D:/xiaoke --agent xiaoke --note "travel test"
|
|
563
|
+
engine7 import --state-dir D:/xiaoke --agent xiaoke
|
|
58
564
|
`);
|
|
59
565
|
}
|
|
60
566
|
function createReadline() {
|
|
@@ -166,7 +672,7 @@ async function interactiveConfig(rl, defaults) {
|
|
|
166
672
|
return v;
|
|
167
673
|
}
|
|
168
674
|
function generateConfig(v) {
|
|
169
|
-
const workspace =
|
|
675
|
+
const workspace = path2.join(v.stateDir, "workspace").replace(/\\/g, "/");
|
|
170
676
|
const config = {
|
|
171
677
|
schemaVersion: SCHEMA_VERSION,
|
|
172
678
|
stateDir: v.stateDir.replace(/\\/g, "/"),
|
|
@@ -292,45 +798,45 @@ function generateConfig(v) {
|
|
|
292
798
|
}
|
|
293
799
|
function buildDirTree(v) {
|
|
294
800
|
const d = v.stateDir;
|
|
295
|
-
const w =
|
|
801
|
+
const w = path2.join(d, "workspace");
|
|
296
802
|
const now = (/* @__PURE__ */ new Date()).toLocaleString("sv-SE", { timeZone: "Asia/Shanghai", hour12: false });
|
|
297
803
|
return [
|
|
298
804
|
// 目录
|
|
299
|
-
{ path:
|
|
300
|
-
{ path:
|
|
301
|
-
{ path:
|
|
302
|
-
{ path:
|
|
303
|
-
{ path:
|
|
304
|
-
{ path:
|
|
305
|
-
{ path:
|
|
306
|
-
{ path:
|
|
307
|
-
{ path:
|
|
308
|
-
{ path:
|
|
309
|
-
{ path:
|
|
805
|
+
{ path: path2.join(d, "configs"), type: "dir" },
|
|
806
|
+
{ path: path2.join(d, "state", "agents", "main", "sessions"), type: "dir" },
|
|
807
|
+
{ path: path2.join(w, "prompts"), type: "dir" },
|
|
808
|
+
{ path: path2.join(w, "memory", "daily"), type: "dir" },
|
|
809
|
+
{ path: path2.join(w, "docs", "research"), type: "dir" },
|
|
810
|
+
{ path: path2.join(w, "docs", "todo"), type: "dir" },
|
|
811
|
+
{ path: path2.join(w, "docs", "decisions"), type: "dir" },
|
|
812
|
+
{ path: path2.join(w, "docs", "knowledge"), type: "dir" },
|
|
813
|
+
{ path: path2.join(w, "docs", "sop"), type: "dir" },
|
|
814
|
+
{ path: path2.join(d, "logs"), type: "dir" },
|
|
815
|
+
{ path: path2.join(d, "media", "inbound"), type: "dir" },
|
|
310
816
|
// workspace 文件
|
|
311
817
|
{
|
|
312
|
-
path:
|
|
818
|
+
path: path2.join(w, "SESSION-STATE.md"),
|
|
313
819
|
type: "file",
|
|
314
820
|
content: readTemplate("workspace/SESSION-STATE.md").replace("{{CURRENT_TIME}}", now)
|
|
315
821
|
},
|
|
316
|
-
{ path:
|
|
822
|
+
{ path: path2.join(w, "HEARTBEAT.md"), type: "file", content: readTemplate("workspace/HEARTBEAT.md") },
|
|
317
823
|
{
|
|
318
|
-
path:
|
|
824
|
+
path: path2.join(w, "SOUL.md"),
|
|
319
825
|
type: "file",
|
|
320
826
|
content: readTemplate("workspace/SOUL.md").replace(/\{\{AGENT_NAME\}\}/g, v.agentName)
|
|
321
827
|
},
|
|
322
|
-
{ path:
|
|
828
|
+
{ path: path2.join(w, "AGENTS.md"), type: "file", content: readTemplate("workspace/AGENTS.md") },
|
|
323
829
|
{
|
|
324
|
-
path:
|
|
830
|
+
path: path2.join(w, "prompts", "contacts.md"),
|
|
325
831
|
type: "file",
|
|
326
832
|
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
833
|
},
|
|
328
|
-
{ path:
|
|
329
|
-
{ path:
|
|
330
|
-
{ path:
|
|
834
|
+
{ path: path2.join(w, "prompts", "auto-memory-instructions.md"), type: "file", content: readTemplate("workspace/prompts/auto-memory-instructions.md") },
|
|
835
|
+
{ 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" },
|
|
836
|
+
{ 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
837
|
// package.json — 让 agent 目录成为独立 npm 项目根,防止 npm hoisting
|
|
332
838
|
{
|
|
333
|
-
path:
|
|
839
|
+
path: path2.join(d, "package.json"),
|
|
334
840
|
type: "file",
|
|
335
841
|
content: JSON.stringify({
|
|
336
842
|
name: v.agentName.toLowerCase().replace(/[^a-z0-9-]/g, "-") || "agent",
|
|
@@ -341,7 +847,7 @@ function buildDirTree(v) {
|
|
|
341
847
|
},
|
|
342
848
|
// 启动脚本(根据 OS 生成)
|
|
343
849
|
...process.platform === "win32" ? [{
|
|
344
|
-
path:
|
|
850
|
+
path: path2.join(d, "start.cmd"),
|
|
345
851
|
type: "file",
|
|
346
852
|
content: `@echo off
|
|
347
853
|
rem Engine 7 startup script
|
|
@@ -373,7 +879,7 @@ if %ERRORLEVEL% NEQ 0 (
|
|
|
373
879
|
)
|
|
374
880
|
`
|
|
375
881
|
}] : [{
|
|
376
|
-
path:
|
|
882
|
+
path: path2.join(d, "start.sh"),
|
|
377
883
|
type: "file",
|
|
378
884
|
content: `#!/bin/bash
|
|
379
885
|
# Engine 7 startup script
|
|
@@ -406,13 +912,13 @@ node "$ENGINE7_BIN" --engine-config configs/main7.json
|
|
|
406
912
|
}
|
|
407
913
|
function readTemplate(relativePath) {
|
|
408
914
|
const candidates = [
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
915
|
+
path2.join(__dirname, "..", "templates", relativePath),
|
|
916
|
+
path2.join(__dirname, "..", "..", "templates", relativePath),
|
|
917
|
+
path2.join(process.cwd(), "templates", relativePath)
|
|
412
918
|
];
|
|
413
919
|
for (const p of candidates) {
|
|
414
|
-
if (
|
|
415
|
-
return
|
|
920
|
+
if (fs2.existsSync(p)) {
|
|
921
|
+
return fs2.readFileSync(p, "utf-8");
|
|
416
922
|
}
|
|
417
923
|
}
|
|
418
924
|
throw new Error(`\u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${relativePath}\uFF08\u627E\u4E86: ${candidates.join(", ")}\uFF09`);
|
|
@@ -427,11 +933,11 @@ function dryRun(entries) {
|
|
|
427
933
|
function execute(entries) {
|
|
428
934
|
for (const e of entries) {
|
|
429
935
|
if (e.type === "dir") {
|
|
430
|
-
|
|
936
|
+
fs2.mkdirSync(e.path, { recursive: true });
|
|
431
937
|
console.log(` \u{1F4C1} ${e.path}`);
|
|
432
938
|
} else {
|
|
433
|
-
|
|
434
|
-
|
|
939
|
+
fs2.mkdirSync(path2.dirname(e.path), { recursive: true });
|
|
940
|
+
fs2.writeFileSync(e.path, e.content || "", "utf-8");
|
|
435
941
|
console.log(` \u{1F4C4} ${e.path}`);
|
|
436
942
|
}
|
|
437
943
|
}
|
|
@@ -447,6 +953,43 @@ async function main() {
|
|
|
447
953
|
printHelp();
|
|
448
954
|
process.exit(0);
|
|
449
955
|
}
|
|
956
|
+
if (subcommand === "export") {
|
|
957
|
+
const { doExport: doExport2 } = await Promise.resolve().then(() => (init_cli_travel(), cli_travel_exports));
|
|
958
|
+
let exportStateDir = "";
|
|
959
|
+
let agentName = "";
|
|
960
|
+
let note = "";
|
|
961
|
+
let dryRun2 = false;
|
|
962
|
+
for (let i = 1; i < args.length; i++) {
|
|
963
|
+
if (args[i] === "--state-dir" && args[i + 1]) exportStateDir = args[++i];
|
|
964
|
+
else if (args[i] === "--agent" && args[i + 1]) agentName = args[++i];
|
|
965
|
+
else if (args[i] === "--note" && args[i + 1]) note = args[++i];
|
|
966
|
+
else if (args[i] === "--dry-run") dryRun2 = true;
|
|
967
|
+
}
|
|
968
|
+
if (!exportStateDir) exportStateDir = path2.resolve(process.cwd());
|
|
969
|
+
if (!agentName) agentName = path2.basename(exportStateDir);
|
|
970
|
+
await doExport2({ agentName, stateDir: exportStateDir, note, dryRun: dryRun2 });
|
|
971
|
+
process.exit(0);
|
|
972
|
+
}
|
|
973
|
+
if (subcommand === "import") {
|
|
974
|
+
const { doImport: doImport2 } = await Promise.resolve().then(() => (init_cli_travel(), cli_travel_exports));
|
|
975
|
+
let importStateDir = "";
|
|
976
|
+
let agentName = "";
|
|
977
|
+
let version = "";
|
|
978
|
+
let dryRun2 = false;
|
|
979
|
+
for (let i = 1; i < args.length; i++) {
|
|
980
|
+
if (args[i] === "--state-dir" && args[i + 1]) importStateDir = args[++i];
|
|
981
|
+
else if (args[i] === "--agent" && args[i + 1]) agentName = args[++i];
|
|
982
|
+
else if (args[i] === "--version" && args[i + 1]) version = args[++i];
|
|
983
|
+
else if (args[i] === "--dry-run") dryRun2 = true;
|
|
984
|
+
}
|
|
985
|
+
if (!importStateDir) {
|
|
986
|
+
console.error("\u274C import \u9700\u8981 --state-dir <path>");
|
|
987
|
+
process.exit(1);
|
|
988
|
+
}
|
|
989
|
+
if (!agentName) agentName = path2.basename(importStateDir);
|
|
990
|
+
await doImport2({ agentName, stateDir: importStateDir, version, dryRun: dryRun2 });
|
|
991
|
+
process.exit(0);
|
|
992
|
+
}
|
|
450
993
|
if (subcommand === "start" || subcommand === "restart") {
|
|
451
994
|
const label = subcommand === "restart" ? "\u{1F504} engine7 restart" : "\u{1F680} engine7 start";
|
|
452
995
|
let configPath2 = "";
|
|
@@ -468,18 +1011,18 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
468
1011
|
}
|
|
469
1012
|
}
|
|
470
1013
|
if (!configPath2) {
|
|
471
|
-
const defaultCfg =
|
|
472
|
-
const homeCfg =
|
|
473
|
-
if (
|
|
1014
|
+
const defaultCfg = path2.join("configs", "main7.json");
|
|
1015
|
+
const homeCfg = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7", "configs", "main7.json");
|
|
1016
|
+
if (fs2.existsSync(defaultCfg)) {
|
|
474
1017
|
configPath2 = defaultCfg;
|
|
475
|
-
} else if (
|
|
1018
|
+
} else if (fs2.existsSync(homeCfg)) {
|
|
476
1019
|
configPath2 = homeCfg;
|
|
477
1020
|
console.log(` \u4F7F\u7528\u9ED8\u8BA4 config: ${homeCfg}`);
|
|
478
1021
|
} else {
|
|
479
1022
|
const ptr = readHomePointer();
|
|
480
1023
|
if (ptr?.stateDir) {
|
|
481
|
-
const ptrCfg =
|
|
482
|
-
if (
|
|
1024
|
+
const ptrCfg = path2.join(ptr.stateDir, "configs", "main7.json");
|
|
1025
|
+
if (fs2.existsSync(ptrCfg)) {
|
|
483
1026
|
configPath2 = ptrCfg;
|
|
484
1027
|
console.log(` \u4F7F\u7528 config: ${ptrCfg}`);
|
|
485
1028
|
}
|
|
@@ -492,21 +1035,21 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
492
1035
|
}
|
|
493
1036
|
}
|
|
494
1037
|
}
|
|
495
|
-
const { execSync, spawn } = await import("node:child_process");
|
|
1038
|
+
const { execSync: execSync2, spawn } = await import("node:child_process");
|
|
496
1039
|
let enginePath;
|
|
497
|
-
const localPath =
|
|
498
|
-
if (
|
|
1040
|
+
const localPath = path2.join("node_modules", "engine7", "dist", "main.mjs");
|
|
1041
|
+
if (fs2.existsSync(localPath)) {
|
|
499
1042
|
enginePath = localPath;
|
|
500
1043
|
} else {
|
|
501
|
-
const cliPath =
|
|
502
|
-
const distDir =
|
|
503
|
-
const candidate =
|
|
504
|
-
if (
|
|
1044
|
+
const cliPath = path2.resolve(process.argv[1]);
|
|
1045
|
+
const distDir = path2.dirname(cliPath);
|
|
1046
|
+
const candidate = path2.join(distDir, "main.mjs");
|
|
1047
|
+
if (fs2.existsSync(candidate)) {
|
|
505
1048
|
enginePath = candidate;
|
|
506
1049
|
} else {
|
|
507
1050
|
try {
|
|
508
|
-
const globalRoot =
|
|
509
|
-
enginePath =
|
|
1051
|
+
const globalRoot = execSync2("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
1052
|
+
enginePath = path2.join(globalRoot, "engine7", "dist", "main.mjs");
|
|
510
1053
|
} catch {
|
|
511
1054
|
console.error("\u274C \u627E\u4E0D\u5230 engine7 main.mjs");
|
|
512
1055
|
process.exit(1);
|
|
@@ -516,17 +1059,17 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
516
1059
|
console.log(label);
|
|
517
1060
|
console.log(` config: ${configPath2}`);
|
|
518
1061
|
console.log(` engine: ${enginePath}`);
|
|
519
|
-
const configName =
|
|
1062
|
+
const configName = path2.basename(configPath2);
|
|
520
1063
|
const myPid = process.pid;
|
|
521
1064
|
try {
|
|
522
1065
|
if (process.platform === "win32") {
|
|
523
|
-
|
|
1066
|
+
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
1067
|
} else {
|
|
525
|
-
const pids =
|
|
1068
|
+
const pids = execSync2(`pgrep -f "dist/main.mjs.*${configName}" 2>/dev/null || true`, { encoding: "utf-8" }).trim();
|
|
526
1069
|
const otherPids = pids.split("\n").filter((p) => p && p.trim() !== String(myPid)).join("\n").trim();
|
|
527
1070
|
if (otherPids) {
|
|
528
1071
|
console.log(`[start] Killing existing engine (PID: ${otherPids})...`);
|
|
529
|
-
|
|
1072
|
+
execSync2(`echo "${otherPids}" | xargs kill -9 2>/dev/null || true`);
|
|
530
1073
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
531
1074
|
}
|
|
532
1075
|
}
|
|
@@ -564,18 +1107,18 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
564
1107
|
else if (args[i] === "--cwd" && args[i + 1]) cwd = args[++i];
|
|
565
1108
|
}
|
|
566
1109
|
if (!configPath2) {
|
|
567
|
-
const defaultCfg =
|
|
568
|
-
const homeCfg =
|
|
569
|
-
if (
|
|
1110
|
+
const defaultCfg = path2.join(cwd, "configs", "main7.json");
|
|
1111
|
+
const homeCfg = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7", "configs", "main7.json");
|
|
1112
|
+
if (fs2.existsSync(defaultCfg)) {
|
|
570
1113
|
configPath2 = defaultCfg;
|
|
571
|
-
} else if (
|
|
1114
|
+
} else if (fs2.existsSync(homeCfg)) {
|
|
572
1115
|
configPath2 = homeCfg;
|
|
573
|
-
cwd =
|
|
1116
|
+
cwd = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
574
1117
|
} else {
|
|
575
1118
|
const ptr = readHomePointer();
|
|
576
1119
|
if (ptr?.stateDir) {
|
|
577
|
-
const ptrCfg =
|
|
578
|
-
if (
|
|
1120
|
+
const ptrCfg = path2.join(ptr.stateDir, "configs", "main7.json");
|
|
1121
|
+
if (fs2.existsSync(ptrCfg)) {
|
|
579
1122
|
configPath2 = ptrCfg;
|
|
580
1123
|
cwd = ptr.stateDir;
|
|
581
1124
|
}
|
|
@@ -588,22 +1131,22 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
588
1131
|
}
|
|
589
1132
|
}
|
|
590
1133
|
}
|
|
591
|
-
const { execSync } = await import("node:child_process");
|
|
1134
|
+
const { execSync: execSync2 } = await import("node:child_process");
|
|
592
1135
|
const engine7Bin = process.argv[1];
|
|
593
1136
|
if (action === "install") {
|
|
594
1137
|
if (process.platform === "win32") {
|
|
595
1138
|
const taskName = "Engine7";
|
|
596
1139
|
const nodeExe = process.execPath;
|
|
597
|
-
const cliMjs =
|
|
598
|
-
const absConfig =
|
|
599
|
-
const wrapperPath =
|
|
1140
|
+
const cliMjs = path2.resolve(engine7Bin);
|
|
1141
|
+
const absConfig = path2.resolve(configPath2);
|
|
1142
|
+
const wrapperPath = path2.join(cwd, "engine7-start.cmd");
|
|
600
1143
|
const wrapperContent = `@echo off\r
|
|
601
1144
|
cd /d "${cwd}"\r
|
|
602
1145
|
"${nodeExe}" "${cliMjs}" start --config "${absConfig}"\r
|
|
603
1146
|
`;
|
|
604
|
-
|
|
1147
|
+
fs2.writeFileSync(wrapperPath, wrapperContent);
|
|
605
1148
|
try {
|
|
606
|
-
|
|
1149
|
+
execSync2(`schtasks /create /tn "${taskName}" /tr "${wrapperPath}" /sc onlogon /rl highest /f`, { stdio: "inherit", shell: true });
|
|
607
1150
|
console.log(`\u2705 Windows \u8BA1\u5212\u4EFB\u52A1 "${taskName}" \u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
608
1151
|
console.log(` wrapper: ${wrapperPath}`);
|
|
609
1152
|
} catch {
|
|
@@ -612,11 +1155,11 @@ cd /d "${cwd}"\r
|
|
|
612
1155
|
}
|
|
613
1156
|
} else if (process.platform === "darwin") {
|
|
614
1157
|
const label = "com.engine7.agent";
|
|
615
|
-
const plistDir =
|
|
616
|
-
|
|
617
|
-
const plistPath =
|
|
618
|
-
const localCli =
|
|
619
|
-
const cliMjs =
|
|
1158
|
+
const plistDir = path2.join(process.env.HOME, "Library", "LaunchAgents");
|
|
1159
|
+
fs2.mkdirSync(plistDir, { recursive: true });
|
|
1160
|
+
const plistPath = path2.join(plistDir, `${label}.plist`);
|
|
1161
|
+
const localCli = path2.join("node_modules", "engine7", "dist", "cli.mjs");
|
|
1162
|
+
const cliMjs = fs2.existsSync(localCli) ? path2.resolve(localCli) : path2.join(path2.dirname(path2.dirname(engine7Bin)), "lib", "node_modules", "engine7", "dist", "cli.mjs");
|
|
620
1163
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
621
1164
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
622
1165
|
<plist version="1.0">
|
|
@@ -628,16 +1171,16 @@ cd /d "${cwd}"\r
|
|
|
628
1171
|
<string>${cliMjs}</string>
|
|
629
1172
|
<string>start</string>
|
|
630
1173
|
<string>--config</string>
|
|
631
|
-
<string>${
|
|
1174
|
+
<string>${path2.resolve(configPath2)}</string>
|
|
632
1175
|
</array>
|
|
633
1176
|
<key>WorkingDirectory</key><string>${cwd}</string>
|
|
634
1177
|
<key>RunAtLoad</key><true/>
|
|
635
1178
|
<key>KeepAlive</key><true/>
|
|
636
1179
|
</dict>
|
|
637
1180
|
</plist>`;
|
|
638
|
-
|
|
1181
|
+
fs2.writeFileSync(plistPath, plist);
|
|
639
1182
|
try {
|
|
640
|
-
|
|
1183
|
+
execSync2(`launchctl load "${plistPath}"`, { stdio: "inherit" });
|
|
641
1184
|
console.log(`\u2705 Mac launchd \u670D\u52A1\u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
642
1185
|
console.log(` ${plistPath}`);
|
|
643
1186
|
} catch {
|
|
@@ -646,9 +1189,9 @@ cd /d "${cwd}"\r
|
|
|
646
1189
|
}
|
|
647
1190
|
} else {
|
|
648
1191
|
const svcName = "engine7";
|
|
649
|
-
const svcDir =
|
|
650
|
-
|
|
651
|
-
const svcPath =
|
|
1192
|
+
const svcDir = path2.join(process.env.HOME, ".config", "systemd", "user");
|
|
1193
|
+
fs2.mkdirSync(svcDir, { recursive: true });
|
|
1194
|
+
const svcPath = path2.join(svcDir, `${svcName}.service`);
|
|
652
1195
|
const svc = `[Unit]
|
|
653
1196
|
Description=Engine 7 Agent
|
|
654
1197
|
After=network.target
|
|
@@ -656,16 +1199,16 @@ After=network.target
|
|
|
656
1199
|
[Service]
|
|
657
1200
|
Type=simple
|
|
658
1201
|
WorkingDirectory=${cwd}
|
|
659
|
-
ExecStart=${process.execPath} ${engine7Bin} start --config ${
|
|
1202
|
+
ExecStart=${process.execPath} ${engine7Bin} start --config ${path2.resolve(configPath2)}
|
|
660
1203
|
Restart=on-failure
|
|
661
1204
|
RestartSec=10
|
|
662
1205
|
|
|
663
1206
|
[Install]
|
|
664
1207
|
WantedBy=default.target`;
|
|
665
|
-
|
|
1208
|
+
fs2.writeFileSync(svcPath, svc);
|
|
666
1209
|
try {
|
|
667
|
-
|
|
668
|
-
|
|
1210
|
+
execSync2(`systemctl --user daemon-reload`, { stdio: "inherit" });
|
|
1211
|
+
execSync2(`systemctl --user enable ${svcName}`, { stdio: "inherit" });
|
|
669
1212
|
console.log(`\u2705 systemd \u7528\u6237\u670D\u52A1\u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
670
1213
|
console.log(` ${svcPath}`);
|
|
671
1214
|
} catch {
|
|
@@ -676,35 +1219,35 @@ WantedBy=default.target`;
|
|
|
676
1219
|
} else if (action === "uninstall") {
|
|
677
1220
|
if (process.platform === "win32") {
|
|
678
1221
|
try {
|
|
679
|
-
|
|
1222
|
+
execSync2(`schtasks /delete /tn "Engine7" /f`, { stdio: "inherit", shell: true });
|
|
680
1223
|
console.log(`\u2705 Windows \u8BA1\u5212\u4EFB\u52A1\u5DF2\u5220\u9664`);
|
|
681
1224
|
} catch {
|
|
682
1225
|
console.error("\u274C \u5220\u9664\u5931\u8D25\uFF0C\u4EFB\u52A1\u53EF\u80FD\u4E0D\u5B58\u5728");
|
|
683
1226
|
process.exit(1);
|
|
684
1227
|
}
|
|
685
1228
|
} else if (process.platform === "darwin") {
|
|
686
|
-
const plistPath =
|
|
1229
|
+
const plistPath = path2.join(process.env.HOME, "Library", "LaunchAgents", "com.engine7.agent.plist");
|
|
687
1230
|
try {
|
|
688
|
-
|
|
1231
|
+
execSync2(`launchctl unload "${plistPath}"`, { stdio: "inherit" });
|
|
689
1232
|
} catch {
|
|
690
1233
|
}
|
|
691
1234
|
try {
|
|
692
|
-
|
|
1235
|
+
fs2.unlinkSync(plistPath);
|
|
693
1236
|
} catch {
|
|
694
1237
|
}
|
|
695
1238
|
console.log(`\u2705 Mac launchd \u670D\u52A1\u5DF2\u5220\u9664`);
|
|
696
1239
|
} else {
|
|
697
1240
|
try {
|
|
698
|
-
|
|
1241
|
+
execSync2(`systemctl --user disable engine7`, { stdio: "inherit" });
|
|
699
1242
|
} catch {
|
|
700
1243
|
}
|
|
701
|
-
const svcPath =
|
|
1244
|
+
const svcPath = path2.join(process.env.HOME, ".config", "systemd", "user", "engine7.service");
|
|
702
1245
|
try {
|
|
703
|
-
|
|
1246
|
+
fs2.unlinkSync(svcPath);
|
|
704
1247
|
} catch {
|
|
705
1248
|
}
|
|
706
1249
|
try {
|
|
707
|
-
|
|
1250
|
+
execSync2(`systemctl --user daemon-reload`, { stdio: "inherit" });
|
|
708
1251
|
} catch {
|
|
709
1252
|
}
|
|
710
1253
|
console.log(`\u2705 systemd \u7528\u6237\u670D\u52A1\u5DF2\u5220\u9664`);
|
|
@@ -712,20 +1255,20 @@ WantedBy=default.target`;
|
|
|
712
1255
|
} else if (action === "status") {
|
|
713
1256
|
if (process.platform === "win32") {
|
|
714
1257
|
try {
|
|
715
|
-
|
|
1258
|
+
execSync2(`schtasks /query /tn "Engine7"`, { stdio: "inherit", shell: true });
|
|
716
1259
|
} catch {
|
|
717
1260
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
718
1261
|
}
|
|
719
1262
|
} else if (process.platform === "darwin") {
|
|
720
|
-
const plistPath =
|
|
721
|
-
if (
|
|
1263
|
+
const plistPath = path2.join(process.env.HOME, "Library", "LaunchAgents", "com.engine7.agent.plist");
|
|
1264
|
+
if (fs2.existsSync(plistPath)) {
|
|
722
1265
|
console.log("\u2705 \u5DF2\u5B89\u88C5\uFF08launchd\uFF09");
|
|
723
1266
|
} else {
|
|
724
1267
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
725
1268
|
}
|
|
726
1269
|
} else {
|
|
727
1270
|
try {
|
|
728
|
-
|
|
1271
|
+
execSync2(`systemctl --user is-enabled engine7`, { stdio: "inherit" });
|
|
729
1272
|
} catch {
|
|
730
1273
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
731
1274
|
}
|
|
@@ -741,8 +1284,8 @@ WantedBy=default.target`;
|
|
|
741
1284
|
console.log(`
|
|
742
1285
|
\u{1F680} engine7 init`);
|
|
743
1286
|
console.log(` state-dir: ${opts.stateDir}`);
|
|
744
|
-
if (
|
|
745
|
-
const files =
|
|
1287
|
+
if (fs2.existsSync(opts.stateDir) && !opts.dryRun) {
|
|
1288
|
+
const files = fs2.readdirSync(opts.stateDir);
|
|
746
1289
|
if (files.length > 0) {
|
|
747
1290
|
console.error(`
|
|
748
1291
|
\u274C \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A: ${opts.stateDir}`);
|
|
@@ -760,7 +1303,7 @@ WantedBy=default.target`;
|
|
|
760
1303
|
rl.close();
|
|
761
1304
|
}
|
|
762
1305
|
const config = generateConfig(values);
|
|
763
|
-
const configPath =
|
|
1306
|
+
const configPath = path2.join(opts.stateDir, "configs", "main7.json");
|
|
764
1307
|
const tree = buildDirTree(values);
|
|
765
1308
|
tree.push({
|
|
766
1309
|
path: configPath,
|
|
@@ -771,9 +1314,9 @@ WantedBy=default.target`;
|
|
|
771
1314
|
dryRun(tree);
|
|
772
1315
|
const skillsSrc = findTemplateDir("skills");
|
|
773
1316
|
if (skillsSrc) {
|
|
774
|
-
const skills =
|
|
1317
|
+
const skills = fs2.readdirSync(skillsSrc);
|
|
775
1318
|
for (const s of skills) {
|
|
776
|
-
console.log(` \u{1F4C1} ${
|
|
1319
|
+
console.log(` \u{1F4C1} ${path2.join(opts.stateDir, "workspace", "skills", s)}`);
|
|
777
1320
|
}
|
|
778
1321
|
}
|
|
779
1322
|
} else {
|
|
@@ -781,55 +1324,55 @@ WantedBy=default.target`;
|
|
|
781
1324
|
execute(tree);
|
|
782
1325
|
const skillsSrc = findTemplateDir("skills");
|
|
783
1326
|
if (skillsSrc) {
|
|
784
|
-
const skillsDest =
|
|
785
|
-
|
|
786
|
-
const skills =
|
|
1327
|
+
const skillsDest = path2.join(opts.stateDir, "workspace", "skills");
|
|
1328
|
+
fs2.mkdirSync(skillsDest, { recursive: true });
|
|
1329
|
+
const skills = fs2.readdirSync(skillsSrc);
|
|
787
1330
|
for (const s of skills) {
|
|
788
|
-
copyDirRecursive(
|
|
1331
|
+
copyDirRecursive(path2.join(skillsSrc, s), path2.join(skillsDest, s));
|
|
789
1332
|
console.log(` \u{1F4E6} skill: ${s}`);
|
|
790
1333
|
}
|
|
791
1334
|
}
|
|
792
1335
|
console.log(`
|
|
793
1336
|
\u2705 \u521D\u59CB\u5316\u5B8C\u6210\uFF01
|
|
794
1337
|
`);
|
|
795
|
-
const homePointer =
|
|
1338
|
+
const homePointer = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
796
1339
|
const pointerData = { stateDir: opts.stateDir };
|
|
797
|
-
|
|
1340
|
+
fs2.writeFileSync(homePointer, JSON.stringify(pointerData, null, 2) + "\n");
|
|
798
1341
|
console.log(` \u{1F4C4} ${homePointer}\uFF08\u4ECE\u4EFB\u610F\u76EE\u5F55\u90FD\u80FD\u627E\u5230\u6B64 agent\uFF09`);
|
|
799
1342
|
console.log(`\u4E0B\u4E00\u6B65\uFF1A`);
|
|
800
1343
|
console.log(` 1. \u68C0\u67E5\u5E76\u4FEE\u6539 ${opts.stateDir}/configs/main7.json`);
|
|
801
1344
|
console.log(` 2. \u542F\u52A8 Engine: engine7 start\uFF08\u5728\u4EFB\u610F\u76EE\u5F55\u90FD\u884C\uFF09`);
|
|
802
1345
|
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: ${
|
|
1346
|
+
console.log(` 4. \u67E5\u770B workspace: ${path2.join(opts.stateDir, "workspace")}`);
|
|
804
1347
|
}
|
|
805
1348
|
}
|
|
806
1349
|
function copyDirRecursive(src, dest) {
|
|
807
|
-
|
|
808
|
-
for (const entry of
|
|
809
|
-
const srcPath =
|
|
810
|
-
const destPath =
|
|
1350
|
+
fs2.mkdirSync(dest, { recursive: true });
|
|
1351
|
+
for (const entry of fs2.readdirSync(src, { withFileTypes: true })) {
|
|
1352
|
+
const srcPath = path2.join(src, entry.name);
|
|
1353
|
+
const destPath = path2.join(dest, entry.name);
|
|
811
1354
|
if (entry.isDirectory()) {
|
|
812
1355
|
copyDirRecursive(srcPath, destPath);
|
|
813
1356
|
} else {
|
|
814
|
-
|
|
1357
|
+
fs2.copyFileSync(srcPath, destPath);
|
|
815
1358
|
}
|
|
816
1359
|
}
|
|
817
1360
|
}
|
|
818
1361
|
function findTemplateDir(name) {
|
|
819
1362
|
const candidates = [
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1363
|
+
path2.join(__dirname, "..", "templates", name),
|
|
1364
|
+
path2.join(__dirname, "..", "..", "templates", name),
|
|
1365
|
+
path2.join(process.cwd(), "templates", name)
|
|
823
1366
|
];
|
|
824
1367
|
for (const p of candidates) {
|
|
825
|
-
if (
|
|
1368
|
+
if (fs2.existsSync(p)) return p;
|
|
826
1369
|
}
|
|
827
1370
|
return null;
|
|
828
1371
|
}
|
|
829
1372
|
function readHomePointer() {
|
|
830
|
-
const ptrPath =
|
|
1373
|
+
const ptrPath = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
831
1374
|
try {
|
|
832
|
-
return JSON.parse(
|
|
1375
|
+
return JSON.parse(fs2.readFileSync(ptrPath, "utf-8"));
|
|
833
1376
|
} catch {
|
|
834
1377
|
return null;
|
|
835
1378
|
}
|