engine7 7.1.6 → 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 +658 -170
- 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,7 +947,45 @@ async function main() {
|
|
|
447
947
|
printHelp();
|
|
448
948
|
process.exit(0);
|
|
449
949
|
}
|
|
450
|
-
if (subcommand === "
|
|
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
|
+
}
|
|
987
|
+
if (subcommand === "start" || subcommand === "restart") {
|
|
988
|
+
const label = subcommand === "restart" ? "\u{1F504} engine7 restart" : "\u{1F680} engine7 start";
|
|
451
989
|
let configPath2 = "";
|
|
452
990
|
for (let i = 1; i < args.length; i++) {
|
|
453
991
|
if (args[i] === "--config" && args[i + 1]) {
|
|
@@ -467,18 +1005,18 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
467
1005
|
}
|
|
468
1006
|
}
|
|
469
1007
|
if (!configPath2) {
|
|
470
|
-
const defaultCfg =
|
|
471
|
-
const homeCfg =
|
|
472
|
-
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)) {
|
|
473
1011
|
configPath2 = defaultCfg;
|
|
474
|
-
} else if (
|
|
1012
|
+
} else if (fs2.existsSync(homeCfg)) {
|
|
475
1013
|
configPath2 = homeCfg;
|
|
476
1014
|
console.log(` \u4F7F\u7528\u9ED8\u8BA4 config: ${homeCfg}`);
|
|
477
1015
|
} else {
|
|
478
1016
|
const ptr = readHomePointer();
|
|
479
1017
|
if (ptr?.stateDir) {
|
|
480
|
-
const ptrCfg =
|
|
481
|
-
if (
|
|
1018
|
+
const ptrCfg = path2.join(ptr.stateDir, "configs", "main7.json");
|
|
1019
|
+
if (fs2.existsSync(ptrCfg)) {
|
|
482
1020
|
configPath2 = ptrCfg;
|
|
483
1021
|
console.log(` \u4F7F\u7528 config: ${ptrCfg}`);
|
|
484
1022
|
}
|
|
@@ -491,41 +1029,41 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
491
1029
|
}
|
|
492
1030
|
}
|
|
493
1031
|
}
|
|
494
|
-
const { execSync, spawn } = await import("node:child_process");
|
|
1032
|
+
const { execSync: execSync2, spawn } = await import("node:child_process");
|
|
495
1033
|
let enginePath;
|
|
496
|
-
const localPath =
|
|
497
|
-
if (
|
|
1034
|
+
const localPath = path2.join("node_modules", "engine7", "dist", "main.mjs");
|
|
1035
|
+
if (fs2.existsSync(localPath)) {
|
|
498
1036
|
enginePath = localPath;
|
|
499
1037
|
} else {
|
|
500
|
-
const cliPath =
|
|
501
|
-
const distDir =
|
|
502
|
-
const candidate =
|
|
503
|
-
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)) {
|
|
504
1042
|
enginePath = candidate;
|
|
505
1043
|
} else {
|
|
506
1044
|
try {
|
|
507
|
-
const globalRoot =
|
|
508
|
-
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");
|
|
509
1047
|
} catch {
|
|
510
1048
|
console.error("\u274C \u627E\u4E0D\u5230 engine7 main.mjs");
|
|
511
1049
|
process.exit(1);
|
|
512
1050
|
}
|
|
513
1051
|
}
|
|
514
1052
|
}
|
|
515
|
-
console.log(
|
|
1053
|
+
console.log(label);
|
|
516
1054
|
console.log(` config: ${configPath2}`);
|
|
517
1055
|
console.log(` engine: ${enginePath}`);
|
|
518
|
-
const configName =
|
|
1056
|
+
const configName = path2.basename(configPath2);
|
|
519
1057
|
const myPid = process.pid;
|
|
520
1058
|
try {
|
|
521
1059
|
if (process.platform === "win32") {
|
|
522
|
-
|
|
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" });
|
|
523
1061
|
} else {
|
|
524
|
-
const pids =
|
|
1062
|
+
const pids = execSync2(`pgrep -f "dist/main.mjs.*${configName}" 2>/dev/null || true`, { encoding: "utf-8" }).trim();
|
|
525
1063
|
const otherPids = pids.split("\n").filter((p) => p && p.trim() !== String(myPid)).join("\n").trim();
|
|
526
1064
|
if (otherPids) {
|
|
527
1065
|
console.log(`[start] Killing existing engine (PID: ${otherPids})...`);
|
|
528
|
-
|
|
1066
|
+
execSync2(`echo "${otherPids}" | xargs kill -9 2>/dev/null || true`);
|
|
529
1067
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
530
1068
|
}
|
|
531
1069
|
}
|
|
@@ -539,56 +1077,6 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
539
1077
|
child.on("exit", (code) => process.exit(code ?? 1));
|
|
540
1078
|
return;
|
|
541
1079
|
}
|
|
542
|
-
if (subcommand === "restart") {
|
|
543
|
-
const { execSync } = await import("node:child_process");
|
|
544
|
-
const myPid = process.pid;
|
|
545
|
-
console.log("\u{1F504} engine7 restart");
|
|
546
|
-
try {
|
|
547
|
-
if (process.platform === "win32") {
|
|
548
|
-
execSync(`powershell -Command "Get-WmiObject Win32_Process | Where-Object { $_.Name -eq 'node.exe' -and $_.CommandLine -like '*dist/main.mjs*' -and $_.ProcessId -ne ${myPid} } | ForEach-Object { Write-Host '[restart] Killing PID' $_.ProcessId; Stop-Process -Id $_.ProcessId -Force }"`, { stdio: "inherit" });
|
|
549
|
-
} else {
|
|
550
|
-
const pids = execSync(`pgrep -f "dist/main.mjs" 2>/dev/null || true`, { encoding: "utf-8" }).trim();
|
|
551
|
-
const otherPids = pids.split("\n").filter((p) => p && p.trim() !== String(myPid)).join("\n").trim();
|
|
552
|
-
if (otherPids) {
|
|
553
|
-
console.log(`[restart] Killing existing engine (PID: ${otherPids})...`);
|
|
554
|
-
execSync(`echo "${otherPids}" | xargs kill -9 2>/dev/null || true`);
|
|
555
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
} catch {
|
|
559
|
-
}
|
|
560
|
-
let configPath2 = "";
|
|
561
|
-
for (let i = 1; i < args.length; i++) {
|
|
562
|
-
if (args[i] === "--config" && args[i + 1]) configPath2 = args[++i];
|
|
563
|
-
}
|
|
564
|
-
if (!configPath2) {
|
|
565
|
-
const defaultCfg = path.join("configs", "main7.json");
|
|
566
|
-
const homeCfg = path.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7", "configs", "main7.json");
|
|
567
|
-
if (fs.existsSync(defaultCfg)) configPath2 = defaultCfg;
|
|
568
|
-
else if (fs.existsSync(homeCfg)) configPath2 = homeCfg;
|
|
569
|
-
else {
|
|
570
|
-
const ptr = readHomePointer();
|
|
571
|
-
if (ptr?.stateDir) {
|
|
572
|
-
const ptrCfg = path.join(ptr.stateDir, "configs", "main7.json");
|
|
573
|
-
if (fs.existsSync(ptrCfg)) configPath2 = ptrCfg;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
if (!configPath2) {
|
|
578
|
-
console.error("\u274C \u627E\u4E0D\u5230 config");
|
|
579
|
-
process.exit(1);
|
|
580
|
-
}
|
|
581
|
-
const cliPath = path.resolve(process.argv[1]);
|
|
582
|
-
const distDir = path.dirname(cliPath);
|
|
583
|
-
const enginePath = fs.existsSync(path.join(distDir, "main.mjs")) ? path.join(distDir, "main.mjs") : path.join(path.dirname(path.dirname(cliPath)), "lib", "node_modules", "engine7", "dist", "main.mjs");
|
|
584
|
-
console.log(` config: ${configPath2}`);
|
|
585
|
-
console.log(` engine: ${enginePath}`);
|
|
586
|
-
console.log("");
|
|
587
|
-
const { spawn } = await import("node:child_process");
|
|
588
|
-
const child = spawn(process.execPath, [enginePath, "--engine-config", configPath2], { stdio: "inherit", shell: false });
|
|
589
|
-
child.on("exit", (code) => process.exit(code ?? 1));
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
1080
|
if (subcommand === "service") {
|
|
593
1081
|
const action = args[1];
|
|
594
1082
|
if (!action || action === "--help" || action === "-h") {
|
|
@@ -613,18 +1101,18 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
613
1101
|
else if (args[i] === "--cwd" && args[i + 1]) cwd = args[++i];
|
|
614
1102
|
}
|
|
615
1103
|
if (!configPath2) {
|
|
616
|
-
const defaultCfg =
|
|
617
|
-
const homeCfg =
|
|
618
|
-
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)) {
|
|
619
1107
|
configPath2 = defaultCfg;
|
|
620
|
-
} else if (
|
|
1108
|
+
} else if (fs2.existsSync(homeCfg)) {
|
|
621
1109
|
configPath2 = homeCfg;
|
|
622
|
-
cwd =
|
|
1110
|
+
cwd = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
623
1111
|
} else {
|
|
624
1112
|
const ptr = readHomePointer();
|
|
625
1113
|
if (ptr?.stateDir) {
|
|
626
|
-
const ptrCfg =
|
|
627
|
-
if (
|
|
1114
|
+
const ptrCfg = path2.join(ptr.stateDir, "configs", "main7.json");
|
|
1115
|
+
if (fs2.existsSync(ptrCfg)) {
|
|
628
1116
|
configPath2 = ptrCfg;
|
|
629
1117
|
cwd = ptr.stateDir;
|
|
630
1118
|
}
|
|
@@ -637,22 +1125,22 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
637
1125
|
}
|
|
638
1126
|
}
|
|
639
1127
|
}
|
|
640
|
-
const { execSync } = await import("node:child_process");
|
|
1128
|
+
const { execSync: execSync2 } = await import("node:child_process");
|
|
641
1129
|
const engine7Bin = process.argv[1];
|
|
642
1130
|
if (action === "install") {
|
|
643
1131
|
if (process.platform === "win32") {
|
|
644
1132
|
const taskName = "Engine7";
|
|
645
1133
|
const nodeExe = process.execPath;
|
|
646
|
-
const cliMjs =
|
|
647
|
-
const absConfig =
|
|
648
|
-
const wrapperPath =
|
|
1134
|
+
const cliMjs = path2.resolve(engine7Bin);
|
|
1135
|
+
const absConfig = path2.resolve(configPath2);
|
|
1136
|
+
const wrapperPath = path2.join(cwd, "engine7-start.cmd");
|
|
649
1137
|
const wrapperContent = `@echo off\r
|
|
650
1138
|
cd /d "${cwd}"\r
|
|
651
1139
|
"${nodeExe}" "${cliMjs}" start --config "${absConfig}"\r
|
|
652
1140
|
`;
|
|
653
|
-
|
|
1141
|
+
fs2.writeFileSync(wrapperPath, wrapperContent);
|
|
654
1142
|
try {
|
|
655
|
-
|
|
1143
|
+
execSync2(`schtasks /create /tn "${taskName}" /tr "${wrapperPath}" /sc onlogon /rl highest /f`, { stdio: "inherit", shell: true });
|
|
656
1144
|
console.log(`\u2705 Windows \u8BA1\u5212\u4EFB\u52A1 "${taskName}" \u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
657
1145
|
console.log(` wrapper: ${wrapperPath}`);
|
|
658
1146
|
} catch {
|
|
@@ -661,11 +1149,11 @@ cd /d "${cwd}"\r
|
|
|
661
1149
|
}
|
|
662
1150
|
} else if (process.platform === "darwin") {
|
|
663
1151
|
const label = "com.engine7.agent";
|
|
664
|
-
const plistDir =
|
|
665
|
-
|
|
666
|
-
const plistPath =
|
|
667
|
-
const localCli =
|
|
668
|
-
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");
|
|
669
1157
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
670
1158
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
671
1159
|
<plist version="1.0">
|
|
@@ -677,16 +1165,16 @@ cd /d "${cwd}"\r
|
|
|
677
1165
|
<string>${cliMjs}</string>
|
|
678
1166
|
<string>start</string>
|
|
679
1167
|
<string>--config</string>
|
|
680
|
-
<string>${
|
|
1168
|
+
<string>${path2.resolve(configPath2)}</string>
|
|
681
1169
|
</array>
|
|
682
1170
|
<key>WorkingDirectory</key><string>${cwd}</string>
|
|
683
1171
|
<key>RunAtLoad</key><true/>
|
|
684
1172
|
<key>KeepAlive</key><true/>
|
|
685
1173
|
</dict>
|
|
686
1174
|
</plist>`;
|
|
687
|
-
|
|
1175
|
+
fs2.writeFileSync(plistPath, plist);
|
|
688
1176
|
try {
|
|
689
|
-
|
|
1177
|
+
execSync2(`launchctl load "${plistPath}"`, { stdio: "inherit" });
|
|
690
1178
|
console.log(`\u2705 Mac launchd \u670D\u52A1\u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
691
1179
|
console.log(` ${plistPath}`);
|
|
692
1180
|
} catch {
|
|
@@ -695,9 +1183,9 @@ cd /d "${cwd}"\r
|
|
|
695
1183
|
}
|
|
696
1184
|
} else {
|
|
697
1185
|
const svcName = "engine7";
|
|
698
|
-
const svcDir =
|
|
699
|
-
|
|
700
|
-
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`);
|
|
701
1189
|
const svc = `[Unit]
|
|
702
1190
|
Description=Engine 7 Agent
|
|
703
1191
|
After=network.target
|
|
@@ -705,16 +1193,16 @@ After=network.target
|
|
|
705
1193
|
[Service]
|
|
706
1194
|
Type=simple
|
|
707
1195
|
WorkingDirectory=${cwd}
|
|
708
|
-
ExecStart=${process.execPath} ${engine7Bin} start --config ${
|
|
1196
|
+
ExecStart=${process.execPath} ${engine7Bin} start --config ${path2.resolve(configPath2)}
|
|
709
1197
|
Restart=on-failure
|
|
710
1198
|
RestartSec=10
|
|
711
1199
|
|
|
712
1200
|
[Install]
|
|
713
1201
|
WantedBy=default.target`;
|
|
714
|
-
|
|
1202
|
+
fs2.writeFileSync(svcPath, svc);
|
|
715
1203
|
try {
|
|
716
|
-
|
|
717
|
-
|
|
1204
|
+
execSync2(`systemctl --user daemon-reload`, { stdio: "inherit" });
|
|
1205
|
+
execSync2(`systemctl --user enable ${svcName}`, { stdio: "inherit" });
|
|
718
1206
|
console.log(`\u2705 systemd \u7528\u6237\u670D\u52A1\u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
719
1207
|
console.log(` ${svcPath}`);
|
|
720
1208
|
} catch {
|
|
@@ -725,35 +1213,35 @@ WantedBy=default.target`;
|
|
|
725
1213
|
} else if (action === "uninstall") {
|
|
726
1214
|
if (process.platform === "win32") {
|
|
727
1215
|
try {
|
|
728
|
-
|
|
1216
|
+
execSync2(`schtasks /delete /tn "Engine7" /f`, { stdio: "inherit", shell: true });
|
|
729
1217
|
console.log(`\u2705 Windows \u8BA1\u5212\u4EFB\u52A1\u5DF2\u5220\u9664`);
|
|
730
1218
|
} catch {
|
|
731
1219
|
console.error("\u274C \u5220\u9664\u5931\u8D25\uFF0C\u4EFB\u52A1\u53EF\u80FD\u4E0D\u5B58\u5728");
|
|
732
1220
|
process.exit(1);
|
|
733
1221
|
}
|
|
734
1222
|
} else if (process.platform === "darwin") {
|
|
735
|
-
const plistPath =
|
|
1223
|
+
const plistPath = path2.join(process.env.HOME, "Library", "LaunchAgents", "com.engine7.agent.plist");
|
|
736
1224
|
try {
|
|
737
|
-
|
|
1225
|
+
execSync2(`launchctl unload "${plistPath}"`, { stdio: "inherit" });
|
|
738
1226
|
} catch {
|
|
739
1227
|
}
|
|
740
1228
|
try {
|
|
741
|
-
|
|
1229
|
+
fs2.unlinkSync(plistPath);
|
|
742
1230
|
} catch {
|
|
743
1231
|
}
|
|
744
1232
|
console.log(`\u2705 Mac launchd \u670D\u52A1\u5DF2\u5220\u9664`);
|
|
745
1233
|
} else {
|
|
746
1234
|
try {
|
|
747
|
-
|
|
1235
|
+
execSync2(`systemctl --user disable engine7`, { stdio: "inherit" });
|
|
748
1236
|
} catch {
|
|
749
1237
|
}
|
|
750
|
-
const svcPath =
|
|
1238
|
+
const svcPath = path2.join(process.env.HOME, ".config", "systemd", "user", "engine7.service");
|
|
751
1239
|
try {
|
|
752
|
-
|
|
1240
|
+
fs2.unlinkSync(svcPath);
|
|
753
1241
|
} catch {
|
|
754
1242
|
}
|
|
755
1243
|
try {
|
|
756
|
-
|
|
1244
|
+
execSync2(`systemctl --user daemon-reload`, { stdio: "inherit" });
|
|
757
1245
|
} catch {
|
|
758
1246
|
}
|
|
759
1247
|
console.log(`\u2705 systemd \u7528\u6237\u670D\u52A1\u5DF2\u5220\u9664`);
|
|
@@ -761,20 +1249,20 @@ WantedBy=default.target`;
|
|
|
761
1249
|
} else if (action === "status") {
|
|
762
1250
|
if (process.platform === "win32") {
|
|
763
1251
|
try {
|
|
764
|
-
|
|
1252
|
+
execSync2(`schtasks /query /tn "Engine7"`, { stdio: "inherit", shell: true });
|
|
765
1253
|
} catch {
|
|
766
1254
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
767
1255
|
}
|
|
768
1256
|
} else if (process.platform === "darwin") {
|
|
769
|
-
const plistPath =
|
|
770
|
-
if (
|
|
1257
|
+
const plistPath = path2.join(process.env.HOME, "Library", "LaunchAgents", "com.engine7.agent.plist");
|
|
1258
|
+
if (fs2.existsSync(plistPath)) {
|
|
771
1259
|
console.log("\u2705 \u5DF2\u5B89\u88C5\uFF08launchd\uFF09");
|
|
772
1260
|
} else {
|
|
773
1261
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
774
1262
|
}
|
|
775
1263
|
} else {
|
|
776
1264
|
try {
|
|
777
|
-
|
|
1265
|
+
execSync2(`systemctl --user is-enabled engine7`, { stdio: "inherit" });
|
|
778
1266
|
} catch {
|
|
779
1267
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
780
1268
|
}
|
|
@@ -790,8 +1278,8 @@ WantedBy=default.target`;
|
|
|
790
1278
|
console.log(`
|
|
791
1279
|
\u{1F680} engine7 init`);
|
|
792
1280
|
console.log(` state-dir: ${opts.stateDir}`);
|
|
793
|
-
if (
|
|
794
|
-
const files =
|
|
1281
|
+
if (fs2.existsSync(opts.stateDir) && !opts.dryRun) {
|
|
1282
|
+
const files = fs2.readdirSync(opts.stateDir);
|
|
795
1283
|
if (files.length > 0) {
|
|
796
1284
|
console.error(`
|
|
797
1285
|
\u274C \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A: ${opts.stateDir}`);
|
|
@@ -809,7 +1297,7 @@ WantedBy=default.target`;
|
|
|
809
1297
|
rl.close();
|
|
810
1298
|
}
|
|
811
1299
|
const config = generateConfig(values);
|
|
812
|
-
const configPath =
|
|
1300
|
+
const configPath = path2.join(opts.stateDir, "configs", "main7.json");
|
|
813
1301
|
const tree = buildDirTree(values);
|
|
814
1302
|
tree.push({
|
|
815
1303
|
path: configPath,
|
|
@@ -820,9 +1308,9 @@ WantedBy=default.target`;
|
|
|
820
1308
|
dryRun(tree);
|
|
821
1309
|
const skillsSrc = findTemplateDir("skills");
|
|
822
1310
|
if (skillsSrc) {
|
|
823
|
-
const skills =
|
|
1311
|
+
const skills = fs2.readdirSync(skillsSrc);
|
|
824
1312
|
for (const s of skills) {
|
|
825
|
-
console.log(` \u{1F4C1} ${
|
|
1313
|
+
console.log(` \u{1F4C1} ${path2.join(opts.stateDir, "workspace", "skills", s)}`);
|
|
826
1314
|
}
|
|
827
1315
|
}
|
|
828
1316
|
} else {
|
|
@@ -830,55 +1318,55 @@ WantedBy=default.target`;
|
|
|
830
1318
|
execute(tree);
|
|
831
1319
|
const skillsSrc = findTemplateDir("skills");
|
|
832
1320
|
if (skillsSrc) {
|
|
833
|
-
const skillsDest =
|
|
834
|
-
|
|
835
|
-
const skills =
|
|
1321
|
+
const skillsDest = path2.join(opts.stateDir, "workspace", "skills");
|
|
1322
|
+
fs2.mkdirSync(skillsDest, { recursive: true });
|
|
1323
|
+
const skills = fs2.readdirSync(skillsSrc);
|
|
836
1324
|
for (const s of skills) {
|
|
837
|
-
copyDirRecursive(
|
|
1325
|
+
copyDirRecursive(path2.join(skillsSrc, s), path2.join(skillsDest, s));
|
|
838
1326
|
console.log(` \u{1F4E6} skill: ${s}`);
|
|
839
1327
|
}
|
|
840
1328
|
}
|
|
841
1329
|
console.log(`
|
|
842
1330
|
\u2705 \u521D\u59CB\u5316\u5B8C\u6210\uFF01
|
|
843
1331
|
`);
|
|
844
|
-
const homePointer =
|
|
1332
|
+
const homePointer = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
845
1333
|
const pointerData = { stateDir: opts.stateDir };
|
|
846
|
-
|
|
1334
|
+
fs2.writeFileSync(homePointer, JSON.stringify(pointerData, null, 2) + "\n");
|
|
847
1335
|
console.log(` \u{1F4C4} ${homePointer}\uFF08\u4ECE\u4EFB\u610F\u76EE\u5F55\u90FD\u80FD\u627E\u5230\u6B64 agent\uFF09`);
|
|
848
1336
|
console.log(`\u4E0B\u4E00\u6B65\uFF1A`);
|
|
849
1337
|
console.log(` 1. \u68C0\u67E5\u5E76\u4FEE\u6539 ${opts.stateDir}/configs/main7.json`);
|
|
850
1338
|
console.log(` 2. \u542F\u52A8 Engine: engine7 start\uFF08\u5728\u4EFB\u610F\u76EE\u5F55\u90FD\u884C\uFF09`);
|
|
851
1339
|
console.log(` 3. \u5F00\u673A\u81EA\u542F: engine7 service install\uFF08\u5728\u4EFB\u610F\u76EE\u5F55\u90FD\u884C\uFF09`);
|
|
852
|
-
console.log(` 4. \u67E5\u770B workspace: ${
|
|
1340
|
+
console.log(` 4. \u67E5\u770B workspace: ${path2.join(opts.stateDir, "workspace")}`);
|
|
853
1341
|
}
|
|
854
1342
|
}
|
|
855
1343
|
function copyDirRecursive(src, dest) {
|
|
856
|
-
|
|
857
|
-
for (const entry of
|
|
858
|
-
const srcPath =
|
|
859
|
-
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);
|
|
860
1348
|
if (entry.isDirectory()) {
|
|
861
1349
|
copyDirRecursive(srcPath, destPath);
|
|
862
1350
|
} else {
|
|
863
|
-
|
|
1351
|
+
fs2.copyFileSync(srcPath, destPath);
|
|
864
1352
|
}
|
|
865
1353
|
}
|
|
866
1354
|
}
|
|
867
1355
|
function findTemplateDir(name) {
|
|
868
1356
|
const candidates = [
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1357
|
+
path2.join(__dirname, "..", "templates", name),
|
|
1358
|
+
path2.join(__dirname, "..", "..", "templates", name),
|
|
1359
|
+
path2.join(process.cwd(), "templates", name)
|
|
872
1360
|
];
|
|
873
1361
|
for (const p of candidates) {
|
|
874
|
-
if (
|
|
1362
|
+
if (fs2.existsSync(p)) return p;
|
|
875
1363
|
}
|
|
876
1364
|
return null;
|
|
877
1365
|
}
|
|
878
1366
|
function readHomePointer() {
|
|
879
|
-
const ptrPath =
|
|
1367
|
+
const ptrPath = path2.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
880
1368
|
try {
|
|
881
|
-
return JSON.parse(
|
|
1369
|
+
return JSON.parse(fs2.readFileSync(ptrPath, "utf-8"));
|
|
882
1370
|
} catch {
|
|
883
1371
|
return null;
|
|
884
1372
|
}
|