@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/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.4", haloProcess);
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
- const isTmp = path60.isAbsolute(filePath) && isInTempDir(filePath);
23642
- if (!isTmp && !validatePath(filePath, projectPath)) {
23643
- return c.json({ error: "Path traversal not allowed" }, 403);
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(filePath);
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
- if (err.code === "ENOENT")
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 }, 500);
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.4",
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.4",
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.4",
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
- const child = spawn5(process.execPath, [WRAPPER_PATH, `--mode=${mode}`, `--id=${id}`], {
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 fs75 from "node:fs";
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: !fs75.existsSync(a.workspacePath),
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 fs76 from "node:fs/promises";
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 fs76.readFile(filePath);
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 fs77 from "node:fs";
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 fs77.promises.unlink(tmpPath).catch(() => {
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 fs78 from "node:fs";
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: !fs78.existsSync(acc.workspacePath)
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 fs79 from "node:fs";
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 = fs79.realpathSync(resolved);
37287
- realRoot = fs79.realpathSync(root);
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 = fs79.statSync(real);
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 = fs79.readFileSync(real);
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 fs84 = await import("node:fs");
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 = fs84.statSync(args.filePath);
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 = fs84.readFileSync(args.filePath);
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 fs84 = await import("node:fs");
38498
+ const fs85 = await import("node:fs");
38473
38499
  const path94 = await import("node:path");
38474
- const buf = fs84.readFileSync(args.filePath);
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 fs84 = await import("node:fs");
38517
+ const fs85 = await import("node:fs");
38492
38518
  const path94 = await import("node:path");
38493
- const buf = fs84.readFileSync(args.filePath);
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 fs80 from "node:fs/promises";
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 fs80.readFile(filePath);
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 fs81 from "node:fs";
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 (fs81.existsSync(path90.join(dir, "pnpm-workspace.yaml")))
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
- fs81.mkdirSync(path90.dirname(lockFile), { recursive: true });
40010
- const fd = fs81.openSync(lockFile, fs81.constants.O_RDWR | fs81.constants.O_CREAT, 420);
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
- fs81.closeSync(fd);
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
- fs81.closeSync(fd);
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
- fs81.ftruncateSync(fd);
40032
- fs81.writeSync(fd, String(process.pid));
40033
- fs81.fsyncSync(fd);
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 = fs81.readSync(fd, buf, 0, 32, 0);
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 (!fs81.existsSync(path90.join(PROJECT_ROOT, "pnpm-workspace.yaml")) || !fs81.existsSync(path90.join(PROJECT_ROOT, ".git")))
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 (fs81.existsSync(path90.join(bundled, "index.html")))
40115
+ if (fs82.existsSync(path90.join(bundled, "index.html")))
40090
40116
  return bundled;
40091
40117
  const sibling = path90.resolve(__dirname3, "..", "..", "admin", "out");
40092
- if (fs81.existsSync(path90.join(sibling, "index.html")))
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.4";
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 (!fs81.existsSync(path90.join(HALO_HOME4, "global", ".template-version"))) {
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 (!fs81.existsSync(path90.join(ws, ".halo")))
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 = fs81.readFileSync(indexPath, "utf-8");
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 fs82 from "node:fs";
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
- fs82.lchownSync(dir, owner.uid, owner.gid);
40469
- for (const rel of fs82.readdirSync(dir, { recursive: true }))
40470
- fs82.lchownSync(path91.join(dir, rel), owner.uid, owner.gid);
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 (fs82.existsSync(dir))
40503
+ if (fs83.existsSync(dir))
40478
40504
  return false;
40479
40505
  const tmp = path91.join(base, `.tmp-${crypto13.randomBytes(8).toString("hex")}`);
40480
- fs82.mkdirSync(tmp, { mode: 448 });
40506
+ fs83.mkdirSync(tmp, { mode: 448 });
40481
40507
  try {
40482
40508
  const template = path91.join(base, TEMPLATE_DIR);
40483
- if (fs82.statSync(template, { throwIfNoEntry: false })?.isDirectory()) {
40484
- fs82.cpSync(template, tmp, { recursive: true, verbatimSymlinks: true, filter: (src) => !skipTemplateEntry(path91.relative(template, src)) });
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 (fs82.readdirSync(tmp).length === 0) {
40489
- fs82.rmdirSync(tmp);
40490
- fs82.mkdirSync(dir, { mode: 448 });
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
- fs82.renameSync(tmp, dir);
40521
+ fs83.renameSync(tmp, dir);
40496
40522
  return true;
40497
40523
  } catch (err) {
40498
- fs82.rmSync(tmp, { recursive: true, force: true });
40524
+ fs83.rmSync(tmp, { recursive: true, force: true });
40499
40525
  const code = err.code;
40500
- if ((code === "EEXIST" || code === "ENOTEMPTY") && fs82.existsSync(dir))
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 (fs82.existsSync(sessionDir))
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 = fs82.openSync(p, O_PATH);
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 = fs82.readlinkSync(`/proc/self/fd/${pfd}`);
40594
+ const link = fs83.readlinkSync(`/proc/self/fd/${pfd}`);
40569
40595
  if (!link.startsWith(`${root}/`))
40570
40596
  return `not under ${root}/`;
40571
- const st = fs82.fstatSync(pfd);
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
- fs82.closeSync(pfd);
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
- fs82.mkdirSync(sub);
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
- fs82.rmSync(sub, { recursive: true, force: true });
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 = fs82.realpathSync(o.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
- fs82.rmSync(dir, { recursive: true, force: true });
40751
+ fs83.rmSync(dir, { recursive: true, force: true });
40726
40752
  settle();
40727
40753
  };
40728
40754
  try {
40729
- dir = fs82.mkdtempSync(path91.join(os11.tmpdir(), "halo-notify-"));
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 = () => fs82.readFileSync("/proc/self/mountinfo", "utf8");
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 fs83 from "node:fs";
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.4";
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 (!fs83.existsSync(path93.join(workspace, ".halo"))) {
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 = fs83.readFileSync(SERVER_LOCK_PATH, "utf-8").trim();
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
- fs83.mkdirSync(path93.dirname(SERVER_LOG_PATH), { recursive: true });
43422
- const out = fs83.openSync(SERVER_LOG_PATH, "a");
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 (!fs83.statSync(workspace, { throwIfNoEntry: false })?.isDirectory()) {
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 (!fs83.existsSync(SERVER_LOG_PATH)) {
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 fs83.existsSync(path93.join(homedir26(), ".halo", "global", ".template-version"));
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);