@turmind/halo 2.2.5 → 2.2.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
@@ -16830,7 +16830,7 @@ async function initObservability(haloProcess = "server") {
16830
16830
  return true;
16831
16831
  }
16832
16832
  const { registerSdk: registerSdk2 } = await Promise.resolve().then(() => (init_otel_sdk(), otel_sdk_exports));
16833
- providers = await registerSdk2(process.env.OTEL_SERVICE_NAME, "2.2.5", haloProcess);
16833
+ providers = await registerSdk2(process.env.OTEL_SERVICE_NAME, "2.2.6", haloProcess);
16834
16834
  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})`);
16835
16835
  return true;
16836
16836
  }
@@ -23920,6 +23920,31 @@ async function planFolderZip(root, projectPath, opts) {
23920
23920
  const [rootReal, wsReal] = await Promise.all([fs53.realpath(root), fs53.realpath(projectPath)]);
23921
23921
  if (isBlockedPath(rootReal, wsReal))
23922
23922
  throw new FolderZipError(403, "Path is not accessible");
23923
+ const planner = createPlanner(wsReal, opts);
23924
+ await planner.walk(rootReal, "");
23925
+ return planner.plan;
23926
+ }
23927
+ async function planSelectionZip(rels, projectPath, opts) {
23928
+ const selected = new Set(rels);
23929
+ const kept = [...selected].filter((rel) => rel === "" || !rel.split("/").some((_, i, segs) => selected.has(segs.slice(0, i).join("/")))).sort();
23930
+ let common = kept[0].split("/").slice(0, -1);
23931
+ for (const rel of kept) {
23932
+ const segs = rel.split("/").slice(0, -1);
23933
+ let i = 0;
23934
+ while (i < common.length && common[i] === segs[i])
23935
+ i++;
23936
+ common = common.slice(0, i);
23937
+ }
23938
+ const parent = common.join("/");
23939
+ const wsReal = await fs53.realpath(projectPath);
23940
+ const planner = createPlanner(wsReal, opts);
23941
+ for (const rel of kept) {
23942
+ const real = rel ? path60.join(await fs53.realpath(path60.join(wsReal, path60.dirname(rel))), path60.basename(rel)) : wsReal;
23943
+ await planner.addSelected(real, parent ? rel.slice(parent.length + 1) : rel);
23944
+ }
23945
+ return { plan: planner.plan, parent };
23946
+ }
23947
+ function createPlanner(wsReal, opts) {
23923
23948
  const { includeIgnored, limits, signal } = opts;
23924
23949
  const deadline = Date.now() + limits.maxWalkMs;
23925
23950
  const plan = { items: [], files: 0, bytes: 0, excluded: new ExcludedLog() };
@@ -24012,8 +24037,31 @@ async function planFolderZip(root, projectPath, opts) {
24012
24037
  }
24013
24038
  return nonEmpty2;
24014
24039
  }
24015
- await walk(rootReal, "");
24016
- return plan;
24040
+ async function addSelected(real, rel) {
24041
+ signal?.throwIfAborted();
24042
+ if (Date.now() > deadline)
24043
+ throw walkTimeout();
24044
+ const name = path60.basename(real);
24045
+ const st = await fs53.lstat(real);
24046
+ if (rel.split("/").some((seg, i) => isZipUnsafeName(seg, i === 0))) {
24047
+ excluded.add("unreadable", `${rel}${st.isDirectory() ? "/" : ""} (name not valid in a zip)`);
24048
+ } else if (st.isSymbolicLink()) {
24049
+ await addSymlink(real, rel, name);
24050
+ } else if (st.isDirectory()) {
24051
+ if (isBlockedPath(real, wsReal))
24052
+ excluded.add("blocked", `${rel}/`);
24053
+ else if (!await walk(real, rel) && rel)
24054
+ plan.items.push({ rel, mtimeMs: st.mtimeMs });
24055
+ } else if (st.isFile()) {
24056
+ if (isEnvName(name) || isBlockedPath(real, wsReal))
24057
+ excluded.add("blocked", rel);
24058
+ else
24059
+ addFile(rel, real, st);
24060
+ } else {
24061
+ excluded.add("unreadable", `${rel} (not a regular file)`);
24062
+ }
24063
+ }
24064
+ return { plan, walk, addSelected };
24017
24065
  }
24018
24066
  function planSummary(plan) {
24019
24067
  return { files: plan.files, bytes: plan.bytes, excluded: { ...plan.excluded.counts }, ignoredNames: { ...plan.excluded.ignoredNames } };
@@ -24747,26 +24795,44 @@ function createFileRoutes(opts = {}) {
24747
24795
  }
24748
24796
  });
24749
24797
  app2.get("/files/download-folder", async (c) => {
24750
- const relPath = c.req.query("path") ?? "";
24798
+ const relPaths = c.req.queries("path") ?? [];
24799
+ const relPath = relPaths[0] ?? "";
24751
24800
  const projectId = c.req.query("projectId");
24752
24801
  if (!projectId)
24753
24802
  return c.json({ error: "projectId is required" }, 400);
24754
24803
  const signal = c.req.raw.signal;
24804
+ const planOpts = { includeIgnored: c.req.query("include") === "excluded", limits: folderZipLimits, signal };
24755
24805
  try {
24756
- const resolved = await resolveProjectFile(projectId, relPath);
24757
- if ("error" in resolved)
24758
- return c.json({ error: resolved.error }, resolved.status);
24759
- const { projectPath, absolutePath } = resolved;
24760
- if (!(await fs54.stat(absolutePath)).isDirectory())
24761
- return c.json({ error: "Not a directory" }, 400);
24762
- const plan = await planFolderZip(absolutePath, projectPath, {
24763
- includeIgnored: c.req.query("include") === "excluded",
24764
- limits: folderZipLimits,
24765
- signal
24766
- });
24806
+ let plan;
24807
+ let folderName;
24808
+ if (relPaths.length > 1) {
24809
+ let projectPath = "";
24810
+ const rels = [];
24811
+ for (const p of relPaths) {
24812
+ const resolved = await resolveProjectFile(projectId, p);
24813
+ if ("error" in resolved)
24814
+ return c.json({ error: resolved.error }, resolved.status);
24815
+ projectPath = resolved.projectPath;
24816
+ const rel = path61.relative(projectPath, resolved.absolutePath);
24817
+ if (rel === ".." || rel.startsWith(`..${path61.sep}`) || path61.isAbsolute(rel))
24818
+ return c.json({ error: "Path traversal not allowed" }, 403);
24819
+ rels.push(rel.split(path61.sep).join("/"));
24820
+ }
24821
+ const selection = await planSelectionZip(rels, projectPath, planOpts);
24822
+ plan = selection.plan;
24823
+ folderName = `${path61.basename(path61.resolve(projectPath, selection.parent)) || "workspace"}-selection`;
24824
+ } else {
24825
+ const resolved = await resolveProjectFile(projectId, relPath);
24826
+ if ("error" in resolved)
24827
+ return c.json({ error: resolved.error }, resolved.status);
24828
+ const { projectPath, absolutePath } = resolved;
24829
+ if (!(await fs54.stat(absolutePath)).isDirectory())
24830
+ return c.json({ error: "Not a directory" }, 400);
24831
+ plan = await planFolderZip(absolutePath, projectPath, planOpts);
24832
+ folderName = path61.basename(absolutePath) || "workspace";
24833
+ }
24767
24834
  if (c.req.query("estimate") === "1")
24768
24835
  return c.json(planSummary(plan));
24769
- const folderName = path61.basename(absolutePath) || "workspace";
24770
24836
  const fileName = `${folderName}.zip`;
24771
24837
  const asciiName = fileName.replace(/[^\x20-\x7e]|["\\]/g, "_");
24772
24838
  const encodedName = encodeURIComponent(fileName).replace(/['()*]/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`);
@@ -32756,7 +32822,7 @@ function buildCard(workspace, interfaceUrl, caps) {
32756
32822
  return {
32757
32823
  name: raw.name,
32758
32824
  description: raw.description,
32759
- version: typeof raw.version === "string" ? raw.version : "2.2.5",
32825
+ version: typeof raw.version === "string" ? raw.version : "2.2.6",
32760
32826
  supportedInterfaces: [{ url: interfaceUrl, protocolBinding: "JSONRPC", protocolVersion: A2A_VERSION }],
32761
32827
  capabilities: { streaming: caps.streaming, pushNotifications: true, extendedAgentCard: false },
32762
32828
  ...caps.tokenAuth === false ? {} : {
@@ -34238,7 +34304,7 @@ async function finishRun(runId, db4, session) {
34238
34304
  if (session.turnError) {
34239
34305
  status = "failed";
34240
34306
  failureReason = session.turnError.slice(0, 500);
34241
- } else if (!text6) {
34307
+ } else if (!answer) {
34242
34308
  status = "failed";
34243
34309
  failureReason = "run produced no output (nothing to dispatch)";
34244
34310
  }
@@ -34250,7 +34316,7 @@ async function finishRun(runId, db4, session) {
34250
34316
  const targets = job ? parseTargets(job.targets) : [];
34251
34317
  let dispatchResults = [];
34252
34318
  if (job && status === "succeeded" && targets.length > 0) {
34253
- dispatchResults = await dispatchToTargets(text6, targets, row.workspacePath ?? job.workspacePath);
34319
+ dispatchResults = await dispatchToTargets(answer, targets, row.workspacePath ?? job.workspacePath);
34254
34320
  if (dispatchResults.length > 0 && dispatchResults.every((r) => !r.ok)) {
34255
34321
  status = "failed";
34256
34322
  failureReason = `all ${dispatchResults.length} target(s) failed: ${dispatchResults.map((r) => `${r.channelType}:${r.error ?? "?"}`).join("; ")}`;
@@ -35928,7 +35994,7 @@ function createCoreChecker(opts) {
35928
35994
  }
35929
35995
  function getCoreStatus(force) {
35930
35996
  checker ??= createCoreChecker({
35931
- haloVersion: "2.2.5",
35997
+ haloVersion: "2.2.6",
35932
35998
  fetchLatest: fetchNpmLatest,
35933
35999
  onChange: (status) => broadcast({ type: "updates:changed", scope: "core", status })
35934
36000
  });
@@ -36096,7 +36162,7 @@ function hubBase() {
36096
36162
  }
36097
36163
  function serverChecker() {
36098
36164
  checker2 ??= createExtensionsChecker({
36099
- haloVersion: "2.2.5",
36165
+ haloVersion: "2.2.6",
36100
36166
  installed: () => {
36101
36167
  const s = getSnapshot();
36102
36168
  return [...s.extensions, ...s.disabled];
@@ -40952,7 +41018,6 @@ var init_api3 = __esm({
40952
41018
 
40953
41019
  // ../server/dist/channels/feishu/handler.js
40954
41020
  import path93 from "node:path";
40955
- import * as Lark from "@larksuiteoapi/node-sdk";
40956
41021
  function buildSessionPrefixForThread2(chatId, rootId) {
40957
41022
  return sessionPrefix("feishu", `${chatId}:${rootId}`);
40958
41023
  }
@@ -41199,13 +41264,16 @@ function startFeishuChannel(deps) {
41199
41264
  const state = ensureState(accountId);
41200
41265
  if (state.stopped || state.wsClient)
41201
41266
  return;
41202
- const wsClient = new Lark.WSClient({
41267
+ const { WSClient: WSClient2, EventDispatcher, LoggerLevel } = await import("./feishu-sdk.cjs");
41268
+ if (state.stopped || state.wsClient)
41269
+ return;
41270
+ const wsClient = new WSClient2({
41203
41271
  appId: account.appId,
41204
41272
  appSecret: account.appSecret,
41205
- loggerLevel: Lark.LoggerLevel.warn
41273
+ loggerLevel: LoggerLevel.warn
41206
41274
  });
41207
41275
  state.wsClient = wsClient;
41208
- const dispatcher = new Lark.EventDispatcher({});
41276
+ const dispatcher = new EventDispatcher({});
41209
41277
  dispatcher.register({
41210
41278
  "im.message.receive_v1": async (data) => {
41211
41279
  dispatchEvent(accountId, data);
@@ -41718,7 +41786,7 @@ var init_event_adapter5 = __esm({
41718
41786
  // ../server/dist/channels/wecom/handler.js
41719
41787
  import path94 from "node:path";
41720
41788
  import fs89 from "node:fs/promises";
41721
- import { WSClient as WSClient2, generateReqId } from "@wecom/aibot-node-sdk";
41789
+ import { WSClient, generateReqId } from "@wecom/aibot-node-sdk";
41722
41790
  function pickConversation(body) {
41723
41791
  const userid = body.from?.userid ?? "unknown";
41724
41792
  if (body.chattype === "group" && body.chatid) {
@@ -41950,7 +42018,7 @@ function startWecomChannel(deps) {
41950
42018
  const state = ensureState(accountId);
41951
42019
  if (state.stopped || state.wsClient)
41952
42020
  return;
41953
- const wsClient = new WSClient2({
42021
+ const wsClient = new WSClient({
41954
42022
  botId: account.botId,
41955
42023
  secret: account.secret,
41956
42024
  maxReconnectAttempts: -1,
@@ -42593,7 +42661,7 @@ var init_dist2 = __esm({
42593
42661
  init_git_ssh();
42594
42662
  __dirname3 = path95.dirname(fileURLToPath3(import.meta.url));
42595
42663
  PORT = config.server.port;
42596
- HALO_VERSION = "2.2.5";
42664
+ HALO_VERSION = "2.2.6";
42597
42665
  PROJECT_ROOT = findProjectRoot();
42598
42666
  HALO_HOME4 = path95.join(homedir26(), ".halo");
42599
42667
  GIT_SHA = readGitSha();
@@ -44791,7 +44859,7 @@ function installUserUnit(opts, run = runCommand) {
44791
44859
  }
44792
44860
 
44793
44861
  // src/index.ts
44794
- var VERSION = "2.2.5";
44862
+ var VERSION = "2.2.6";
44795
44863
  var HELP_TOP = `Usage: halo <command> [options]
44796
44864
 
44797
44865
  Commands:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turmind/halo",
3
- "version": "2.2.5",
3
+ "version": "2.2.6",
4
4
  "description": "Halo — multi-agent collaborative workspace. CLI + TUI + HTTP/WS server in one package.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,7 +22,6 @@
22
22
  "@aws-sdk/client-transcribe-streaming": "^3.1142.0",
23
23
  "@aws-sdk/credential-provider-node": "^3.972.30",
24
24
  "@hono/node-server": "^1.19.17",
25
- "@larksuiteoapi/node-sdk": "^1.65.0",
26
25
  "@opentelemetry/api": "^1.9.1",
27
26
  "@opentelemetry/api-logs": "^0.222.0",
28
27
  "@opentelemetry/exporter-logs-otlp-proto": "^0.222.0",