@turmind/halo 2.0.4 → 2.0.6
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/admin-out/404.html +1 -1
- package/admin-out/_next/static/chunks/app/page-2e03b23bcc38e934.js +1 -0
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +2 -2
- package/bundled-docs/design/canvas-extensions.md +3 -3
- package/bundled-docs/design/evolution.md +2 -2
- package/bundled-docs/dev/api.md +4 -4
- package/bundled-docs/dev/deploy.md +1 -1
- package/bundled-docs/requirements/chat.md +1 -1
- package/bundled-docs/requirements/editor.md +1 -1
- package/dist/evo-wrapper.js +2810 -0
- package/dist/index.js +141 -115
- package/package.json +1 -1
- package/admin-out/_next/static/chunks/app/page-cb05cd98d8f9042a.js +0 -1
- /package/admin-out/_next/static/{_E9raLYdqHoBszjgHvqK3 → l5P7Pjp-jJ_CFv3zMF4cK}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{_E9raLYdqHoBszjgHvqK3 → l5P7Pjp-jJ_CFv3zMF4cK}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -15857,7 +15857,7 @@ async function initObservability(haloProcess = "server") {
|
|
|
15857
15857
|
return true;
|
|
15858
15858
|
}
|
|
15859
15859
|
const { registerSdk: registerSdk2 } = await Promise.resolve().then(() => (init_otel_sdk(), otel_sdk_exports));
|
|
15860
|
-
providers = await registerSdk2(process.env.OTEL_SERVICE_NAME, "2.0.
|
|
15860
|
+
providers = await registerSdk2(process.env.OTEL_SERVICE_NAME, "2.0.6", haloProcess);
|
|
15861
15861
|
console.log(`[Observability] OTLP export \u2192 ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT} (service.name=${process.env.OTEL_SERVICE_NAME}, halo.process=${haloProcess}, capture_content=${config.observability.captureContent})`);
|
|
15862
15862
|
return true;
|
|
15863
15863
|
}
|
|
@@ -23626,6 +23626,24 @@ function createFileRoutes(opts = {}) {
|
|
|
23626
23626
|
m4a: "audio/mp4",
|
|
23627
23627
|
wasm: "application/wasm"
|
|
23628
23628
|
};
|
|
23629
|
+
function systemPath(target) {
|
|
23630
|
+
if (!target || !path60.isAbsolute(target) || target.split(/[\\/]/).includes(".."))
|
|
23631
|
+
return null;
|
|
23632
|
+
return path60.resolve(target);
|
|
23633
|
+
}
|
|
23634
|
+
const fsErrorStatus = (err) => {
|
|
23635
|
+
const code = err.code;
|
|
23636
|
+
return code === "ENOENT" || code === "ENOTDIR" ? 404 : code === "EACCES" || code === "EPERM" ? 403 : 500;
|
|
23637
|
+
};
|
|
23638
|
+
function hiddenAsMissing(syscall, target) {
|
|
23639
|
+
return Object.assign(new Error(`ENOENT: no such file or directory, ${syscall} '${target}'`), { code: "ENOENT" });
|
|
23640
|
+
}
|
|
23641
|
+
async function readableRealPath(target) {
|
|
23642
|
+
const real = await fs51.realpath(target);
|
|
23643
|
+
if (isHiddenHostPath(real))
|
|
23644
|
+
throw hiddenAsMissing("realpath", target);
|
|
23645
|
+
return real;
|
|
23646
|
+
}
|
|
23629
23647
|
app2.get("/files/download", async (c) => {
|
|
23630
23648
|
try {
|
|
23631
23649
|
const filePath = c.req.query("path");
|
|
@@ -23638,16 +23656,23 @@ function createFileRoutes(opts = {}) {
|
|
|
23638
23656
|
if (!projectPath) {
|
|
23639
23657
|
return c.json({ error: "Project not found" }, 404);
|
|
23640
23658
|
}
|
|
23641
|
-
|
|
23642
|
-
if (
|
|
23643
|
-
|
|
23659
|
+
let absolutePath;
|
|
23660
|
+
if (path60.isAbsolute(filePath)) {
|
|
23661
|
+
const target = systemPath(filePath);
|
|
23662
|
+
if (!target)
|
|
23663
|
+
return c.json({ error: "absolute path without .. required" }, 400);
|
|
23664
|
+
absolutePath = await readableRealPath(target);
|
|
23665
|
+
} else {
|
|
23666
|
+
if (!validatePath(filePath, projectPath)) {
|
|
23667
|
+
return c.json({ error: "Path traversal not allowed" }, 403);
|
|
23668
|
+
}
|
|
23669
|
+
absolutePath = path60.resolve(projectPath, filePath);
|
|
23644
23670
|
}
|
|
23645
|
-
const absolutePath = isTmp ? path60.resolve(filePath) : path60.resolve(projectPath, filePath);
|
|
23646
23671
|
const stat = await fs51.stat(absolutePath);
|
|
23647
23672
|
if (stat.isDirectory()) {
|
|
23648
23673
|
return c.json({ error: "Cannot download a directory" }, 400);
|
|
23649
23674
|
}
|
|
23650
|
-
const fileName = path60.basename(
|
|
23675
|
+
const fileName = path60.basename(absolutePath);
|
|
23651
23676
|
const ext = fileName.split(".").pop()?.toLowerCase() ?? "";
|
|
23652
23677
|
const mime = inline ? imageMimeFromExt(ext) ?? MIME_MAP[ext] ?? "application/octet-stream" : "application/octet-stream";
|
|
23653
23678
|
const disposition = inline ? `inline; filename="${encodeURIComponent(fileName)}"` : `attachment; filename="${encodeURIComponent(fileName)}"`;
|
|
@@ -23690,11 +23715,12 @@ function createFileRoutes(opts = {}) {
|
|
|
23690
23715
|
}
|
|
23691
23716
|
return new Response(webStream, { status, headers });
|
|
23692
23717
|
} catch (err) {
|
|
23693
|
-
|
|
23718
|
+
const status = fsErrorStatus(err);
|
|
23719
|
+
if (status === 404)
|
|
23694
23720
|
return c.json({ error: "File not found" }, 404);
|
|
23695
23721
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
23696
23722
|
console.log(`[Files] Error downloading: ${errorMessage}`);
|
|
23697
|
-
return c.json({ error: errorMessage },
|
|
23723
|
+
return c.json({ error: errorMessage }, status);
|
|
23698
23724
|
}
|
|
23699
23725
|
});
|
|
23700
23726
|
app2.get("/files/download-folder", async (c) => {
|
|
@@ -23874,24 +23900,6 @@ function createFileRoutes(opts = {}) {
|
|
|
23874
23900
|
return c.json({ error: err.message }, 404);
|
|
23875
23901
|
}
|
|
23876
23902
|
});
|
|
23877
|
-
function systemPath(target) {
|
|
23878
|
-
if (!target || !path60.isAbsolute(target) || target.split(/[\\/]/).includes(".."))
|
|
23879
|
-
return null;
|
|
23880
|
-
return path60.resolve(target);
|
|
23881
|
-
}
|
|
23882
|
-
const fsErrorStatus = (err) => {
|
|
23883
|
-
const code = err.code;
|
|
23884
|
-
return code === "ENOENT" || code === "ENOTDIR" ? 404 : code === "EACCES" || code === "EPERM" ? 403 : 500;
|
|
23885
|
-
};
|
|
23886
|
-
function hiddenAsMissing(syscall, target) {
|
|
23887
|
-
return Object.assign(new Error(`ENOENT: no such file or directory, ${syscall} '${target}'`), { code: "ENOENT" });
|
|
23888
|
-
}
|
|
23889
|
-
async function readableRealPath(target) {
|
|
23890
|
-
const real = await fs51.realpath(target);
|
|
23891
|
-
if (isHiddenHostPath(real))
|
|
23892
|
-
throw hiddenAsMissing("realpath", target);
|
|
23893
|
-
return real;
|
|
23894
|
-
}
|
|
23895
23903
|
app2.get("/fs/stat", async (c) => {
|
|
23896
23904
|
const target = systemPath(c.req.query("path"));
|
|
23897
23905
|
if (!target)
|
|
@@ -23928,7 +23936,6 @@ var init_files2 = __esm({
|
|
|
23928
23936
|
"../server/dist/routes/files.js"() {
|
|
23929
23937
|
"use strict";
|
|
23930
23938
|
init_dist();
|
|
23931
|
-
init_media();
|
|
23932
23939
|
init_workspace_path();
|
|
23933
23940
|
init_sandbox();
|
|
23934
23941
|
init_folder_zip();
|
|
@@ -31001,7 +31008,7 @@ function buildCard(workspace, interfaceUrl, caps) {
|
|
|
31001
31008
|
return {
|
|
31002
31009
|
name: raw.name,
|
|
31003
31010
|
description: raw.description,
|
|
31004
|
-
version: typeof raw.version === "string" ? raw.version : "2.0.
|
|
31011
|
+
version: typeof raw.version === "string" ? raw.version : "2.0.6",
|
|
31005
31012
|
supportedInterfaces: [{ url: interfaceUrl, protocolBinding: "JSONRPC", protocolVersion: A2A_VERSION }],
|
|
31006
31013
|
capabilities: { streaming: caps.streaming, pushNotifications: true, extendedAgentCard: false },
|
|
31007
31014
|
...caps.tokenAuth === false ? {} : {
|
|
@@ -33760,7 +33767,7 @@ function createCoreChecker(opts) {
|
|
|
33760
33767
|
}
|
|
33761
33768
|
function getCoreStatus(force) {
|
|
33762
33769
|
checker ??= createCoreChecker({
|
|
33763
|
-
haloVersion: "2.0.
|
|
33770
|
+
haloVersion: "2.0.6",
|
|
33764
33771
|
fetchLatest: fetchNpmLatest,
|
|
33765
33772
|
onChange: (status) => broadcast({ type: "updates:changed", scope: "core", status })
|
|
33766
33773
|
});
|
|
@@ -33928,7 +33935,7 @@ function hubBase() {
|
|
|
33928
33935
|
}
|
|
33929
33936
|
function serverChecker() {
|
|
33930
33937
|
checker2 ??= createExtensionsChecker({
|
|
33931
|
-
haloVersion: "2.0.
|
|
33938
|
+
haloVersion: "2.0.6",
|
|
33932
33939
|
installed: () => {
|
|
33933
33940
|
const s = getSnapshot();
|
|
33934
33941
|
return [...s.extensions, ...s.disabled];
|
|
@@ -34627,29 +34634,48 @@ var init_ticker = __esm({
|
|
|
34627
34634
|
});
|
|
34628
34635
|
|
|
34629
34636
|
// ../server/dist/evolution/spawn.js
|
|
34637
|
+
import fs75 from "node:fs";
|
|
34630
34638
|
import path83 from "node:path";
|
|
34631
34639
|
import { spawn as spawn5 } from "node:child_process";
|
|
34632
34640
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
34641
|
+
function spawnEvoWrapper(wrapperPath, mode, id) {
|
|
34642
|
+
fs75.mkdirSync(evoLogsDir(), { recursive: true });
|
|
34643
|
+
const logPath = mode === "run" ? evoWrapperLogFile(id) : evoApplyLogFile(id);
|
|
34644
|
+
const logFd = fs75.openSync(logPath, "a");
|
|
34645
|
+
let child;
|
|
34646
|
+
try {
|
|
34647
|
+
child = spawn5(process.execPath, [wrapperPath, `--mode=${mode}`, `--id=${id}`], {
|
|
34648
|
+
detached: true,
|
|
34649
|
+
stdio: ["ignore", "ignore", logFd],
|
|
34650
|
+
env: cleanChildEnv(),
|
|
34651
|
+
// Without this, a detached console process on Windows gets a freshly
|
|
34652
|
+
// allocated console window (a black box pops up for every evo/score/apply
|
|
34653
|
+
// run). cron doesn't hit this — it spawns the cli inline, not via a
|
|
34654
|
+
// detached wrapper. No-op on macOS/Linux.
|
|
34655
|
+
windowsHide: true
|
|
34656
|
+
});
|
|
34657
|
+
} finally {
|
|
34658
|
+
fs75.closeSync(logFd);
|
|
34659
|
+
}
|
|
34660
|
+
child.once("exit", (code, signal) => {
|
|
34661
|
+
if (code === 0)
|
|
34662
|
+
return;
|
|
34663
|
+
console.error(`[EvoSpawn] ${mode} wrapper pid=${child.pid} id=${id} exited code=${code} signal=${signal} \u2014 see ${logPath}`);
|
|
34664
|
+
});
|
|
34665
|
+
child.unref();
|
|
34666
|
+
console.log(`[EvoSpawn] launched ${mode} wrapper pid=${child.pid} id=${id}`);
|
|
34667
|
+
return child;
|
|
34668
|
+
}
|
|
34633
34669
|
var __dirname2, WRAPPER_PATH, realEvoSpawner;
|
|
34634
34670
|
var init_spawn = __esm({
|
|
34635
34671
|
"../server/dist/evolution/spawn.js"() {
|
|
34636
34672
|
"use strict";
|
|
34637
34673
|
init_child_env();
|
|
34674
|
+
init_paths();
|
|
34638
34675
|
__dirname2 = path83.dirname(fileURLToPath2(import.meta.url));
|
|
34639
34676
|
WRAPPER_PATH = path83.resolve(__dirname2, "evo-wrapper.js");
|
|
34640
34677
|
realEvoSpawner = (mode, id) => {
|
|
34641
|
-
|
|
34642
|
-
detached: true,
|
|
34643
|
-
stdio: "ignore",
|
|
34644
|
-
env: cleanChildEnv(),
|
|
34645
|
-
// Without this, a detached console process on Windows gets a freshly
|
|
34646
|
-
// allocated console window (a black box pops up for every evo/score/apply
|
|
34647
|
-
// run). cron doesn't hit this — it spawns the cli inline, not via a
|
|
34648
|
-
// detached wrapper. No-op on macOS/Linux.
|
|
34649
|
-
windowsHide: true
|
|
34650
|
-
});
|
|
34651
|
-
child.unref();
|
|
34652
|
-
console.log(`[EvoSpawn] launched ${mode} wrapper pid=${child.pid} id=${id}`);
|
|
34678
|
+
spawnEvoWrapper(WRAPPER_PATH, mode, id);
|
|
34653
34679
|
};
|
|
34654
34680
|
}
|
|
34655
34681
|
});
|
|
@@ -35467,12 +35493,12 @@ var init_handler3 = __esm({
|
|
|
35467
35493
|
});
|
|
35468
35494
|
|
|
35469
35495
|
// ../server/dist/routes/channel-accounts.js
|
|
35470
|
-
import
|
|
35496
|
+
import fs76 from "node:fs";
|
|
35471
35497
|
function accountListFields(a) {
|
|
35472
35498
|
return {
|
|
35473
35499
|
accountId: a.accountId,
|
|
35474
35500
|
workspacePath: a.workspacePath,
|
|
35475
|
-
workspaceMissing: !
|
|
35501
|
+
workspaceMissing: !fs76.existsSync(a.workspacePath),
|
|
35476
35502
|
label: a.label,
|
|
35477
35503
|
enabled: a.enabled,
|
|
35478
35504
|
accessLevel: a.accessLevel,
|
|
@@ -36114,7 +36140,7 @@ var init_cdn = __esm({
|
|
|
36114
36140
|
});
|
|
36115
36141
|
|
|
36116
36142
|
// ../server/dist/channels/wechat/send-media.js
|
|
36117
|
-
import
|
|
36143
|
+
import fs77 from "node:fs/promises";
|
|
36118
36144
|
import path84 from "node:path";
|
|
36119
36145
|
function inferKind(filePath) {
|
|
36120
36146
|
const cls = classifyMedia(filePath);
|
|
@@ -36127,7 +36153,7 @@ function inferKind(filePath) {
|
|
|
36127
36153
|
async function sendMediaFile(params) {
|
|
36128
36154
|
const { baseUrl, token, toUserId, contextToken, contextTokenAt, filePath } = params;
|
|
36129
36155
|
const kind = params.kind ?? inferKind(filePath);
|
|
36130
|
-
const plaintext = await
|
|
36156
|
+
const plaintext = await fs77.readFile(filePath);
|
|
36131
36157
|
const prep = prepareUpload(plaintext);
|
|
36132
36158
|
console.log(`[WeChat] sendMediaFile: path=${filePath} kind=${kind} rawsize=${prep.rawsize} filesize=${prep.filesize}`);
|
|
36133
36159
|
const mediaType = kind === "image" ? UploadMediaType.IMAGE : kind === "video" ? UploadMediaType.VIDEO : UploadMediaType.FILE;
|
|
@@ -36328,7 +36354,7 @@ var init_login = __esm({
|
|
|
36328
36354
|
});
|
|
36329
36355
|
|
|
36330
36356
|
// ../server/dist/channels/wechat/handler.js
|
|
36331
|
-
import
|
|
36357
|
+
import fs78 from "node:fs";
|
|
36332
36358
|
import path85 from "node:path";
|
|
36333
36359
|
import QRCode from "qrcode";
|
|
36334
36360
|
function wxRoute(fromUserId, contextToken, contextTokenAt) {
|
|
@@ -36759,7 +36785,7 @@ async function handleSlashCommand(args) {
|
|
|
36759
36785
|
contextToken,
|
|
36760
36786
|
filePath: tmpPath
|
|
36761
36787
|
});
|
|
36762
|
-
await
|
|
36788
|
+
await fs78.promises.unlink(tmpPath).catch(() => {
|
|
36763
36789
|
});
|
|
36764
36790
|
await reply(t("wechat.qr_sent", lang, { level, path: account.workspacePath }));
|
|
36765
36791
|
waitLogin({ sessionKey: login.sessionKey }).then(async (result2) => {
|
|
@@ -36840,7 +36866,7 @@ var init_handler4 = __esm({
|
|
|
36840
36866
|
// ../server/dist/routes/wechat.js
|
|
36841
36867
|
import { Hono as Hono22 } from "hono";
|
|
36842
36868
|
import path86 from "node:path";
|
|
36843
|
-
import
|
|
36869
|
+
import fs79 from "node:fs";
|
|
36844
36870
|
function createWechatRoutes(deps) {
|
|
36845
36871
|
const { db: db4, channel } = deps;
|
|
36846
36872
|
const app2 = new Hono22();
|
|
@@ -36849,7 +36875,7 @@ function createWechatRoutes(deps) {
|
|
|
36849
36875
|
return {
|
|
36850
36876
|
...stripSecrets(acc),
|
|
36851
36877
|
workspacePath: acc.workspacePath,
|
|
36852
|
-
workspaceMissing: !
|
|
36878
|
+
workspaceMissing: !fs79.existsSync(acc.workspacePath)
|
|
36853
36879
|
};
|
|
36854
36880
|
});
|
|
36855
36881
|
return c.json({ accounts });
|
|
@@ -37078,7 +37104,7 @@ var init_descriptor2 = __esm({
|
|
|
37078
37104
|
import { Hono as Hono23 } from "hono";
|
|
37079
37105
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
37080
37106
|
import crypto12 from "node:crypto";
|
|
37081
|
-
import
|
|
37107
|
+
import fs80 from "node:fs";
|
|
37082
37108
|
function streamWithKeepalive(c, chunks) {
|
|
37083
37109
|
return streamSSE3(c, async (stream) => {
|
|
37084
37110
|
const timer2 = setInterval(() => {
|
|
@@ -37283,8 +37309,8 @@ function createWebRoutes(deps) {
|
|
|
37283
37309
|
let real;
|
|
37284
37310
|
let realRoot;
|
|
37285
37311
|
try {
|
|
37286
|
-
real =
|
|
37287
|
-
realRoot =
|
|
37312
|
+
real = fs80.realpathSync(resolved);
|
|
37313
|
+
realRoot = fs80.realpathSync(root);
|
|
37288
37314
|
} catch {
|
|
37289
37315
|
return c.json({ error: "file not found" }, 404);
|
|
37290
37316
|
}
|
|
@@ -37294,7 +37320,7 @@ function createWebRoutes(deps) {
|
|
|
37294
37320
|
if (isHiddenWorkspacePath(real, realRoot)) {
|
|
37295
37321
|
return c.json({ error: "path is not accessible" }, 403);
|
|
37296
37322
|
}
|
|
37297
|
-
const stat =
|
|
37323
|
+
const stat = fs80.statSync(real);
|
|
37298
37324
|
if (stat.isDirectory())
|
|
37299
37325
|
return c.json({ error: "cannot serve directory" }, 400);
|
|
37300
37326
|
const ext = path94.default.extname(real).toLowerCase();
|
|
@@ -37306,7 +37332,7 @@ function createWebRoutes(deps) {
|
|
|
37306
37332
|
};
|
|
37307
37333
|
const contentType = imageMimeFromExt(ext) || nonImageMimes[ext] || "application/octet-stream";
|
|
37308
37334
|
const fileName = path94.default.basename(resolved);
|
|
37309
|
-
const data =
|
|
37335
|
+
const data = fs80.readFileSync(real);
|
|
37310
37336
|
c.header("content-type", contentType);
|
|
37311
37337
|
c.header("content-disposition", `inline; filename="${fileName}"`);
|
|
37312
37338
|
return c.body(data);
|
|
@@ -37528,10 +37554,10 @@ async function authTest(botToken) {
|
|
|
37528
37554
|
return slackPost("auth.test", botToken, {});
|
|
37529
37555
|
}
|
|
37530
37556
|
async function uploadFile(args) {
|
|
37531
|
-
const
|
|
37557
|
+
const fs85 = await import("node:fs");
|
|
37532
37558
|
const path94 = await import("node:path");
|
|
37533
37559
|
const filename = args.filename ?? path94.basename(args.filePath);
|
|
37534
|
-
const stat =
|
|
37560
|
+
const stat = fs85.statSync(args.filePath);
|
|
37535
37561
|
if (!stat.isFile())
|
|
37536
37562
|
throw new Error(`[slack:uploadFile] not a regular file: ${args.filePath}`);
|
|
37537
37563
|
const length = stat.size;
|
|
@@ -37549,7 +37575,7 @@ async function uploadFile(args) {
|
|
|
37549
37575
|
if (step1Parsed.ok === false || !step1Parsed.upload_url || !step1Parsed.file_id) {
|
|
37550
37576
|
throw new Error(`[slack:files.getUploadURLExternal] ${step1Parsed.error ?? "unknown"}`);
|
|
37551
37577
|
}
|
|
37552
|
-
const buf =
|
|
37578
|
+
const buf = fs85.readFileSync(args.filePath);
|
|
37553
37579
|
const step2 = await fetch(step1Parsed.upload_url, {
|
|
37554
37580
|
method: "POST",
|
|
37555
37581
|
body: buf
|
|
@@ -38469,9 +38495,9 @@ async function replyMessage(args) {
|
|
|
38469
38495
|
return { message_id: parsed.data?.message_id ?? "" };
|
|
38470
38496
|
}
|
|
38471
38497
|
async function uploadImage(args) {
|
|
38472
|
-
const
|
|
38498
|
+
const fs85 = await import("node:fs");
|
|
38473
38499
|
const path94 = await import("node:path");
|
|
38474
|
-
const buf =
|
|
38500
|
+
const buf = fs85.readFileSync(args.filePath);
|
|
38475
38501
|
const filename = path94.basename(args.filePath);
|
|
38476
38502
|
const send2 = async (token) => {
|
|
38477
38503
|
const form = new FormData();
|
|
@@ -38488,9 +38514,9 @@ async function uploadImage(args) {
|
|
|
38488
38514
|
return { imageKey: parsed.data.image_key };
|
|
38489
38515
|
}
|
|
38490
38516
|
async function uploadFile2(args) {
|
|
38491
|
-
const
|
|
38517
|
+
const fs85 = await import("node:fs");
|
|
38492
38518
|
const path94 = await import("node:path");
|
|
38493
|
-
const buf =
|
|
38519
|
+
const buf = fs85.readFileSync(args.filePath);
|
|
38494
38520
|
const filename = args.filename ?? path94.basename(args.filePath);
|
|
38495
38521
|
const send2 = async (token) => {
|
|
38496
38522
|
const form = new FormData();
|
|
@@ -39324,7 +39350,7 @@ var init_event_adapter5 = __esm({
|
|
|
39324
39350
|
|
|
39325
39351
|
// ../server/dist/channels/wecom/handler.js
|
|
39326
39352
|
import path89 from "node:path";
|
|
39327
|
-
import
|
|
39353
|
+
import fs81 from "node:fs/promises";
|
|
39328
39354
|
import { WSClient as WSClient2, generateReqId } from "@wecom/aibot-node-sdk";
|
|
39329
39355
|
function pickConversation(body) {
|
|
39330
39356
|
const userid = body.from?.userid ?? "unknown";
|
|
@@ -39450,7 +39476,7 @@ async function sendWecomMedia(args) {
|
|
|
39450
39476
|
const ext = path89.extname(filePath).toLowerCase();
|
|
39451
39477
|
const cls = classifyMedia(filePath);
|
|
39452
39478
|
const type = cls === "image" && [".png", ".jpg", ".jpeg", ".gif"].includes(ext) ? "image" : cls === "video" && ext === ".mp4" ? "video" : "file";
|
|
39453
|
-
const buf = await
|
|
39479
|
+
const buf = await fs81.readFile(filePath);
|
|
39454
39480
|
if (buf.length > MAX_WECOM_DOWNLOAD_BYTES)
|
|
39455
39481
|
throw new Error("file exceeds 20MB");
|
|
39456
39482
|
const { media_id: mediaId } = await wsClient.uploadMedia(buf, { type, filename: path89.basename(filePath) });
|
|
@@ -39987,14 +40013,14 @@ import { serve } from "@hono/node-server";
|
|
|
39987
40013
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
39988
40014
|
import { WebSocketServer as WebSocketServer4 } from "ws";
|
|
39989
40015
|
import path90 from "node:path";
|
|
39990
|
-
import
|
|
40016
|
+
import fs82 from "node:fs";
|
|
39991
40017
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
39992
40018
|
import { homedir as homedir25 } from "node:os";
|
|
39993
40019
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
39994
40020
|
function findProjectRoot() {
|
|
39995
40021
|
let dir = process.cwd();
|
|
39996
40022
|
for (let i = 0; i < 32; i++) {
|
|
39997
|
-
if (
|
|
40023
|
+
if (fs82.existsSync(path90.join(dir, "pnpm-workspace.yaml")))
|
|
39998
40024
|
return dir;
|
|
39999
40025
|
const parent = path90.dirname(dir);
|
|
40000
40026
|
if (parent === dir)
|
|
@@ -40006,15 +40032,15 @@ function findProjectRoot() {
|
|
|
40006
40032
|
return process.cwd();
|
|
40007
40033
|
}
|
|
40008
40034
|
function acquireSingleInstanceLock(lockFile) {
|
|
40009
|
-
|
|
40010
|
-
const fd =
|
|
40035
|
+
fs82.mkdirSync(path90.dirname(lockFile), { recursive: true });
|
|
40036
|
+
const fd = fs82.openSync(lockFile, fs82.constants.O_RDWR | fs82.constants.O_CREAT, 420);
|
|
40011
40037
|
const result = spawnSync4("flock", ["-n", "3"], {
|
|
40012
40038
|
stdio: ["ignore", "ignore", "ignore", fd]
|
|
40013
40039
|
});
|
|
40014
40040
|
if (result.error) {
|
|
40015
40041
|
const content = readLockPid(fd);
|
|
40016
40042
|
if (content > 0 && isProcessAlive(content)) {
|
|
40017
|
-
|
|
40043
|
+
fs82.closeSync(fd);
|
|
40018
40044
|
console.error(`[Server] another halo server is already running (pid ${content}). kill it first.`);
|
|
40019
40045
|
process.exit(1);
|
|
40020
40046
|
}
|
|
@@ -40023,19 +40049,19 @@ function acquireSingleInstanceLock(lockFile) {
|
|
|
40023
40049
|
if (content > 0 && !isProcessAlive(content)) {
|
|
40024
40050
|
console.warn(`[Server] removing stale server.lock (pid ${content} not running)`);
|
|
40025
40051
|
} else {
|
|
40026
|
-
|
|
40052
|
+
fs82.closeSync(fd);
|
|
40027
40053
|
console.error(`[Server] another halo server is already running${content > 0 ? ` (pid ${content})` : ""}. kill it first.`);
|
|
40028
40054
|
process.exit(1);
|
|
40029
40055
|
}
|
|
40030
40056
|
}
|
|
40031
|
-
|
|
40032
|
-
|
|
40033
|
-
|
|
40057
|
+
fs82.ftruncateSync(fd);
|
|
40058
|
+
fs82.writeSync(fd, String(process.pid));
|
|
40059
|
+
fs82.fsyncSync(fd);
|
|
40034
40060
|
}
|
|
40035
40061
|
function readLockPid(fd) {
|
|
40036
40062
|
try {
|
|
40037
40063
|
const buf = Buffer.alloc(32);
|
|
40038
|
-
const n =
|
|
40064
|
+
const n = fs82.readSync(fd, buf, 0, 32, 0);
|
|
40039
40065
|
return parseInt(buf.subarray(0, n).toString("utf-8").trim(), 10) || 0;
|
|
40040
40066
|
} catch {
|
|
40041
40067
|
return 0;
|
|
@@ -40063,7 +40089,7 @@ function isProcessAlive(pid) {
|
|
|
40063
40089
|
}
|
|
40064
40090
|
}
|
|
40065
40091
|
function readGitSha() {
|
|
40066
|
-
if (!
|
|
40092
|
+
if (!fs82.existsSync(path90.join(PROJECT_ROOT, "pnpm-workspace.yaml")) || !fs82.existsSync(path90.join(PROJECT_ROOT, ".git")))
|
|
40067
40093
|
return null;
|
|
40068
40094
|
try {
|
|
40069
40095
|
const rev = spawnSync4("git", ["rev-parse", "--short", "HEAD"], { cwd: PROJECT_ROOT, encoding: "utf-8" });
|
|
@@ -40086,10 +40112,10 @@ function resolveFrontendDir() {
|
|
|
40086
40112
|
if (process.env.HALO_FRONTEND_DIR)
|
|
40087
40113
|
return path90.resolve(process.env.HALO_FRONTEND_DIR);
|
|
40088
40114
|
const bundled = path90.resolve(__dirname3, "..", "admin-out");
|
|
40089
|
-
if (
|
|
40115
|
+
if (fs82.existsSync(path90.join(bundled, "index.html")))
|
|
40090
40116
|
return bundled;
|
|
40091
40117
|
const sibling = path90.resolve(__dirname3, "..", "..", "admin", "out");
|
|
40092
|
-
if (
|
|
40118
|
+
if (fs82.existsSync(path90.join(sibling, "index.html")))
|
|
40093
40119
|
return sibling;
|
|
40094
40120
|
return path90.resolve(PROJECT_ROOT, "packages", "admin", "out");
|
|
40095
40121
|
}
|
|
@@ -40194,11 +40220,11 @@ var init_dist2 = __esm({
|
|
|
40194
40220
|
init_git_ssh();
|
|
40195
40221
|
__dirname3 = path90.dirname(fileURLToPath3(import.meta.url));
|
|
40196
40222
|
PORT = config.server.port;
|
|
40197
|
-
HALO_VERSION = "2.0.
|
|
40223
|
+
HALO_VERSION = "2.0.6";
|
|
40198
40224
|
PROJECT_ROOT = findProjectRoot();
|
|
40199
40225
|
HALO_HOME4 = path90.join(homedir25(), ".halo");
|
|
40200
40226
|
GIT_SHA = readGitSha();
|
|
40201
|
-
if (!
|
|
40227
|
+
if (!fs82.existsSync(path90.join(HALO_HOME4, "global", ".template-version"))) {
|
|
40202
40228
|
process.stderr.write("\x1B[31m[Server] ~/.halo/global/ not initialized. Run `halo setup` first.\x1B[0m\n");
|
|
40203
40229
|
process.exit(1);
|
|
40204
40230
|
}
|
|
@@ -40300,7 +40326,7 @@ var init_dist2 = __esm({
|
|
|
40300
40326
|
setCronSessionRegistry(registry);
|
|
40301
40327
|
if (OWNS_RUNTIMES && !AGENTCORE) {
|
|
40302
40328
|
for (const ws of listRunningWorkspaces()) {
|
|
40303
|
-
if (!
|
|
40329
|
+
if (!fs82.existsSync(path90.join(ws, ".halo")))
|
|
40304
40330
|
continue;
|
|
40305
40331
|
if (!claimWorkspaceRuntime(ws))
|
|
40306
40332
|
continue;
|
|
@@ -40346,7 +40372,7 @@ var init_dist2 = __esm({
|
|
|
40346
40372
|
app.get("/*", (c) => {
|
|
40347
40373
|
const indexPath = path90.join(FRONTEND_DIR, "index.html");
|
|
40348
40374
|
try {
|
|
40349
|
-
const html =
|
|
40375
|
+
const html = fs82.readFileSync(indexPath, "utf-8");
|
|
40350
40376
|
return c.html(html);
|
|
40351
40377
|
} catch {
|
|
40352
40378
|
return c.text("Frontend not built. Run: cd packages/admin && npx next build", 503);
|
|
@@ -40434,7 +40460,7 @@ __export(agentcore_front_exports, {
|
|
|
40434
40460
|
workerEnv: () => workerEnv
|
|
40435
40461
|
});
|
|
40436
40462
|
import http3 from "node:http";
|
|
40437
|
-
import
|
|
40463
|
+
import fs83 from "node:fs";
|
|
40438
40464
|
import fsp2 from "node:fs/promises";
|
|
40439
40465
|
import os11 from "node:os";
|
|
40440
40466
|
import path91 from "node:path";
|
|
@@ -40465,39 +40491,39 @@ function skipTemplateEntry(rel) {
|
|
|
40465
40491
|
}
|
|
40466
40492
|
function chownTree(dir, owner, sid) {
|
|
40467
40493
|
try {
|
|
40468
|
-
|
|
40469
|
-
for (const rel of
|
|
40470
|
-
|
|
40494
|
+
fs83.lchownSync(dir, owner.uid, owner.gid);
|
|
40495
|
+
for (const rel of fs83.readdirSync(dir, { recursive: true }))
|
|
40496
|
+
fs83.lchownSync(path91.join(dir, rel), owner.uid, owner.gid);
|
|
40471
40497
|
} catch (err) {
|
|
40472
40498
|
console.warn(`[AgentCore] front: chown ${sid} to ${owner.uid}:${owner.gid} failed (${err.message}) \u2014 serving it as is`);
|
|
40473
40499
|
}
|
|
40474
40500
|
}
|
|
40475
40501
|
function ensureSessionDir(base, sid, owner) {
|
|
40476
40502
|
const dir = path91.join(base, sid);
|
|
40477
|
-
if (
|
|
40503
|
+
if (fs83.existsSync(dir))
|
|
40478
40504
|
return false;
|
|
40479
40505
|
const tmp = path91.join(base, `.tmp-${crypto13.randomBytes(8).toString("hex")}`);
|
|
40480
|
-
|
|
40506
|
+
fs83.mkdirSync(tmp, { mode: 448 });
|
|
40481
40507
|
try {
|
|
40482
40508
|
const template = path91.join(base, TEMPLATE_DIR);
|
|
40483
|
-
if (
|
|
40484
|
-
|
|
40509
|
+
if (fs83.statSync(template, { throwIfNoEntry: false })?.isDirectory()) {
|
|
40510
|
+
fs83.cpSync(template, tmp, { recursive: true, verbatimSymlinks: true, filter: (src) => !skipTemplateEntry(path91.relative(template, src)) });
|
|
40485
40511
|
}
|
|
40486
40512
|
if (owner)
|
|
40487
40513
|
chownTree(tmp, owner, sid);
|
|
40488
|
-
if (
|
|
40489
|
-
|
|
40490
|
-
|
|
40514
|
+
if (fs83.readdirSync(tmp).length === 0) {
|
|
40515
|
+
fs83.rmdirSync(tmp);
|
|
40516
|
+
fs83.mkdirSync(dir, { mode: 448 });
|
|
40491
40517
|
if (owner)
|
|
40492
40518
|
chownTree(dir, owner, sid);
|
|
40493
40519
|
return true;
|
|
40494
40520
|
}
|
|
40495
|
-
|
|
40521
|
+
fs83.renameSync(tmp, dir);
|
|
40496
40522
|
return true;
|
|
40497
40523
|
} catch (err) {
|
|
40498
|
-
|
|
40524
|
+
fs83.rmSync(tmp, { recursive: true, force: true });
|
|
40499
40525
|
const code = err.code;
|
|
40500
|
-
if ((code === "EEXIST" || code === "ENOTEMPTY") &&
|
|
40526
|
+
if ((code === "EEXIST" || code === "ENOTEMPTY") && fs83.existsSync(dir))
|
|
40501
40527
|
return false;
|
|
40502
40528
|
throw err;
|
|
40503
40529
|
}
|
|
@@ -40541,7 +40567,7 @@ async function runViewSetup(base, sessionDir, deps = SYSTEM_SETUP) {
|
|
|
40541
40567
|
const left = mountLayers(base, deps.mountinfo());
|
|
40542
40568
|
if (left > 0)
|
|
40543
40569
|
throw new Error(`view setup: ${base} still has ${left} mount(s) after ${MAX_UMOUNTS} \xD7 umount -l`);
|
|
40544
|
-
if (
|
|
40570
|
+
if (fs83.existsSync(sessionDir))
|
|
40545
40571
|
throw new Error(`view setup: ${base} is still visible after umount -l`);
|
|
40546
40572
|
}
|
|
40547
40573
|
function workerEnv(src, front) {
|
|
@@ -40560,15 +40586,15 @@ async function copyMediaFile(p, root, dest, max = MAX_MEDIA_BYTES) {
|
|
|
40560
40586
|
return "not an absolute path";
|
|
40561
40587
|
let pfd;
|
|
40562
40588
|
try {
|
|
40563
|
-
pfd =
|
|
40589
|
+
pfd = fs83.openSync(p, O_PATH);
|
|
40564
40590
|
} catch (err) {
|
|
40565
40591
|
return `cannot open (${err.code})`;
|
|
40566
40592
|
}
|
|
40567
40593
|
try {
|
|
40568
|
-
const link =
|
|
40594
|
+
const link = fs83.readlinkSync(`/proc/self/fd/${pfd}`);
|
|
40569
40595
|
if (!link.startsWith(`${root}/`))
|
|
40570
40596
|
return `not under ${root}/`;
|
|
40571
|
-
const st =
|
|
40597
|
+
const st = fs83.fstatSync(pfd);
|
|
40572
40598
|
if (!st.isFile())
|
|
40573
40599
|
return "not a regular file";
|
|
40574
40600
|
if (st.size > max)
|
|
@@ -40595,7 +40621,7 @@ async function copyMediaFile(p, root, dest, max = MAX_MEDIA_BYTES) {
|
|
|
40595
40621
|
await src.close();
|
|
40596
40622
|
}
|
|
40597
40623
|
} finally {
|
|
40598
|
-
|
|
40624
|
+
fs83.closeSync(pfd);
|
|
40599
40625
|
}
|
|
40600
40626
|
}
|
|
40601
40627
|
async function copyMediaLines(text6, root, dir, sid) {
|
|
@@ -40608,7 +40634,7 @@ async function copyMediaLines(text6, root, dir, sid) {
|
|
|
40608
40634
|
continue;
|
|
40609
40635
|
}
|
|
40610
40636
|
const sub = path91.join(dir, String(n++));
|
|
40611
|
-
|
|
40637
|
+
fs83.mkdirSync(sub);
|
|
40612
40638
|
const name = path91.basename(m[1]).replace(/[\x00-\x1f\x7f]/g, "_").replace(/^\.+/, "") || "file";
|
|
40613
40639
|
const dest = path91.join(sub, name);
|
|
40614
40640
|
const why = await copyMediaFile(m[1], root, dest).catch((err) => err.message);
|
|
@@ -40616,7 +40642,7 @@ async function copyMediaLines(text6, root, dir, sid) {
|
|
|
40616
40642
|
out.push(`MEDIA:${dest}`);
|
|
40617
40643
|
continue;
|
|
40618
40644
|
}
|
|
40619
|
-
|
|
40645
|
+
fs83.rmSync(sub, { recursive: true, force: true });
|
|
40620
40646
|
console.warn(`[AgentCore] notify ${sid}: MEDIA dropped (${why}): ${JSON.stringify(m[1].slice(0, 300))}`);
|
|
40621
40647
|
}
|
|
40622
40648
|
return out.join("\n");
|
|
@@ -40660,7 +40686,7 @@ function readBody(req, max) {
|
|
|
40660
40686
|
});
|
|
40661
40687
|
}
|
|
40662
40688
|
async function startAgentCoreFront(o) {
|
|
40663
|
-
const base =
|
|
40689
|
+
const base = fs83.realpathSync(o.base);
|
|
40664
40690
|
const workerPort = o.workerPort ?? WORKER_PORT;
|
|
40665
40691
|
const token = crypto13.randomBytes(32).toString("hex");
|
|
40666
40692
|
const isMounted = o.isMounted ?? ((b) => mountLayers(b, readMountinfo()) > 0);
|
|
@@ -40722,11 +40748,11 @@ async function startAgentCoreFront(o) {
|
|
|
40722
40748
|
preparing++;
|
|
40723
40749
|
let dir = "";
|
|
40724
40750
|
const cleanup = () => {
|
|
40725
|
-
|
|
40751
|
+
fs83.rmSync(dir, { recursive: true, force: true });
|
|
40726
40752
|
settle();
|
|
40727
40753
|
};
|
|
40728
40754
|
try {
|
|
40729
|
-
dir =
|
|
40755
|
+
dir = fs83.mkdtempSync(path91.join(os11.tmpdir(), "halo-notify-"));
|
|
40730
40756
|
const text6 = await copyMediaLines(b.text, viewRoot, dir, b.session);
|
|
40731
40757
|
runNotify(cmd, text6, { HALO_SESSION_ID: b.session, HALO_RUN_STATUS: b.status, HALO_WORKSPACE: dir }, dir, cleanup);
|
|
40732
40758
|
} catch (err) {
|
|
@@ -40943,7 +40969,7 @@ var init_agentcore_front = __esm({
|
|
|
40943
40969
|
MAX_MEDIA_BYTES = 20 * 1024 * 1024;
|
|
40944
40970
|
O_PATH = 2097152;
|
|
40945
40971
|
execFileP = promisify3(execFile2);
|
|
40946
|
-
readMountinfo = () =>
|
|
40972
|
+
readMountinfo = () => fs83.readFileSync("/proc/self/mountinfo", "utf8");
|
|
40947
40973
|
SYSTEM_SETUP = {
|
|
40948
40974
|
mountinfo: readMountinfo,
|
|
40949
40975
|
exec: (argv) => execFileP(argv[0], argv.slice(1), { timeout: SETUP_TIMEOUT_MS })
|
|
@@ -42081,7 +42107,7 @@ process.emitWarning = (warning, ...args) => {
|
|
|
42081
42107
|
// src/index.ts
|
|
42082
42108
|
init_server_supervisor();
|
|
42083
42109
|
import path93 from "node:path";
|
|
42084
|
-
import
|
|
42110
|
+
import fs84 from "node:fs";
|
|
42085
42111
|
import { homedir as homedir26, userInfo } from "node:os";
|
|
42086
42112
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
42087
42113
|
|
|
@@ -42372,7 +42398,7 @@ function installUserUnit(opts, run = runCommand) {
|
|
|
42372
42398
|
}
|
|
42373
42399
|
|
|
42374
42400
|
// src/index.ts
|
|
42375
|
-
var VERSION = "2.0.
|
|
42401
|
+
var VERSION = "2.0.6";
|
|
42376
42402
|
var HELP_TOP = `Usage: halo <command> [options]
|
|
42377
42403
|
|
|
42378
42404
|
Commands:
|
|
@@ -43166,7 +43192,7 @@ ${HELP_SESSIONS}`);
|
|
|
43166
43192
|
}
|
|
43167
43193
|
}
|
|
43168
43194
|
async function cmdSessionsMigrate(workspace) {
|
|
43169
|
-
if (!
|
|
43195
|
+
if (!fs84.existsSync(path93.join(workspace, ".halo"))) {
|
|
43170
43196
|
process.stderr.write(`Not a Halo workspace (no .halo/): ${workspace}
|
|
43171
43197
|
`);
|
|
43172
43198
|
process.exitCode = 1;
|
|
@@ -43311,7 +43337,7 @@ var SERVER_LOCK_PATH = path93.join(homedir26(), ".halo", "global", "server.lock"
|
|
|
43311
43337
|
var SERVER_LOG_PATH = path93.join(homedir26(), ".halo", "logs", "server.log");
|
|
43312
43338
|
function readServerPid() {
|
|
43313
43339
|
try {
|
|
43314
|
-
const txt =
|
|
43340
|
+
const txt = fs84.readFileSync(SERVER_LOCK_PATH, "utf-8").trim();
|
|
43315
43341
|
const n = parseInt(txt, 10);
|
|
43316
43342
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
43317
43343
|
} catch {
|
|
@@ -43418,8 +43444,8 @@ async function cmdServerStart(argv, restart) {
|
|
|
43418
43444
|
}
|
|
43419
43445
|
if (plan.note) writeDim(plan.note);
|
|
43420
43446
|
const { spawn: spawn7 } = await import("node:child_process");
|
|
43421
|
-
|
|
43422
|
-
const out =
|
|
43447
|
+
fs84.mkdirSync(path93.dirname(SERVER_LOG_PATH), { recursive: true });
|
|
43448
|
+
const out = fs84.openSync(SERVER_LOG_PATH, "a");
|
|
43423
43449
|
const child = spawn7(process.argv[0], [process.argv[1], "server", "start"], {
|
|
43424
43450
|
detached: true,
|
|
43425
43451
|
stdio: ["ignore", out, out],
|
|
@@ -43501,7 +43527,7 @@ ${HELP_AGENTCORE}`);
|
|
|
43501
43527
|
return;
|
|
43502
43528
|
}
|
|
43503
43529
|
const workspace = path93.resolve(raw);
|
|
43504
|
-
if (!
|
|
43530
|
+
if (!fs84.statSync(workspace, { throwIfNoEntry: false })?.isDirectory()) {
|
|
43505
43531
|
process.stderr.write(`Workspace is not a directory: ${workspace}
|
|
43506
43532
|
`);
|
|
43507
43533
|
process.exitCode = 1;
|
|
@@ -43612,7 +43638,7 @@ async function cmdServerLogs(argv) {
|
|
|
43612
43638
|
lines: { type: "string", short: "n" }
|
|
43613
43639
|
}
|
|
43614
43640
|
});
|
|
43615
|
-
if (!
|
|
43641
|
+
if (!fs84.existsSync(SERVER_LOG_PATH)) {
|
|
43616
43642
|
process.stderr.write(`No log file at ${SERVER_LOG_PATH}
|
|
43617
43643
|
`);
|
|
43618
43644
|
return;
|
|
@@ -43630,7 +43656,7 @@ function exitMissingSetup() {
|
|
|
43630
43656
|
process.exit(1);
|
|
43631
43657
|
}
|
|
43632
43658
|
function isHaloHomeReady() {
|
|
43633
|
-
return
|
|
43659
|
+
return fs84.existsSync(path93.join(homedir26(), ".halo", "global", ".template-version"));
|
|
43634
43660
|
}
|
|
43635
43661
|
async function main2() {
|
|
43636
43662
|
const argv = process.argv.slice(2);
|