@turmind/halo 1.1.1 → 1.1.2

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
@@ -1658,10 +1658,19 @@ function isAgentDisabled(agentId, workspaceRoot, disabledSet) {
1658
1658
  return disabledSet.has(`${scope}:${agentId}`);
1659
1659
  }
1660
1660
  async function loadAgentYaml(agentId, workspaceRoot) {
1661
+ const yamlPath = path6.join(agentSourceDir(agentId, workspaceRoot), "agent.yaml");
1661
1662
  try {
1662
- const content = await fs6.readFile(path6.join(agentSourceDir(agentId, workspaceRoot), "agent.yaml"), "utf-8");
1663
- return YAML4.parse(content);
1664
- } catch {
1663
+ const content = await fs6.readFile(yamlPath, "utf-8");
1664
+ const parsed = YAML4.parse(content);
1665
+ if (parsed === null || typeof parsed !== "object") {
1666
+ console.warn(`[AgentLoader] Failed to parse ${yamlPath}: empty or non-object YAML`);
1667
+ return null;
1668
+ }
1669
+ return parsed;
1670
+ } catch (err) {
1671
+ if (err.code === "ENOENT")
1672
+ return null;
1673
+ console.warn(`[AgentLoader] Failed to parse ${yamlPath}: ${err instanceof Error ? err.message : String(err)}`);
1665
1674
  return null;
1666
1675
  }
1667
1676
  }
@@ -2036,6 +2045,7 @@ __export(init_exports, {
2036
2045
  mergeAgentYaml: () => mergeAgentYaml,
2037
2046
  readInstalledOptionalSkills: () => readInstalledOptionalSkills,
2038
2047
  readSeedVersion: () => readSeedVersion,
2048
+ writeFileAtomic: () => writeFileAtomic,
2039
2049
  writeInstalledOptionalSkills: () => writeInstalledOptionalSkills
2040
2050
  });
2041
2051
  import fs7 from "node:fs";
@@ -2057,10 +2067,28 @@ function readSeedVersion(haloHome2) {
2057
2067
  return 0;
2058
2068
  }
2059
2069
  }
2070
+ function writeFileAtomic(filePath, content, mode) {
2071
+ fs7.mkdirSync(path8.dirname(filePath), { recursive: true });
2072
+ const tmpPath = `${filePath}.tmp`;
2073
+ fs7.writeFileSync(tmpPath, content, mode === void 0 ? { encoding: "utf-8" } : { encoding: "utf-8", mode });
2074
+ try {
2075
+ fs7.renameSync(tmpPath, filePath);
2076
+ } catch {
2077
+ fs7.writeFileSync(filePath, content, mode === void 0 ? { encoding: "utf-8" } : { encoding: "utf-8", mode });
2078
+ try {
2079
+ fs7.rmSync(tmpPath, { force: true });
2080
+ } catch {
2081
+ }
2082
+ }
2083
+ }
2060
2084
  function copyTemplate(srcAbs, dstAbs) {
2061
- fs7.mkdirSync(path8.dirname(dstAbs), { recursive: true });
2062
2085
  const content = fs7.readFileSync(srcAbs, "utf-8");
2063
- fs7.writeFileSync(dstAbs, content, "utf-8");
2086
+ try {
2087
+ if (fs7.readFileSync(dstAbs, "utf-8") === content)
2088
+ return;
2089
+ } catch {
2090
+ }
2091
+ writeFileAtomic(dstAbs, content);
2064
2092
  }
2065
2093
  function writeIfMissing(filePath, content, mode) {
2066
2094
  if (fs7.existsSync(filePath))
@@ -2075,7 +2103,8 @@ function mergeAgentYaml(srcAbs, dstAbs) {
2075
2103
  return;
2076
2104
  }
2077
2105
  try {
2078
- const userDoc = YAML5.parseDocument(fs7.readFileSync(dstAbs, "utf-8"));
2106
+ const userRaw = fs7.readFileSync(dstAbs, "utf-8");
2107
+ const userDoc = YAML5.parseDocument(userRaw);
2079
2108
  const templateDoc = YAML5.parseDocument(fs7.readFileSync(srcAbs, "utf-8"));
2080
2109
  const userModel = userDoc.get("model");
2081
2110
  if (userModel != null) {
@@ -2085,7 +2114,10 @@ function mergeAgentYaml(srcAbs, dstAbs) {
2085
2114
  if (userContext != null) {
2086
2115
  templateDoc.set("context", userContext);
2087
2116
  }
2088
- fs7.writeFileSync(dstAbs, templateDoc.toString(), "utf-8");
2117
+ const merged = templateDoc.toString();
2118
+ if (merged === userRaw)
2119
+ return;
2120
+ writeFileAtomic(dstAbs, merged);
2089
2121
  } catch (err) {
2090
2122
  console.log(`[Init] mergeAgentYaml fallback (force-copy) for ${dstAbs}: ${err instanceof Error ? err.message : String(err)}`);
2091
2123
  copyTemplate(srcAbs, dstAbs);
@@ -4128,27 +4160,27 @@ var init_workspace = __esm({
4128
4160
  constructor(projectRoot) {
4129
4161
  this.projectRoot = projectRoot;
4130
4162
  }
4131
- async readFile(path65) {
4132
- const fullPath = this.validatePath(path65);
4163
+ async readFile(path66) {
4164
+ const fullPath = this.validatePath(path66);
4133
4165
  return readFile(fullPath, "utf-8");
4134
4166
  }
4135
- async writeFile(path65, content) {
4136
- const fullPath = this.validatePath(path65);
4167
+ async writeFile(path66, content) {
4168
+ const fullPath = this.validatePath(path66);
4137
4169
  const dir = resolve(fullPath, "..");
4138
4170
  await mkdir(dir, { recursive: true });
4139
4171
  await writeFile(fullPath, content, "utf-8");
4140
4172
  }
4141
- validatePath(path65) {
4142
- const fullPath = isAbsolute(path65) ? resolve(path65) : resolve(this.projectRoot, path65);
4173
+ validatePath(path66) {
4174
+ const fullPath = isAbsolute(path66) ? resolve(path66) : resolve(this.projectRoot, path66);
4143
4175
  const root = resolve(this.projectRoot);
4144
4176
  if (fullPath !== root && !fullPath.startsWith(root + sep)) {
4145
- throw new WorkspaceError(`Path "${path65}" is outside the workspace directory "${this.projectRoot}"`);
4177
+ throw new WorkspaceError(`Path "${path66}" is outside the workspace directory "${this.projectRoot}"`);
4146
4178
  }
4147
4179
  try {
4148
4180
  const real = realpathSync(fullPath);
4149
4181
  const realRoot = realpathSync(root);
4150
4182
  if (real !== realRoot && !real.startsWith(realRoot + sep)) {
4151
- throw new WorkspaceError(`Path "${path65}" is outside the workspace directory "${this.projectRoot}"`);
4183
+ throw new WorkspaceError(`Path "${path66}" is outside the workspace directory "${this.projectRoot}"`);
4152
4184
  }
4153
4185
  } catch (err) {
4154
4186
  if (err instanceof WorkspaceError)
@@ -4326,12 +4358,12 @@ var init_git_manager = __esm({
4326
4358
  const code = parts[0];
4327
4359
  const status = code[0];
4328
4360
  const isRenameOrCopy = status === "R" || status === "C";
4329
- const path65 = isRenameOrCopy ? parts[2] : parts[1];
4330
- if (!path65 || seen.has(path65))
4361
+ const path66 = isRenameOrCopy ? parts[2] : parts[1];
4362
+ if (!path66 || seen.has(path66))
4331
4363
  continue;
4332
- seen.add(path65);
4364
+ seen.add(path66);
4333
4365
  files.push({
4334
- path: path65,
4366
+ path: path66,
4335
4367
  status,
4336
4368
  from: isRenameOrCopy ? parts[1] : void 0
4337
4369
  });
@@ -4479,9 +4511,9 @@ var init_git_manager = __esm({
4479
4511
  * empty for an added file or the root commit) and `modified` is the version
4480
4512
  * at `commit` (empty for a deletion). The working-tree/staged path above is
4481
4513
  * left entirely untouched (commit === undefined preserves existing behavior). */
4482
- async getFileDiff(path65, staged, from, commit) {
4514
+ async getFileDiff(path66, staged, from, commit) {
4483
4515
  if (commit) {
4484
- const parentPath = from || path65;
4516
+ const parentPath = from || path66;
4485
4517
  let original2 = "";
4486
4518
  try {
4487
4519
  original2 = await this.git.show([`${commit}^:${parentPath}`]);
@@ -4490,13 +4522,13 @@ var init_git_manager = __esm({
4490
4522
  }
4491
4523
  let modified2 = "";
4492
4524
  try {
4493
- modified2 = await this.git.show([`${commit}:${path65}`]);
4525
+ modified2 = await this.git.show([`${commit}:${path66}`]);
4494
4526
  } catch {
4495
4527
  modified2 = "";
4496
4528
  }
4497
4529
  return { original: original2, modified: modified2 };
4498
4530
  }
4499
- const headPath = from || path65;
4531
+ const headPath = from || path66;
4500
4532
  let original = "";
4501
4533
  try {
4502
4534
  original = await this.git.show([`HEAD:${headPath}`]);
@@ -4506,13 +4538,13 @@ var init_git_manager = __esm({
4506
4538
  let modified = "";
4507
4539
  if (staged) {
4508
4540
  try {
4509
- modified = await this.git.show([`:${path65}`]);
4541
+ modified = await this.git.show([`:${path66}`]);
4510
4542
  } catch {
4511
4543
  modified = "";
4512
4544
  }
4513
4545
  } else {
4514
4546
  try {
4515
- modified = await this.workspace.readFile(path65);
4547
+ modified = await this.workspace.readFile(path66);
4516
4548
  } catch {
4517
4549
  modified = "";
4518
4550
  }
@@ -9121,13 +9153,20 @@ var init_session_agent_builder = __esm({
9121
9153
  });
9122
9154
  return { agent, yamlConfig, contextConfig, modelId, systemPrompt, thinkingEffort, meta, draftReset };
9123
9155
  }
9124
- /** Throw with a clear message if agent.yaml is missing the model triple. */
9156
+ /** Throw with a clear message if agent.yaml is missing the model triple.
9157
+ * Includes the resolved yaml path and distinguishes "file unreadable"
9158
+ * (yamlConfig null — read/parse failed, see loadAgentYaml) from "config
9159
+ * genuinely incomplete", so the error is self-diagnosing. */
9125
9160
  validateAgentModelConfig(agentId, yamlConfig) {
9126
- const modelId = yamlConfig?.model?.id;
9127
- const endpoint = yamlConfig?.model?.endpoint;
9128
- const providerId = yamlConfig?.model?.provider;
9161
+ const yamlPath = path23.join(agentSourceDir(agentId, this.host.workspaceRoot), "agent.yaml");
9162
+ if (yamlConfig === null) {
9163
+ throw new Error(`[SessionManager] Agent "${agentId}": agent.yaml missing or unreadable at ${yamlPath}.`);
9164
+ }
9165
+ const modelId = yamlConfig.model?.id;
9166
+ const endpoint = yamlConfig.model?.endpoint;
9167
+ const providerId = yamlConfig.model?.provider;
9129
9168
  if (!modelId || !endpoint || !providerId) {
9130
- throw new Error(`[SessionManager] Agent "${agentId}" is missing model config. agent.yaml must specify model.provider, model.id and model.endpoint.`);
9169
+ throw new Error(`[SessionManager] Agent "${agentId}" is missing model config in ${yamlPath}. agent.yaml must specify model.provider, model.id and model.endpoint.`);
9131
9170
  }
9132
9171
  return { modelId, endpoint, providerId };
9133
9172
  }
@@ -14044,6 +14083,41 @@ var init_setup_prompts = __esm({
14044
14083
  }
14045
14084
  });
14046
14085
 
14086
+ // packages/cli/src/upgrade-verify.ts
14087
+ var upgrade_verify_exports = {};
14088
+ __export(upgrade_verify_exports, {
14089
+ ALLOW_SCRIPTS: () => ALLOW_SCRIPTS,
14090
+ NATIVE_MODULE_SMOKES: () => NATIVE_MODULE_SMOKES,
14091
+ verifyNativeModules: () => verifyNativeModules
14092
+ });
14093
+ import path33 from "node:path";
14094
+ import fs27 from "node:fs";
14095
+ import { spawnSync } from "node:child_process";
14096
+ function verifyNativeModules(pkgRoot) {
14097
+ const broken = [];
14098
+ for (const { name, smoke } of NATIVE_MODULE_SMOKES) {
14099
+ const modDir = path33.join(pkgRoot, "node_modules", name);
14100
+ if (!fs27.existsSync(modDir)) {
14101
+ broken.push(name);
14102
+ continue;
14103
+ }
14104
+ const probe = spawnSync(process.execPath, ["-e", smoke, modDir], { encoding: "utf-8" });
14105
+ if (probe.error || probe.status !== 0) broken.push(name);
14106
+ }
14107
+ return broken;
14108
+ }
14109
+ var NATIVE_MODULE_SMOKES, ALLOW_SCRIPTS;
14110
+ var init_upgrade_verify = __esm({
14111
+ "packages/cli/src/upgrade-verify.ts"() {
14112
+ "use strict";
14113
+ NATIVE_MODULE_SMOKES = [
14114
+ { name: "better-sqlite3", smoke: "new (require(process.argv[1]))(':memory:')" },
14115
+ { name: "node-pty", smoke: "require(process.argv[1])" }
14116
+ ];
14117
+ ALLOW_SCRIPTS = "better-sqlite3";
14118
+ }
14119
+ });
14120
+
14047
14121
  // packages/cli/src/render-md.ts
14048
14122
  import { marked } from "marked";
14049
14123
  import { markedTerminal } from "marked-terminal";
@@ -14060,8 +14134,8 @@ var init_render_md = __esm({
14060
14134
  });
14061
14135
 
14062
14136
  // packages/cli/src/resolve-refs.ts
14063
- import fs27 from "node:fs";
14064
- import path33 from "node:path";
14137
+ import fs28 from "node:fs";
14138
+ import path34 from "node:path";
14065
14139
  function resolveRefs(input, workspace) {
14066
14140
  const images = [];
14067
14141
  const attachments = [];
@@ -14074,11 +14148,11 @@ function resolveRefs(input, workspace) {
14074
14148
  lastIdx = match.index + match[0].length;
14075
14149
  const kind = match[1];
14076
14150
  const rawPath = match[2] ?? match[3];
14077
- const resolved = path33.isAbsolute(rawPath) ? rawPath : path33.resolve(workspace, rawPath);
14078
- const ext = path33.extname(resolved).toLowerCase();
14151
+ const resolved = path34.isAbsolute(rawPath) ? rawPath : path34.resolve(workspace, rawPath);
14152
+ const ext = path34.extname(resolved).toLowerCase();
14079
14153
  let stat = null;
14080
14154
  try {
14081
- stat = fs27.statSync(resolved);
14155
+ stat = fs28.statSync(resolved);
14082
14156
  } catch {
14083
14157
  }
14084
14158
  if (!stat) {
@@ -14096,18 +14170,18 @@ function resolveRefs(input, workspace) {
14096
14170
  warnings.push(`${rawPath}: image too large (${(stat.size / 1024 / 1024).toFixed(1)}MB, max ${MAX_IMAGE_BYTES / 1024 / 1024}MB), skipped`);
14097
14171
  continue;
14098
14172
  }
14099
- const data = fs27.readFileSync(resolved).toString("base64");
14173
+ const data = fs28.readFileSync(resolved).toString("base64");
14100
14174
  images.push({ data, mimeType: imageMimeFromExt(ext) ?? "application/octet-stream" });
14101
14175
  } else {
14102
- const relPath = path33.relative(workspace, resolved);
14176
+ const relPath = path34.relative(workspace, resolved);
14103
14177
  if (stat.size > MAX_FILE_BYTES) {
14104
14178
  const buf = Buffer.alloc(MAX_FILE_BYTES);
14105
- const fd = fs27.openSync(resolved, "r");
14179
+ const fd = fs28.openSync(resolved, "r");
14106
14180
  let n = 0;
14107
14181
  try {
14108
- n = fs27.readSync(fd, buf, 0, MAX_FILE_BYTES, 0);
14182
+ n = fs28.readSync(fd, buf, 0, MAX_FILE_BYTES, 0);
14109
14183
  } finally {
14110
- fs27.closeSync(fd);
14184
+ fs28.closeSync(fd);
14111
14185
  }
14112
14186
  const content = buf.subarray(0, n).toString("utf-8").replace(/\uFFFD+$/, "");
14113
14187
  parts.push(`
@@ -14118,7 +14192,7 @@ ${content}
14118
14192
  `);
14119
14193
  warnings.push(`${rawPath}: truncated (${(stat.size / 1024).toFixed(0)}KB, max ${MAX_FILE_BYTES / 1024}KB)`);
14120
14194
  } else {
14121
- const content = fs27.readFileSync(resolved, "utf-8");
14195
+ const content = fs28.readFileSync(resolved, "utf-8");
14122
14196
  parts.push(`
14123
14197
  <file path="${relPath}">
14124
14198
  ${content}
@@ -14390,12 +14464,12 @@ var init_streaming = __esm({
14390
14464
  });
14391
14465
 
14392
14466
  // packages/cli/src/tui/history.ts
14393
- import fs28 from "node:fs";
14394
- import path34 from "node:path";
14467
+ import fs29 from "node:fs";
14468
+ import path35 from "node:path";
14395
14469
  import os2 from "node:os";
14396
14470
  function loadHistory() {
14397
14471
  try {
14398
- const arr = JSON.parse(fs28.readFileSync(HISTORY_FILE, "utf8"));
14472
+ const arr = JSON.parse(fs29.readFileSync(HISTORY_FILE, "utf8"));
14399
14473
  if (Array.isArray(arr)) {
14400
14474
  return arr.filter((x) => typeof x === "string").slice(-MAX_ENTRIES);
14401
14475
  }
@@ -14408,8 +14482,8 @@ function appendHistory(entry) {
14408
14482
  const arr = loadHistory();
14409
14483
  if (arr[arr.length - 1] === entry) return;
14410
14484
  arr.push(entry);
14411
- fs28.mkdirSync(path34.dirname(HISTORY_FILE), { recursive: true });
14412
- fs28.writeFileSync(HISTORY_FILE, JSON.stringify(arr.slice(-MAX_ENTRIES)));
14485
+ fs29.mkdirSync(path35.dirname(HISTORY_FILE), { recursive: true });
14486
+ fs29.writeFileSync(HISTORY_FILE, JSON.stringify(arr.slice(-MAX_ENTRIES)));
14413
14487
  } catch {
14414
14488
  }
14415
14489
  }
@@ -14417,7 +14491,7 @@ var HISTORY_FILE, MAX_ENTRIES;
14417
14491
  var init_history = __esm({
14418
14492
  "packages/cli/src/tui/history.ts"() {
14419
14493
  "use strict";
14420
- HISTORY_FILE = path34.join(os2.homedir(), ".halo", "global", "tui-history.json");
14494
+ HISTORY_FILE = path35.join(os2.homedir(), ".halo", "global", "tui-history.json");
14421
14495
  MAX_ENTRIES = 100;
14422
14496
  }
14423
14497
  });
@@ -14528,8 +14602,8 @@ var init_path_suggest = __esm({
14528
14602
  });
14529
14603
 
14530
14604
  // packages/cli/src/tui/path-suggest-engine.ts
14531
- import fs29 from "node:fs";
14532
- import path35 from "node:path";
14605
+ import fs30 from "node:fs";
14606
+ import path36 from "node:path";
14533
14607
  function detectActiveRef(value) {
14534
14608
  const reExplicit = /@(file|image|scope)\s+(?:"([^"]*)|(\S*))$/;
14535
14609
  const mExplicit = value.match(reExplicit);
@@ -14558,20 +14632,20 @@ function resolveDirAndPrefix(partial, workspace) {
14558
14632
  if (partial === "") {
14559
14633
  return { absDir: workspace, displayDir: ".", prefix: "" };
14560
14634
  }
14561
- const absBase = path35.isAbsolute(partial) ? partial : path35.join(workspace, partial);
14635
+ const absBase = path36.isAbsolute(partial) ? partial : path36.join(workspace, partial);
14562
14636
  if (partial.endsWith("/")) {
14563
14637
  return { absDir: absBase, displayDir: partial.replace(/\/$/, "") || "/", prefix: "" };
14564
14638
  }
14565
- const dirAbs = path35.dirname(absBase);
14566
- const prefix = path35.basename(absBase);
14567
- const dirRel = path35.dirname(partial);
14639
+ const dirAbs = path36.dirname(absBase);
14640
+ const prefix = path36.basename(absBase);
14641
+ const dirRel = path36.dirname(partial);
14568
14642
  return { absDir: dirAbs, displayDir: dirRel === "." ? "." : dirRel, prefix };
14569
14643
  }
14570
14644
  function scanCandidates(partial, workspace, kind) {
14571
14645
  const { absDir, displayDir, prefix } = resolveDirAndPrefix(partial, workspace);
14572
14646
  let entries;
14573
14647
  try {
14574
- entries = fs29.readdirSync(absDir, { withFileTypes: true });
14648
+ entries = fs30.readdirSync(absDir, { withFileTypes: true });
14575
14649
  } catch {
14576
14650
  return { items: [], cwdLabel: displayDir };
14577
14651
  }
@@ -14580,17 +14654,17 @@ function scanCandidates(partial, workspace, kind) {
14580
14654
  if (e.name.startsWith(".")) continue;
14581
14655
  if (prefix && !e.name.toLowerCase().startsWith(prefix.toLowerCase())) continue;
14582
14656
  const isDir = e.isDirectory();
14583
- const ext = path35.extname(e.name).toLowerCase();
14657
+ const ext = path36.extname(e.name).toLowerCase();
14584
14658
  const isImage = IMAGE_EXTS.has(ext);
14585
14659
  if (kind === "image" && !isDir && !isImage) continue;
14586
14660
  if (kind === "scope" && !isDir) continue;
14587
14661
  let insertPath;
14588
- if (path35.isAbsolute(partial)) {
14589
- insertPath = path35.join(absDir, e.name);
14662
+ if (path36.isAbsolute(partial)) {
14663
+ insertPath = path36.join(absDir, e.name);
14590
14664
  } else if (partial.endsWith("/") || partial === "") {
14591
14665
  insertPath = partial + e.name;
14592
14666
  } else {
14593
- const dirRel = path35.dirname(partial);
14667
+ const dirRel = path36.dirname(partial);
14594
14668
  insertPath = (dirRel === "." ? "" : dirRel + "/") + e.name;
14595
14669
  }
14596
14670
  items.push({ name: e.name, insertPath, isDir, isImage });
@@ -14609,8 +14683,8 @@ function applyPathPick(value, ref, pick) {
14609
14683
  const before = value.slice(0, markerStart);
14610
14684
  const kind = ref.kind === "scope" ? "scope" : ref.kind === "image" ? "image" : pick.isImage ? "image" : "file";
14611
14685
  const needsQuote = pick.insertPath.includes(" ");
14612
- const path65 = pick.isDir ? needsQuote ? `"${pick.insertPath}/` : `${pick.insertPath}/` : needsQuote ? `"${pick.insertPath}" ` : `${pick.insertPath} `;
14613
- return `${before}@${kind} ${path65}`;
14686
+ const path66 = pick.isDir ? needsQuote ? `"${pick.insertPath}/` : `${pick.insertPath}/` : needsQuote ? `"${pick.insertPath}" ` : `${pick.insertPath} `;
14687
+ return `${before}@${kind} ${path66}`;
14614
14688
  }
14615
14689
  var init_path_suggest_engine = __esm({
14616
14690
  "packages/cli/src/tui/path-suggest-engine.ts"() {
@@ -16083,15 +16157,15 @@ var init_cli = __esm({
16083
16157
  });
16084
16158
 
16085
16159
  // packages/server/dist/channels/shared/media.js
16086
- import path36 from "node:path";
16160
+ import path37 from "node:path";
16087
16161
  import os3 from "node:os";
16088
16162
  function tempDir() {
16089
- return path36.resolve(os3.tmpdir());
16163
+ return path37.resolve(os3.tmpdir());
16090
16164
  }
16091
16165
  function isInTempDir(filePath) {
16092
- const resolved = path36.resolve(filePath);
16166
+ const resolved = path37.resolve(filePath);
16093
16167
  const tmp = tempDir();
16094
- return resolved === tmp || resolved.startsWith(tmp + path36.sep);
16168
+ return resolved === tmp || resolved.startsWith(tmp + path37.sep);
16095
16169
  }
16096
16170
  function extractMediaPaths(text5) {
16097
16171
  const mediaPaths = [];
@@ -16107,12 +16181,12 @@ function extractMediaMessage(text5) {
16107
16181
  return { text: stripped.replace(/\n{3,}/g, "\n\n").trim(), mediaPaths };
16108
16182
  }
16109
16183
  function isMediaPathAllowed(filePath, workspacePath) {
16110
- const resolved = path36.resolve(filePath);
16111
- const ws = path36.resolve(workspacePath);
16112
- return resolved === ws || resolved.startsWith(ws + path36.sep) || isInTempDir(resolved);
16184
+ const resolved = path37.resolve(filePath);
16185
+ const ws = path37.resolve(workspacePath);
16186
+ return resolved === ws || resolved.startsWith(ws + path37.sep) || isInTempDir(resolved);
16113
16187
  }
16114
16188
  function classifyMedia(filePath) {
16115
- const ext = path36.extname(filePath).toLowerCase();
16189
+ const ext = path37.extname(filePath).toLowerCase();
16116
16190
  if (IMAGE_EXTS.has(ext))
16117
16191
  return "image";
16118
16192
  if (VIDEO_EXTS.has(ext))
@@ -16133,13 +16207,13 @@ var init_media = __esm({
16133
16207
  });
16134
16208
 
16135
16209
  // packages/server/dist/routes/workspace-path.js
16136
- import fs30 from "node:fs/promises";
16210
+ import fs31 from "node:fs/promises";
16137
16211
  import { existsSync as existsSync2, realpathSync as realpathSync3 } from "node:fs";
16138
- import path37 from "node:path";
16212
+ import path38 from "node:path";
16139
16213
  async function resolveProjectPath(projectId) {
16140
- if (path37.isAbsolute(projectId)) {
16214
+ if (path38.isAbsolute(projectId)) {
16141
16215
  try {
16142
- await fs30.access(projectId);
16216
+ await fs31.access(projectId);
16143
16217
  return projectId;
16144
16218
  } catch {
16145
16219
  return null;
@@ -16148,13 +16222,13 @@ async function resolveProjectPath(projectId) {
16148
16222
  return null;
16149
16223
  }
16150
16224
  function realpathBounded2(filePath) {
16151
- let prefix = path37.resolve(filePath);
16225
+ let prefix = path38.resolve(filePath);
16152
16226
  const tail = [];
16153
16227
  while (!existsSync2(prefix)) {
16154
- const parent = path37.dirname(prefix);
16228
+ const parent = path38.dirname(prefix);
16155
16229
  if (parent === prefix)
16156
16230
  break;
16157
- tail.unshift(path37.basename(prefix));
16231
+ tail.unshift(path38.basename(prefix));
16158
16232
  prefix = parent;
16159
16233
  }
16160
16234
  let realPrefix;
@@ -16163,12 +16237,12 @@ function realpathBounded2(filePath) {
16163
16237
  } catch {
16164
16238
  realPrefix = prefix;
16165
16239
  }
16166
- return tail.length > 0 ? path37.join(realPrefix, ...tail) : realPrefix;
16240
+ return tail.length > 0 ? path38.join(realPrefix, ...tail) : realPrefix;
16167
16241
  }
16168
16242
  function validatePath(filePath, projectPath) {
16169
- const resolved = realpathBounded2(path37.resolve(projectPath, filePath));
16243
+ const resolved = realpathBounded2(path38.resolve(projectPath, filePath));
16170
16244
  const proj = realpathBounded2(projectPath);
16171
- return resolved === proj || resolved.startsWith(proj + path37.sep);
16245
+ return resolved === proj || resolved.startsWith(proj + path38.sep);
16172
16246
  }
16173
16247
  function isSafeIdSegment(id) {
16174
16248
  if (id === "." || id === "..")
@@ -16183,10 +16257,10 @@ var init_workspace_path = __esm({
16183
16257
 
16184
16258
  // packages/server/dist/routes/files.js
16185
16259
  import { Hono } from "hono";
16186
- import fs31 from "node:fs/promises";
16260
+ import fs32 from "node:fs/promises";
16187
16261
  import { createReadStream } from "node:fs";
16188
16262
  import { Readable } from "node:stream";
16189
- import path38 from "node:path";
16263
+ import path39 from "node:path";
16190
16264
  import { homedir as homedir17 } from "node:os";
16191
16265
  function createFileRoutes() {
16192
16266
  const app2 = new Hono();
@@ -16203,7 +16277,7 @@ function createFileRoutes() {
16203
16277
  }
16204
16278
  async function dirHasChildren(dirPath) {
16205
16279
  try {
16206
- const entries = await fs31.readdir(dirPath, { withFileTypes: true });
16280
+ const entries = await fs32.readdir(dirPath, { withFileTypes: true });
16207
16281
  for (const entry of entries) {
16208
16282
  if (!isSkippedName(entry.name))
16209
16283
  return true;
@@ -16216,7 +16290,7 @@ function createFileRoutes() {
16216
16290
  async function listDir(dirPath, basePath) {
16217
16291
  let entries;
16218
16292
  try {
16219
- entries = await fs31.readdir(dirPath, { withFileTypes: true });
16293
+ entries = await fs32.readdir(dirPath, { withFileTypes: true });
16220
16294
  } catch {
16221
16295
  return [];
16222
16296
  }
@@ -16231,8 +16305,8 @@ function createFileRoutes() {
16231
16305
  for (const entry of sorted) {
16232
16306
  if (isSkippedName(entry.name))
16233
16307
  continue;
16234
- const fullPath = path38.join(dirPath, entry.name);
16235
- const relativePath = path38.relative(basePath, fullPath).split(path38.sep).join("/");
16308
+ const fullPath = path39.join(dirPath, entry.name);
16309
+ const relativePath = path39.relative(basePath, fullPath).split(path39.sep).join("/");
16236
16310
  if (entry.isDirectory()) {
16237
16311
  nodes.push({
16238
16312
  name: entry.name,
@@ -16264,9 +16338,9 @@ function createFileRoutes() {
16264
16338
  if (relPath && !validatePath(relPath, projectPath)) {
16265
16339
  return c.json({ error: "Path traversal not allowed" }, 403);
16266
16340
  }
16267
- const targetPath = relPath ? path38.resolve(projectPath, relPath) : projectPath;
16341
+ const targetPath = relPath ? path39.resolve(projectPath, relPath) : projectPath;
16268
16342
  try {
16269
- const stat = await fs31.stat(targetPath);
16343
+ const stat = await fs32.stat(targetPath);
16270
16344
  if (!stat.isDirectory()) {
16271
16345
  return c.json({ error: "Path is not a directory" }, 400);
16272
16346
  }
@@ -16276,7 +16350,7 @@ function createFileRoutes() {
16276
16350
  const tree = await listDir(targetPath, projectPath);
16277
16351
  return c.json({
16278
16352
  projectId,
16279
- root: path38.basename(projectPath),
16353
+ root: path39.basename(projectPath),
16280
16354
  path: relPath,
16281
16355
  tree
16282
16356
  });
@@ -16309,7 +16383,7 @@ function createFileRoutes() {
16309
16383
  return;
16310
16384
  let entries;
16311
16385
  try {
16312
- entries = await fs31.readdir(dirPath, { withFileTypes: true });
16386
+ entries = await fs32.readdir(dirPath, { withFileTypes: true });
16313
16387
  } catch {
16314
16388
  return;
16315
16389
  }
@@ -16319,11 +16393,11 @@ function createFileRoutes() {
16319
16393
  if (isSkippedName(entry.name))
16320
16394
  continue;
16321
16395
  scanned++;
16322
- const fullPath = path38.join(dirPath, entry.name);
16396
+ const fullPath = path39.join(dirPath, entry.name);
16323
16397
  if (entry.isDirectory()) {
16324
16398
  if (dirsOnly) {
16325
16399
  const name = entry.name.toLowerCase();
16326
- const rel = path38.relative(projectRoot, fullPath);
16400
+ const rel = path39.relative(projectRoot, fullPath);
16327
16401
  if (!q || name.includes(q) || rel.toLowerCase().includes(q)) {
16328
16402
  matches.push({ name: entry.name, path: rel });
16329
16403
  }
@@ -16331,7 +16405,7 @@ function createFileRoutes() {
16331
16405
  await walk(fullPath);
16332
16406
  } else if (entry.isFile() && !dirsOnly) {
16333
16407
  const name = entry.name.toLowerCase();
16334
- const rel = path38.relative(projectRoot, fullPath);
16408
+ const rel = path39.relative(projectRoot, fullPath);
16335
16409
  if (!q || name.includes(q) || rel.toLowerCase().includes(q)) {
16336
16410
  matches.push({ name: entry.name, path: rel });
16337
16411
  }
@@ -16375,15 +16449,15 @@ function createFileRoutes() {
16375
16449
  }
16376
16450
  const uploaded = [];
16377
16451
  for (const file of files) {
16378
- const relPath = targetDir ? path38.join(targetDir, file.name) : file.name;
16452
+ const relPath = targetDir ? path39.join(targetDir, file.name) : file.name;
16379
16453
  if (!validatePath(relPath, projectPath)) {
16380
16454
  continue;
16381
16455
  }
16382
- const destPath = path38.resolve(projectPath, relPath);
16383
- const destDir = path38.dirname(destPath);
16384
- await fs31.mkdir(destDir, { recursive: true });
16456
+ const destPath = path39.resolve(projectPath, relPath);
16457
+ const destDir = path39.dirname(destPath);
16458
+ await fs32.mkdir(destDir, { recursive: true });
16385
16459
  const buffer = Buffer.from(await file.arrayBuffer());
16386
- await fs31.writeFile(destPath, buffer);
16460
+ await fs32.writeFile(destPath, buffer);
16387
16461
  uploaded.push({
16388
16462
  name: file.name,
16389
16463
  path: relPath,
@@ -16411,9 +16485,9 @@ function createFileRoutes() {
16411
16485
  if (!validatePath(filePath, projectPath)) {
16412
16486
  return c.json({ error: "Path traversal not allowed" }, 403);
16413
16487
  }
16414
- const absolutePath = path38.resolve(projectPath, filePath);
16415
- await fs31.writeFile(absolutePath, content, "utf-8");
16416
- const stat = await fs31.stat(absolutePath);
16488
+ const absolutePath = path39.resolve(projectPath, filePath);
16489
+ await fs32.writeFile(absolutePath, content, "utf-8");
16490
+ const stat = await fs32.stat(absolutePath);
16417
16491
  return c.json({ ok: true, path: filePath, modifiedAt: stat.mtimeMs });
16418
16492
  } catch (err) {
16419
16493
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -16435,14 +16509,14 @@ function createFileRoutes() {
16435
16509
  if (!validatePath(filePath, projectPath)) {
16436
16510
  return c.json({ error: "Path traversal not allowed" }, 403);
16437
16511
  }
16438
- const absolutePath = path38.resolve(projectPath, filePath);
16439
- await fs31.mkdir(path38.dirname(absolutePath), { recursive: true });
16512
+ const absolutePath = path39.resolve(projectPath, filePath);
16513
+ await fs32.mkdir(path39.dirname(absolutePath), { recursive: true });
16440
16514
  try {
16441
- await fs31.access(absolutePath);
16515
+ await fs32.access(absolutePath);
16442
16516
  return c.json({ error: "File already exists" }, 409);
16443
16517
  } catch {
16444
16518
  }
16445
- await fs31.writeFile(absolutePath, "", "utf-8");
16519
+ await fs32.writeFile(absolutePath, "", "utf-8");
16446
16520
  return c.json({ ok: true, path: filePath });
16447
16521
  } catch (err) {
16448
16522
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -16464,8 +16538,8 @@ function createFileRoutes() {
16464
16538
  if (!validatePath(dirPath, projectPath)) {
16465
16539
  return c.json({ error: "Path traversal not allowed" }, 403);
16466
16540
  }
16467
- const absolutePath = path38.resolve(projectPath, dirPath);
16468
- await fs31.mkdir(absolutePath, { recursive: true });
16541
+ const absolutePath = path39.resolve(projectPath, dirPath);
16542
+ await fs32.mkdir(absolutePath, { recursive: true });
16469
16543
  return c.json({ ok: true, path: dirPath });
16470
16544
  } catch (err) {
16471
16545
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -16487,10 +16561,10 @@ function createFileRoutes() {
16487
16561
  if (!validatePath(oldPath, projectPath) || !validatePath(newPath, projectPath)) {
16488
16562
  return c.json({ error: "Path traversal not allowed" }, 403);
16489
16563
  }
16490
- const absOld = path38.resolve(projectPath, oldPath);
16491
- const absNew = path38.resolve(projectPath, newPath);
16492
- await fs31.mkdir(path38.dirname(absNew), { recursive: true });
16493
- await fs31.rename(absOld, absNew);
16564
+ const absOld = path39.resolve(projectPath, oldPath);
16565
+ const absNew = path39.resolve(projectPath, newPath);
16566
+ await fs32.mkdir(path39.dirname(absNew), { recursive: true });
16567
+ await fs32.rename(absOld, absNew);
16494
16568
  return c.json({ ok: true, oldPath, newPath });
16495
16569
  } catch (err) {
16496
16570
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -16512,11 +16586,11 @@ function createFileRoutes() {
16512
16586
  if (!validatePath(filePath, projectPath)) {
16513
16587
  return c.json({ error: "Path traversal not allowed" }, 403);
16514
16588
  }
16515
- const absolutePath = path38.resolve(projectPath, filePath);
16516
- if (absolutePath === path38.resolve(projectPath)) {
16589
+ const absolutePath = path39.resolve(projectPath, filePath);
16590
+ if (absolutePath === path39.resolve(projectPath)) {
16517
16591
  return c.json({ error: "Cannot delete project root" }, 403);
16518
16592
  }
16519
- await fs31.rm(absolutePath, { recursive: true, force: true });
16593
+ await fs32.rm(absolutePath, { recursive: true, force: true });
16520
16594
  return c.json({ ok: true, path: filePath });
16521
16595
  } catch (err) {
16522
16596
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -16550,16 +16624,16 @@ function createFileRoutes() {
16550
16624
  if (!projectPath) {
16551
16625
  return c.json({ error: "Project not found" }, 404);
16552
16626
  }
16553
- const isTmp = path38.isAbsolute(filePath) && isInTempDir(filePath);
16627
+ const isTmp = path39.isAbsolute(filePath) && isInTempDir(filePath);
16554
16628
  if (!isTmp && !validatePath(filePath, projectPath)) {
16555
16629
  return c.json({ error: "Path traversal not allowed" }, 403);
16556
16630
  }
16557
- const absolutePath = isTmp ? path38.resolve(filePath) : path38.resolve(projectPath, filePath);
16558
- const stat = await fs31.stat(absolutePath);
16631
+ const absolutePath = isTmp ? path39.resolve(filePath) : path39.resolve(projectPath, filePath);
16632
+ const stat = await fs32.stat(absolutePath);
16559
16633
  if (stat.isDirectory()) {
16560
16634
  return c.json({ error: "Cannot download a directory" }, 400);
16561
16635
  }
16562
- const fileName = path38.basename(filePath);
16636
+ const fileName = path39.basename(filePath);
16563
16637
  const ext = fileName.split(".").pop()?.toLowerCase() ?? "";
16564
16638
  const mime = inline ? imageMimeFromExt(ext) ?? MIME_MAP[ext] ?? "application/octet-stream" : "application/octet-stream";
16565
16639
  const disposition = inline ? `inline; filename="${encodeURIComponent(fileName)}"` : `attachment; filename="${encodeURIComponent(fileName)}"`;
@@ -16618,8 +16692,8 @@ function createFileRoutes() {
16618
16692
  return c.json({ error: "Project not found" }, 404);
16619
16693
  if (!validatePath(filePath, projectPath))
16620
16694
  return c.json({ error: "Path traversal not allowed" }, 403);
16621
- const absolutePath = path38.resolve(projectPath, filePath);
16622
- const stat = await fs31.stat(absolutePath);
16695
+ const absolutePath = path39.resolve(projectPath, filePath);
16696
+ const stat = await fs32.stat(absolutePath);
16623
16697
  return c.json({ path: filePath, modifiedAt: stat.mtimeMs, createdAt: stat.birthtimeMs, size: stat.size });
16624
16698
  } catch (err) {
16625
16699
  if (err.code === "ENOENT")
@@ -16641,9 +16715,9 @@ function createFileRoutes() {
16641
16715
  if (!validatePath(filePath, projectPath)) {
16642
16716
  return c.json({ error: "Path traversal not allowed" }, 403);
16643
16717
  }
16644
- const absolutePath = path38.resolve(projectPath, filePath);
16718
+ const absolutePath = path39.resolve(projectPath, filePath);
16645
16719
  try {
16646
- const stat = await fs31.stat(absolutePath);
16720
+ const stat = await fs32.stat(absolutePath);
16647
16721
  if (stat.isDirectory()) {
16648
16722
  return c.json({ error: "Path is a directory, use /files/tree instead" }, 400);
16649
16723
  }
@@ -16651,7 +16725,7 @@ function createFileRoutes() {
16651
16725
  if (stat.size > maxSize) {
16652
16726
  return c.json({ error: "File too large (max 10MB)" }, 413);
16653
16727
  }
16654
- const content = await fs31.readFile(absolutePath, "utf-8");
16728
+ const content = await fs32.readFile(absolutePath, "utf-8");
16655
16729
  return c.json({
16656
16730
  path: filePath,
16657
16731
  content,
@@ -16678,16 +16752,16 @@ function createFileRoutes() {
16678
16752
  const body = await c.req.json();
16679
16753
  if (!body.path)
16680
16754
  return c.json({ error: "path required" }, 400);
16681
- const resolved = path38.resolve(body.path);
16755
+ const resolved = path39.resolve(body.path);
16682
16756
  let stat;
16683
16757
  try {
16684
- stat = await fs31.stat(resolved);
16758
+ stat = await fs32.stat(resolved);
16685
16759
  } catch {
16686
16760
  return c.json({ error: "path not found" }, 404);
16687
16761
  }
16688
16762
  if (!stat.isDirectory())
16689
16763
  return c.json({ error: "not a directory" }, 400);
16690
- if (resolved === path38.parse(resolved).root) {
16764
+ if (resolved === path39.parse(resolved).root) {
16691
16765
  return c.json({ error: "filesystem root cannot be a workspace" }, 400);
16692
16766
  }
16693
16767
  const { ensureWorkspaceHalo: ensureWorkspaceHalo2 } = await Promise.resolve().then(() => (init_init(), init_exports));
@@ -16696,10 +16770,10 @@ function createFileRoutes() {
16696
16770
  });
16697
16771
  app2.get("/fs/exists", async (c) => {
16698
16772
  const target = c.req.query("path");
16699
- if (!target || !path38.isAbsolute(target))
16773
+ if (!target || !path39.isAbsolute(target))
16700
16774
  return c.json({ exists: false, reason: "absolute path required" });
16701
16775
  try {
16702
- const stat = await fs31.stat(target);
16776
+ const stat = await fs32.stat(target);
16703
16777
  return c.json({ exists: true, isDirectory: stat.isDirectory() });
16704
16778
  } catch {
16705
16779
  return c.json({ exists: false });
@@ -16707,13 +16781,13 @@ function createFileRoutes() {
16707
16781
  });
16708
16782
  app2.get("/fs/browse", async (c) => {
16709
16783
  const target = c.req.query("path") ?? homedir17();
16710
- if (!path38.isAbsolute(target))
16784
+ if (!path39.isAbsolute(target))
16711
16785
  return c.json({ error: "absolute path required" }, 400);
16712
16786
  try {
16713
- const resolved = path38.resolve(target);
16714
- const entries = await fs31.readdir(resolved, { withFileTypes: true });
16715
- const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: path38.join(resolved, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
16716
- return c.json({ path: resolved, parent: path38.dirname(resolved), entries: dirs });
16787
+ const resolved = path39.resolve(target);
16788
+ const entries = await fs32.readdir(resolved, { withFileTypes: true });
16789
+ const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: path39.join(resolved, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
16790
+ return c.json({ path: resolved, parent: path39.dirname(resolved), entries: dirs });
16717
16791
  } catch (err) {
16718
16792
  return c.json({ error: err.message }, 404);
16719
16793
  }
@@ -16731,9 +16805,9 @@ var init_files = __esm({
16731
16805
 
16732
16806
  // packages/server/dist/routes/data-preview.js
16733
16807
  import { Hono as Hono2 } from "hono";
16734
- import fs32 from "node:fs/promises";
16808
+ import fs33 from "node:fs/promises";
16735
16809
  import { createReadStream as createReadStream2 } from "node:fs";
16736
- import path39 from "node:path";
16810
+ import path40 from "node:path";
16737
16811
  import Database3 from "better-sqlite3";
16738
16812
  import { asyncBufferFromFile, parquetMetadataAsync, parquetSchema, parquetReadObjects } from "hyparquet";
16739
16813
  import { compressors } from "hyparquet-compressors";
@@ -16764,7 +16838,7 @@ function quoteIdent(name) {
16764
16838
  return `"${name.replaceAll('"', '""')}"`;
16765
16839
  }
16766
16840
  async function sniffDelimiter(absolutePath) {
16767
- const fh = await fs32.open(absolutePath, "r");
16841
+ const fh = await fs33.open(absolutePath, "r");
16768
16842
  let head;
16769
16843
  try {
16770
16844
  const buf = Buffer.alloc(65536);
@@ -16848,9 +16922,9 @@ function createDataPreviewRoutes() {
16848
16922
  return { error: "Project not found", status: 404 };
16849
16923
  if (!validatePath(filePath, projectPath))
16850
16924
  return { error: "Path traversal not allowed", status: 403 };
16851
- const absolutePath = path39.resolve(projectPath, filePath);
16925
+ const absolutePath = path40.resolve(projectPath, filePath);
16852
16926
  try {
16853
- const stat = await fs32.stat(absolutePath);
16927
+ const stat = await fs33.stat(absolutePath);
16854
16928
  if (stat.isDirectory())
16855
16929
  return { error: "Path is a directory", status: 400 };
16856
16930
  } catch {
@@ -17056,15 +17130,15 @@ var init_data_preview = __esm({
17056
17130
  });
17057
17131
 
17058
17132
  // packages/server/dist/git-credentials.js
17059
- import fs33 from "node:fs";
17060
- import path40 from "node:path";
17133
+ import fs34 from "node:fs";
17134
+ import path41 from "node:path";
17061
17135
  import { homedir as homedir18 } from "node:os";
17062
- import { spawnSync } from "node:child_process";
17136
+ import { spawnSync as spawnSync2 } from "node:child_process";
17063
17137
  function writeGitCredentialsFile(host, username, token) {
17064
17138
  const line = `https://${encodeURIComponent(username)}:${encodeURIComponent(token)}@${host}`;
17065
17139
  let lines = [];
17066
- if (fs33.existsSync(GIT_CREDENTIALS_PATH)) {
17067
- lines = fs33.readFileSync(GIT_CREDENTIALS_PATH, "utf-8").split("\n").filter((l) => l.trim() !== "");
17140
+ if (fs34.existsSync(GIT_CREDENTIALS_PATH)) {
17141
+ lines = fs34.readFileSync(GIT_CREDENTIALS_PATH, "utf-8").split("\n").filter((l) => l.trim() !== "");
17068
17142
  }
17069
17143
  const kept = lines.filter((l) => {
17070
17144
  try {
@@ -17074,17 +17148,17 @@ function writeGitCredentialsFile(host, username, token) {
17074
17148
  }
17075
17149
  });
17076
17150
  kept.push(line);
17077
- fs33.writeFileSync(GIT_CREDENTIALS_PATH, kept.join("\n") + "\n", { encoding: "utf-8", mode: 384 });
17151
+ fs34.writeFileSync(GIT_CREDENTIALS_PATH, kept.join("\n") + "\n", { encoding: "utf-8", mode: 384 });
17078
17152
  try {
17079
- fs33.chmodSync(GIT_CREDENTIALS_PATH, 384);
17153
+ fs34.chmodSync(GIT_CREDENTIALS_PATH, 384);
17080
17154
  } catch {
17081
17155
  }
17082
17156
  }
17083
17157
  function ensureCredentialHelper() {
17084
- const current = spawnSync("git", ["config", "--global", "--get", "credential.helper"], { encoding: "utf-8" });
17158
+ const current = spawnSync2("git", ["config", "--global", "--get", "credential.helper"], { encoding: "utf-8" });
17085
17159
  const helper = (current.stdout ?? "").trim();
17086
17160
  if (!helper) {
17087
- spawnSync("git", ["config", "--global", "credential.helper", "store"], { encoding: "utf-8" });
17161
+ spawnSync2("git", ["config", "--global", "credential.helper", "store"], { encoding: "utf-8" });
17088
17162
  }
17089
17163
  }
17090
17164
  function saveGitCredentials({ host, username, token }) {
@@ -17093,9 +17167,9 @@ function saveGitCredentials({ host, username, token }) {
17093
17167
  console.log(`[GitCredentials] saved for host ${host}`);
17094
17168
  }
17095
17169
  function listGitCredentials() {
17096
- if (!fs33.existsSync(GIT_CREDENTIALS_PATH))
17170
+ if (!fs34.existsSync(GIT_CREDENTIALS_PATH))
17097
17171
  return [];
17098
- const lines = fs33.readFileSync(GIT_CREDENTIALS_PATH, "utf-8").split("\n").filter((l) => l.trim() !== "");
17172
+ const lines = fs34.readFileSync(GIT_CREDENTIALS_PATH, "utf-8").split("\n").filter((l) => l.trim() !== "");
17099
17173
  const byHost = /* @__PURE__ */ new Map();
17100
17174
  for (const line of lines) {
17101
17175
  try {
@@ -17109,9 +17183,9 @@ function listGitCredentials() {
17109
17183
  return [...byHost.values()];
17110
17184
  }
17111
17185
  function deleteGitCredential(host) {
17112
- if (!fs33.existsSync(GIT_CREDENTIALS_PATH))
17186
+ if (!fs34.existsSync(GIT_CREDENTIALS_PATH))
17113
17187
  return;
17114
- const lines = fs33.readFileSync(GIT_CREDENTIALS_PATH, "utf-8").split("\n").filter((l) => l.trim() !== "");
17188
+ const lines = fs34.readFileSync(GIT_CREDENTIALS_PATH, "utf-8").split("\n").filter((l) => l.trim() !== "");
17115
17189
  const kept = lines.filter((l) => {
17116
17190
  try {
17117
17191
  return new URL(l).host !== host;
@@ -17121,9 +17195,9 @@ function deleteGitCredential(host) {
17121
17195
  });
17122
17196
  if (kept.length === lines.length)
17123
17197
  return;
17124
- fs33.writeFileSync(GIT_CREDENTIALS_PATH, kept.length ? kept.join("\n") + "\n" : "", { encoding: "utf-8", mode: 384 });
17198
+ fs34.writeFileSync(GIT_CREDENTIALS_PATH, kept.length ? kept.join("\n") + "\n" : "", { encoding: "utf-8", mode: 384 });
17125
17199
  try {
17126
- fs33.chmodSync(GIT_CREDENTIALS_PATH, 384);
17200
+ fs34.chmodSync(GIT_CREDENTIALS_PATH, 384);
17127
17201
  } catch {
17128
17202
  }
17129
17203
  console.log(`[GitCredentials] removed for host ${host}`);
@@ -17132,15 +17206,15 @@ var GIT_CREDENTIALS_PATH;
17132
17206
  var init_git_credentials = __esm({
17133
17207
  "packages/server/dist/git-credentials.js"() {
17134
17208
  "use strict";
17135
- GIT_CREDENTIALS_PATH = path40.join(homedir18(), ".git-credentials");
17209
+ GIT_CREDENTIALS_PATH = path41.join(homedir18(), ".git-credentials");
17136
17210
  }
17137
17211
  });
17138
17212
 
17139
17213
  // packages/server/dist/git-ssh.js
17140
- import fs34 from "node:fs";
17214
+ import fs35 from "node:fs";
17141
17215
  import os4 from "node:os";
17142
- import path41 from "node:path";
17143
- import { spawnSync as spawnSync2 } from "node:child_process";
17216
+ import path42 from "node:path";
17217
+ import { spawnSync as spawnSync3 } from "node:child_process";
17144
17218
  function looksLikePrivateKey(name, abs) {
17145
17219
  if (name.endsWith(".pub"))
17146
17220
  return false;
@@ -17148,13 +17222,13 @@ function looksLikePrivateKey(name, abs) {
17148
17222
  return false;
17149
17223
  let firstLine = "";
17150
17224
  try {
17151
- const fd = fs34.openSync(abs, "r");
17225
+ const fd = fs35.openSync(abs, "r");
17152
17226
  try {
17153
17227
  const buf = Buffer.alloc(64);
17154
- const n = fs34.readSync(fd, buf, 0, 64, 0);
17228
+ const n = fs35.readSync(fd, buf, 0, 64, 0);
17155
17229
  firstLine = buf.toString("utf-8", 0, n).split("\n")[0];
17156
17230
  } finally {
17157
- fs34.closeSync(fd);
17231
+ fs35.closeSync(fd);
17158
17232
  }
17159
17233
  } catch {
17160
17234
  return false;
@@ -17162,21 +17236,21 @@ function looksLikePrivateKey(name, abs) {
17162
17236
  return firstLine.includes("PRIVATE KEY-----");
17163
17237
  }
17164
17238
  function isKeyEncrypted(abs) {
17165
- const r = spawnSync2("ssh-keygen", ["-y", "-P", "", "-f", abs], { encoding: "utf-8" });
17239
+ const r = spawnSync3("ssh-keygen", ["-y", "-P", "", "-f", abs], { encoding: "utf-8" });
17166
17240
  return r.status !== 0;
17167
17241
  }
17168
17242
  function listSshKeys() {
17169
17243
  let entries = [];
17170
17244
  try {
17171
- entries = fs34.readdirSync(SSH_DIR);
17245
+ entries = fs35.readdirSync(SSH_DIR);
17172
17246
  } catch {
17173
17247
  return [];
17174
17248
  }
17175
17249
  const keys = [];
17176
17250
  for (const name of entries) {
17177
- const abs = path41.join(SSH_DIR, name);
17251
+ const abs = path42.join(SSH_DIR, name);
17178
17252
  try {
17179
- if (!fs34.statSync(abs).isFile())
17253
+ if (!fs35.statSync(abs).isFile())
17180
17254
  continue;
17181
17255
  } catch {
17182
17256
  continue;
@@ -17190,7 +17264,7 @@ function listSshKeys() {
17190
17264
  function getSshAgentStatus() {
17191
17265
  if (!process.env.SSH_AUTH_SOCK)
17192
17266
  return { agentRunning: false, loadedKeys: [] };
17193
- const r = spawnSync2("ssh-add", ["-l"], { encoding: "utf-8" });
17267
+ const r = spawnSync3("ssh-add", ["-l"], { encoding: "utf-8" });
17194
17268
  if (r.status === 2)
17195
17269
  return { agentRunning: false, loadedKeys: [] };
17196
17270
  const loadedKeys = (r.stdout ?? "").split("\n").map((l) => l.trim()).filter(Boolean).filter((l) => l !== "The agent has no identities.");
@@ -17201,10 +17275,10 @@ function unlockSshKey(keyPath, passphrase) {
17201
17275
  return { ok: false, error: "ssh-agent is not running" };
17202
17276
  let dir = "";
17203
17277
  try {
17204
- dir = fs34.mkdtempSync(path41.join(os4.tmpdir(), "halo-askpass-"));
17205
- const askpass = path41.join(dir, "askpass.sh");
17206
- fs34.writeFileSync(askpass, ASKPASS_SCRIPT, { mode: 448 });
17207
- const r = spawnSync2("ssh-add", [keyPath], {
17278
+ dir = fs35.mkdtempSync(path42.join(os4.tmpdir(), "halo-askpass-"));
17279
+ const askpass = path42.join(dir, "askpass.sh");
17280
+ fs35.writeFileSync(askpass, ASKPASS_SCRIPT, { mode: 448 });
17281
+ const r = spawnSync3("ssh-add", [keyPath], {
17208
17282
  encoding: "utf-8",
17209
17283
  // stdin from /dev/null so ssh-add can never fall back to a tty prompt and block.
17210
17284
  stdio: ["ignore", "pipe", "pipe"],
@@ -17219,10 +17293,10 @@ function unlockSshKey(keyPath, passphrase) {
17219
17293
  }
17220
17294
  });
17221
17295
  if (r.status === 0) {
17222
- console.log(`[GitSsh] unlocked key ${path41.basename(keyPath)}`);
17296
+ console.log(`[GitSsh] unlocked key ${path42.basename(keyPath)}`);
17223
17297
  return { ok: true };
17224
17298
  }
17225
- console.log(`[GitSsh] unlock failed for ${path41.basename(keyPath)} (exit ${r.status ?? "killed"})`);
17299
+ console.log(`[GitSsh] unlock failed for ${path42.basename(keyPath)} (exit ${r.status ?? "killed"})`);
17226
17300
  if (r.status === 2)
17227
17301
  return { ok: false, error: "ssh-agent is not reachable" };
17228
17302
  return { ok: false, error: "Incorrect passphrase, or the key could not be unlocked" };
@@ -17232,19 +17306,19 @@ function unlockSshKey(keyPath, passphrase) {
17232
17306
  return { ok: false, error: "Failed to unlock key" };
17233
17307
  } finally {
17234
17308
  if (dir)
17235
- fs34.rmSync(dir, { recursive: true, force: true });
17309
+ fs35.rmSync(dir, { recursive: true, force: true });
17236
17310
  }
17237
17311
  }
17238
17312
  function ensureSshAgent() {
17239
17313
  try {
17240
17314
  if (process.env.SSH_AUTH_SOCK) {
17241
- const probe = spawnSync2("ssh-add", ["-l"], { encoding: "utf-8" });
17315
+ const probe = spawnSync3("ssh-add", ["-l"], { encoding: "utf-8" });
17242
17316
  if (probe.status !== 2) {
17243
17317
  console.log(`[GitSsh] using existing agent at ${process.env.SSH_AUTH_SOCK}`);
17244
17318
  return;
17245
17319
  }
17246
17320
  }
17247
- const r = spawnSync2("ssh-agent", ["-s"], { encoding: "utf-8" });
17321
+ const r = spawnSync3("ssh-agent", ["-s"], { encoding: "utf-8" });
17248
17322
  if (r.status !== 0) {
17249
17323
  console.log("[GitSsh] ssh-agent unavailable; SSH push/pull will need a manually-started agent");
17250
17324
  return;
@@ -17289,19 +17363,19 @@ function parseRemote(url) {
17289
17363
  }
17290
17364
  }
17291
17365
  function switchRemoteProtocol(projectRoot, to) {
17292
- const cur = spawnSync2("git", ["-C", projectRoot, "remote", "get-url", "origin"], { encoding: "utf-8" });
17366
+ const cur = spawnSync3("git", ["-C", projectRoot, "remote", "get-url", "origin"], { encoding: "utf-8" });
17293
17367
  if (cur.status !== 0)
17294
17368
  throw new Error((cur.stderr ?? "no origin remote").trim());
17295
17369
  const { host, repoPath } = parseRemote((cur.stdout ?? "").trim());
17296
17370
  const next = to === "ssh" ? `git@${host}:${repoPath}` : `https://${host}/${repoPath}`;
17297
- const set = spawnSync2("git", ["-C", projectRoot, "remote", "set-url", "origin", next], { encoding: "utf-8" });
17371
+ const set = spawnSync3("git", ["-C", projectRoot, "remote", "set-url", "origin", next], { encoding: "utf-8" });
17298
17372
  if (set.status !== 0)
17299
17373
  throw new Error((set.stderr ?? "failed to set remote url").trim());
17300
17374
  console.log(`[GitSsh] switched origin to ${to}`);
17301
17375
  return next;
17302
17376
  }
17303
17377
  function getRemoteProtocol(projectRoot) {
17304
- const cur = spawnSync2("git", ["-C", projectRoot, "remote", "get-url", "origin"], { encoding: "utf-8" });
17378
+ const cur = spawnSync3("git", ["-C", projectRoot, "remote", "get-url", "origin"], { encoding: "utf-8" });
17305
17379
  if (cur.status !== 0)
17306
17380
  return { url: "", protocol: "other" };
17307
17381
  const url = (cur.stdout ?? "").trim();
@@ -17315,7 +17389,7 @@ var SSH_DIR, NON_KEY_NAMES, ownedAgentPid, ASKPASS_SCRIPT;
17315
17389
  var init_git_ssh = __esm({
17316
17390
  "packages/server/dist/git-ssh.js"() {
17317
17391
  "use strict";
17318
- SSH_DIR = path41.join(os4.homedir(), ".ssh");
17392
+ SSH_DIR = path42.join(os4.homedir(), ".ssh");
17319
17393
  NON_KEY_NAMES = /* @__PURE__ */ new Set(["known_hosts", "known_hosts2", "authorized_keys", "config"]);
17320
17394
  ownedAgentPid = null;
17321
17395
  ASKPASS_SCRIPT = `#!/bin/sh
@@ -17329,7 +17403,7 @@ printf '%s\\n' "$HALO_SSH_PASSPHRASE"
17329
17403
  // packages/server/dist/routes/git.js
17330
17404
  import { Hono as Hono3 } from "hono";
17331
17405
  import os5 from "node:os";
17332
- import path42 from "node:path";
17406
+ import path43 from "node:path";
17333
17407
  function notifyGitChanged(workspacePath) {
17334
17408
  broadcastToWorkspace(workspacePath, { type: "file:changed", path: ".git", action: "change" });
17335
17409
  }
@@ -17646,9 +17720,9 @@ function createGitRoutes() {
17646
17720
  if (!keyPath || typeof passphrase !== "string") {
17647
17721
  return c.json({ error: "keyPath and passphrase are required" }, 400);
17648
17722
  }
17649
- const sshDir = path42.join(os5.homedir(), ".ssh");
17650
- const resolved = path42.resolve(keyPath);
17651
- if (path42.dirname(resolved) !== path42.resolve(sshDir)) {
17723
+ const sshDir = path43.join(os5.homedir(), ".ssh");
17724
+ const resolved = path43.resolve(keyPath);
17725
+ if (path43.dirname(resolved) !== path43.resolve(sshDir)) {
17652
17726
  return c.json({ error: "keyPath must be inside ~/.ssh" }, 400);
17653
17727
  }
17654
17728
  return c.json(unlockSshKey(resolved, passphrase));
@@ -17724,8 +17798,8 @@ var init_mtime_cache = __esm({
17724
17798
 
17725
17799
  // packages/server/dist/routes/agent-configs.js
17726
17800
  import { Hono as Hono4 } from "hono";
17727
- import fs35 from "node:fs/promises";
17728
- import path43 from "node:path";
17801
+ import fs36 from "node:fs/promises";
17802
+ import path44 from "node:path";
17729
17803
  import { homedir as homedir19 } from "node:os";
17730
17804
  import YAML10 from "yaml";
17731
17805
  function getAvailableTools() {
@@ -17736,6 +17810,17 @@ function getAvailableTools() {
17736
17810
  _cachedTools = [...tools, draft].map((t2) => ({ name: t2.name, description: t2.description }));
17737
17811
  return _cachedTools;
17738
17812
  }
17813
+ async function writeFileAtomic2(filePath, content) {
17814
+ const tmpPath = `${filePath}.tmp`;
17815
+ await fs36.writeFile(tmpPath, content, "utf-8");
17816
+ try {
17817
+ await fs36.rename(tmpPath, filePath);
17818
+ } catch {
17819
+ await fs36.writeFile(filePath, content, "utf-8");
17820
+ await fs36.rm(tmpPath, { force: true }).catch(() => {
17821
+ });
17822
+ }
17823
+ }
17739
17824
  function parseAgentYaml(content) {
17740
17825
  try {
17741
17826
  const data = YAML10.parse(content);
@@ -17821,29 +17906,29 @@ function defaultAgentYamlTemplate() {
17821
17906
  }, { lineWidth: 120 });
17822
17907
  }
17823
17908
  async function ensureDir2(dir) {
17824
- await fs35.mkdir(dir, { recursive: true });
17909
+ await fs36.mkdir(dir, { recursive: true });
17825
17910
  }
17826
17911
  async function scanAgentsDir(dir, scope) {
17827
17912
  const agents = [];
17828
17913
  let names;
17829
17914
  try {
17830
- names = await fs35.readdir(dir);
17915
+ names = await fs36.readdir(dir);
17831
17916
  } catch {
17832
17917
  return agents;
17833
17918
  }
17834
17919
  for (const entryName of names) {
17835
- const agentDir = path43.join(dir, entryName);
17920
+ const agentDir = path44.join(dir, entryName);
17836
17921
  try {
17837
- const stat = await fs35.stat(agentDir);
17922
+ const stat = await fs36.stat(agentDir);
17838
17923
  if (!stat.isDirectory())
17839
17924
  continue;
17840
17925
  } catch {
17841
17926
  continue;
17842
17927
  }
17843
- const yamlPath = path43.join(agentDir, "agent.yaml");
17928
+ const yamlPath = path44.join(agentDir, "agent.yaml");
17844
17929
  try {
17845
- const yamlStat = await fs35.stat(yamlPath);
17846
- const parsed = _yamlCache.get(yamlPath, yamlStat.mtimeMs) ?? _yamlCache.set(yamlPath, yamlStat.mtimeMs, parseAgentYaml(await fs35.readFile(yamlPath, "utf-8")));
17930
+ const yamlStat = await fs36.stat(yamlPath);
17931
+ const parsed = _yamlCache.get(yamlPath, yamlStat.mtimeMs) ?? _yamlCache.set(yamlPath, yamlStat.mtimeMs, parseAgentYaml(await fs36.readFile(yamlPath, "utf-8")));
17847
17932
  const { name, description, model, priority, tools, skills, internal } = parsed;
17848
17933
  agents.push({
17849
17934
  id: entryName,
@@ -17864,13 +17949,13 @@ async function scanAgentsDir(dir, scope) {
17864
17949
  return agents;
17865
17950
  }
17866
17951
  async function ensureDefaultAgent(dir) {
17867
- const defaultDir = path43.join(dir, "default");
17868
- const yamlPath = path43.join(defaultDir, "agent.yaml");
17952
+ const defaultDir = path44.join(dir, "default");
17953
+ const yamlPath = path44.join(defaultDir, "agent.yaml");
17869
17954
  try {
17870
- await fs35.access(yamlPath);
17955
+ await fs36.access(yamlPath);
17871
17956
  } catch {
17872
17957
  await ensureDir2(defaultDir);
17873
- await fs35.writeFile(yamlPath, defaultAgentYamlTemplate(), "utf-8");
17958
+ await fs36.writeFile(yamlPath, defaultAgentYamlTemplate(), "utf-8");
17874
17959
  }
17875
17960
  }
17876
17961
  function createAgentConfigRoutes() {
@@ -17888,7 +17973,7 @@ function createAgentConfigRoutes() {
17888
17973
  const projectId = c.req.query("projectId");
17889
17974
  let workspaceAgents = [];
17890
17975
  if (projectId) {
17891
- const wsAgentsDir = path43.join(projectId, ".halo", "agents");
17976
+ const wsAgentsDir = path44.join(projectId, ".halo", "agents");
17892
17977
  workspaceAgents = await scanAgentsDir(wsAgentsDir, "workspace");
17893
17978
  }
17894
17979
  const wsIds = new Set(workspaceAgents.map((a) => a.id));
@@ -17920,33 +18005,33 @@ function createAgentConfigRoutes() {
17920
18005
  if (scope === "workspace") {
17921
18006
  if (!body.projectId)
17922
18007
  return c.json({ error: "projectId required for workspace agents" }, 400);
17923
- baseDir = path43.join(body.projectId, ".halo", "agents");
18008
+ baseDir = path44.join(body.projectId, ".halo", "agents");
17924
18009
  } else {
17925
18010
  baseDir = GLOBAL_AGENTS_DIR4;
17926
18011
  }
17927
- const agentDir = path43.join(baseDir, id);
18012
+ const agentDir = path44.join(baseDir, id);
17928
18013
  try {
17929
- await fs35.access(agentDir);
18014
+ await fs36.access(agentDir);
17930
18015
  return c.json({ error: "Agent already exists" }, 409);
17931
18016
  } catch {
17932
18017
  }
17933
18018
  let conflictScope = null;
17934
18019
  if (scope === "workspace") {
17935
18020
  try {
17936
- await fs35.access(path43.join(GLOBAL_AGENTS_DIR4, id));
18021
+ await fs36.access(path44.join(GLOBAL_AGENTS_DIR4, id));
17937
18022
  conflictScope = "global";
17938
18023
  } catch {
17939
18024
  }
17940
18025
  } else if (body.projectId) {
17941
18026
  try {
17942
- await fs35.access(path43.join(body.projectId, ".halo", "agents", id));
18027
+ await fs36.access(path44.join(body.projectId, ".halo", "agents", id));
17943
18028
  conflictScope = "workspace";
17944
18029
  } catch {
17945
18030
  }
17946
18031
  }
17947
18032
  await ensureDir2(agentDir);
17948
- await fs35.writeFile(path43.join(agentDir, "agent.yaml"), defaultAgentYaml(body.name, body.description ?? ""), "utf-8");
17949
- await fs35.writeFile(path43.join(agentDir, "AGENT.md"), `# ${body.name}
18033
+ await fs36.writeFile(path44.join(agentDir, "agent.yaml"), defaultAgentYaml(body.name, body.description ?? ""), "utf-8");
18034
+ await fs36.writeFile(path44.join(agentDir, "AGENT.md"), `# ${body.name}
17950
18035
 
17951
18036
  ${body.description ?? ""}
17952
18037
  `, "utf-8");
@@ -17960,13 +18045,13 @@ ${body.description ?? ""}
17960
18045
  const projectId = c.req.query("projectId");
17961
18046
  let agentDir;
17962
18047
  if (scope === "workspace" && projectId) {
17963
- agentDir = path43.join(projectId, ".halo", "agents", id);
18048
+ agentDir = path44.join(projectId, ".halo", "agents", id);
17964
18049
  } else {
17965
- agentDir = path43.join(GLOBAL_AGENTS_DIR4, id);
18050
+ agentDir = path44.join(GLOBAL_AGENTS_DIR4, id);
17966
18051
  }
17967
- const yamlPath = path43.join(agentDir, "agent.yaml");
18052
+ const yamlPath = path44.join(agentDir, "agent.yaml");
17968
18053
  try {
17969
- const content = await fs35.readFile(yamlPath, "utf-8");
18054
+ const content = await fs36.readFile(yamlPath, "utf-8");
17970
18055
  return c.json({ yaml: content });
17971
18056
  } catch {
17972
18057
  return c.json({ error: "Agent not found" }, 404);
@@ -17983,17 +18068,17 @@ ${body.description ?? ""}
17983
18068
  const scope = body.scope ?? "global";
17984
18069
  let agentDir;
17985
18070
  if (scope === "workspace" && body.projectId) {
17986
- agentDir = path43.join(body.projectId, ".halo", "agents", id);
18071
+ agentDir = path44.join(body.projectId, ".halo", "agents", id);
17987
18072
  } else {
17988
- agentDir = path43.join(GLOBAL_AGENTS_DIR4, id);
18073
+ agentDir = path44.join(GLOBAL_AGENTS_DIR4, id);
17989
18074
  }
17990
- const yamlPath = path43.join(agentDir, "agent.yaml");
18075
+ const yamlPath = path44.join(agentDir, "agent.yaml");
17991
18076
  try {
17992
- await fs35.access(agentDir);
18077
+ await fs36.access(agentDir);
17993
18078
  } catch {
17994
18079
  return c.json({ error: "Agent not found" }, 404);
17995
18080
  }
17996
- await fs35.writeFile(yamlPath, body.yaml, "utf-8");
18081
+ await writeFileAtomic2(yamlPath, body.yaml);
17997
18082
  const { name, description, model } = parseAgentYaml(body.yaml);
17998
18083
  return c.json({ agent: { id, name, description, model, path: agentDir, scope } });
17999
18084
  });
@@ -18005,12 +18090,12 @@ ${body.description ?? ""}
18005
18090
  const projectId = c.req.query("projectId");
18006
18091
  let agentDir;
18007
18092
  if (scope === "workspace" && projectId) {
18008
- agentDir = path43.join(projectId, ".halo", "agents", id);
18093
+ agentDir = path44.join(projectId, ".halo", "agents", id);
18009
18094
  } else {
18010
- agentDir = path43.join(GLOBAL_AGENTS_DIR4, id);
18095
+ agentDir = path44.join(GLOBAL_AGENTS_DIR4, id);
18011
18096
  }
18012
18097
  try {
18013
- await fs35.access(agentDir);
18098
+ await fs36.access(agentDir);
18014
18099
  } catch {
18015
18100
  return c.json({ error: "Agent not found" }, 404);
18016
18101
  }
@@ -18020,7 +18105,7 @@ ${body.description ?? ""}
18020
18105
  return c.json({ error: "Cannot delete the last global agent. At least one global agent must remain." }, 400);
18021
18106
  }
18022
18107
  }
18023
- await fs35.rm(agentDir, { recursive: true, force: true });
18108
+ await fs36.rm(agentDir, { recursive: true, force: true });
18024
18109
  return c.json({ ok: true });
18025
18110
  });
18026
18111
  app2.patch("/agent-configs/:id/toggle", async (c) => {
@@ -18052,7 +18137,7 @@ ${body.description ?? ""}
18052
18137
  if (!filePath)
18053
18138
  return c.json({ content: "", exists: false, readOnly: !isMdWritable(fileType) });
18054
18139
  try {
18055
- const content = await fs35.readFile(filePath, "utf-8");
18140
+ const content = await fs36.readFile(filePath, "utf-8");
18056
18141
  return c.json({ content, exists: true, path: filePath, readOnly: !isMdWritable(fileType) });
18057
18142
  } catch {
18058
18143
  return c.json({ content: "", exists: false, path: filePath, readOnly: !isMdWritable(fileType) });
@@ -18085,7 +18170,7 @@ ${body.description ?? ""}
18085
18170
  continue;
18086
18171
  }
18087
18172
  try {
18088
- const content = await fs35.readFile(filePath, "utf-8");
18173
+ const content = await fs36.readFile(filePath, "utf-8");
18089
18174
  result[ft] = { content, exists: true, path: filePath, readOnly: !isMdWritable(ft) };
18090
18175
  } catch {
18091
18176
  result[ft] = { content: "", exists: false, path: filePath, readOnly: !isMdWritable(ft) };
@@ -18095,10 +18180,10 @@ ${body.description ?? ""}
18095
18180
  });
18096
18181
  function getSessionsDir(agentId, _source, projectId) {
18097
18182
  if (agentId.startsWith("__") && agentId.endsWith("__")) {
18098
- return path43.join(homedir19(), ".halo", "global", "internal-sessions", agentId);
18183
+ return path44.join(homedir19(), ".halo", "global", "internal-sessions", agentId);
18099
18184
  }
18100
- const base = projectId ? path43.join(projectId, ".halo") : path43.join(homedir19(), ".halo");
18101
- return path43.join(base, "sessions", agentId);
18185
+ const base = projectId ? path44.join(projectId, ".halo") : path44.join(homedir19(), ".halo");
18186
+ return path44.join(base, "sessions", agentId);
18102
18187
  }
18103
18188
  app2.get("/agent-configs/:id/sessions", async (c) => {
18104
18189
  const agentId = c.req.param("id");
@@ -18109,7 +18194,7 @@ ${body.description ?? ""}
18109
18194
  const dir = getSessionsDir(agentId, source, projectId || void 0);
18110
18195
  let files;
18111
18196
  try {
18112
- files = await fs35.readdir(dir);
18197
+ files = await fs36.readdir(dir);
18113
18198
  } catch {
18114
18199
  return c.json({ sessions: [] });
18115
18200
  }
@@ -18118,7 +18203,7 @@ ${body.description ?? ""}
18118
18203
  if (!f.endsWith(".json"))
18119
18204
  continue;
18120
18205
  try {
18121
- const raw = await fs35.readFile(path43.join(dir, f), "utf-8");
18206
+ const raw = await fs36.readFile(path44.join(dir, f), "utf-8");
18122
18207
  const session = JSON.parse(raw);
18123
18208
  sessions.push({
18124
18209
  id: session.id,
@@ -18147,9 +18232,9 @@ ${body.description ?? ""}
18147
18232
  const projectId = c.req.query("projectId");
18148
18233
  const source = c.req.query("source") || "test-chat";
18149
18234
  const dir = getSessionsDir(agentId, source, projectId || void 0);
18150
- const filePath = path43.join(dir, `${sessionId}.json`);
18235
+ const filePath = path44.join(dir, `${sessionId}.json`);
18151
18236
  try {
18152
- const raw = await fs35.readFile(filePath, "utf-8");
18237
+ const raw = await fs36.readFile(filePath, "utf-8");
18153
18238
  return c.json({ session: JSON.parse(raw) });
18154
18239
  } catch {
18155
18240
  return c.json({ error: "Session not found" }, 404);
@@ -18183,7 +18268,7 @@ ${body.description ?? ""}
18183
18268
  agentSnapshot: body.agentSnapshot ?? {},
18184
18269
  messages: messages2
18185
18270
  };
18186
- await fs35.writeFile(path43.join(dir, `${sessionId}.json`), JSON.stringify(session, null, 2), "utf-8");
18271
+ await fs36.writeFile(path44.join(dir, `${sessionId}.json`), JSON.stringify(session, null, 2), "utf-8");
18187
18272
  return c.json({ id: sessionId, session: { ...session, messages: void 0 } });
18188
18273
  });
18189
18274
  app2.delete("/agent-configs/:id/sessions", async (c) => {
@@ -18195,11 +18280,11 @@ ${body.description ?? ""}
18195
18280
  const dir = getSessionsDir(agentId, source, projectId || void 0);
18196
18281
  let deleted = 0;
18197
18282
  try {
18198
- const files = await fs35.readdir(dir);
18283
+ const files = await fs36.readdir(dir);
18199
18284
  for (const f of files) {
18200
18285
  if (!f.endsWith(".json"))
18201
18286
  continue;
18202
- await fs35.rm(path43.join(dir, f));
18287
+ await fs36.rm(path44.join(dir, f));
18203
18288
  deleted++;
18204
18289
  }
18205
18290
  } catch {
@@ -18216,7 +18301,7 @@ ${body.description ?? ""}
18216
18301
  const source = c.req.query("source") || "test-chat";
18217
18302
  const dir = getSessionsDir(agentId, source, projectId || void 0);
18218
18303
  try {
18219
- await fs35.rm(path43.join(dir, `${sessionId}.json`));
18304
+ await fs36.rm(path44.join(dir, `${sessionId}.json`));
18220
18305
  return c.json({ ok: true });
18221
18306
  } catch {
18222
18307
  return c.json({ error: "Session not found" }, 404);
@@ -18236,7 +18321,7 @@ var init_agent_configs = __esm({
18236
18321
  init_workspace_path();
18237
18322
  init_mtime_cache();
18238
18323
  _cachedTools = null;
18239
- GLOBAL_AGENTS_DIR4 = path43.join(homedir19(), ".halo", "global", "agents");
18324
+ GLOBAL_AGENTS_DIR4 = path44.join(homedir19(), ".halo", "global", "agents");
18240
18325
  FALLBACK_SCAFFOLD_PROVIDER = "aws-bedrock-claude-invoke";
18241
18326
  _yamlCache = createMtimeCache();
18242
18327
  }
@@ -18244,8 +18329,8 @@ var init_agent_configs = __esm({
18244
18329
 
18245
18330
  // packages/server/dist/routes/skills.js
18246
18331
  import { Hono as Hono5 } from "hono";
18247
- import fs36 from "node:fs/promises";
18248
- import path44 from "node:path";
18332
+ import fs37 from "node:fs/promises";
18333
+ import path45 from "node:path";
18249
18334
  import { homedir as homedir20 } from "node:os";
18250
18335
  import YAML11 from "yaml";
18251
18336
  function defaultSkillSettings() {
@@ -18264,20 +18349,20 @@ function defaultSkillSettings() {
18264
18349
  }
18265
18350
  async function readYamlFile3(filePath) {
18266
18351
  try {
18267
- const content = await fs36.readFile(filePath, "utf-8");
18352
+ const content = await fs37.readFile(filePath, "utf-8");
18268
18353
  return YAML11.parse(content) ?? {};
18269
18354
  } catch {
18270
18355
  try {
18271
18356
  const jsonPath = filePath.replace(/\.yaml$/, ".json");
18272
- return JSON.parse(await fs36.readFile(jsonPath, "utf-8"));
18357
+ return JSON.parse(await fs37.readFile(jsonPath, "utf-8"));
18273
18358
  } catch {
18274
18359
  return {};
18275
18360
  }
18276
18361
  }
18277
18362
  }
18278
18363
  async function writeYamlFile(filePath, data) {
18279
- await fs36.mkdir(path44.dirname(filePath), { recursive: true });
18280
- await fs36.writeFile(filePath, YAML11.stringify(data, { lineWidth: 120 }), "utf-8");
18364
+ await fs37.mkdir(path45.dirname(filePath), { recursive: true });
18365
+ await fs37.writeFile(filePath, YAML11.stringify(data, { lineWidth: 120 }), "utf-8");
18281
18366
  }
18282
18367
  async function addSkillSettings(settingsPath, skillId) {
18283
18368
  const settings = await readYamlFile3(settingsPath);
@@ -18300,7 +18385,7 @@ async function removeSkillSettings(settingsPath, skillId) {
18300
18385
  }
18301
18386
  }
18302
18387
  async function ensureDir3(dir) {
18303
- await fs36.mkdir(dir, { recursive: true });
18388
+ await fs37.mkdir(dir, { recursive: true });
18304
18389
  }
18305
18390
  function toSkillMd(name, description) {
18306
18391
  return `---
@@ -18317,32 +18402,32 @@ async function scanSkillsDir(dir, scope) {
18317
18402
  const skills = [];
18318
18403
  let names;
18319
18404
  try {
18320
- names = await fs36.readdir(dir);
18405
+ names = await fs37.readdir(dir);
18321
18406
  } catch {
18322
18407
  return skills;
18323
18408
  }
18324
18409
  for (const entryName of names) {
18325
- const skillDir = path44.join(dir, entryName);
18410
+ const skillDir = path45.join(dir, entryName);
18326
18411
  try {
18327
- const stat = await fs36.stat(skillDir);
18412
+ const stat = await fs37.stat(skillDir);
18328
18413
  if (!stat.isDirectory())
18329
18414
  continue;
18330
18415
  } catch {
18331
18416
  continue;
18332
18417
  }
18333
- const skillMdPath = path44.join(skillDir, "SKILL.md");
18418
+ const skillMdPath = path45.join(skillDir, "SKILL.md");
18334
18419
  try {
18335
- const mdStat = await fs36.stat(skillMdPath);
18420
+ const mdStat = await fs37.stat(skillMdPath);
18336
18421
  let meta = _skillMdCache.get(skillMdPath, mdStat.mtimeMs);
18337
18422
  if (!meta) {
18338
- const { name, description, command } = parseSkillFrontmatter(await fs36.readFile(skillMdPath, "utf-8"));
18423
+ const { name, description, command } = parseSkillFrontmatter(await fs37.readFile(skillMdPath, "utf-8"));
18339
18424
  meta = _skillMdCache.set(skillMdPath, mdStat.mtimeMs, { name: name || entryName, description: description ?? "", command });
18340
18425
  }
18341
18426
  skills.push({ id: entryName, name: meta.name, description: meta.description, path: skillDir, scope, command: meta.command });
18342
18427
  } catch {
18343
- const yamlPath = path44.join(skillDir, "skill.yaml");
18428
+ const yamlPath = path45.join(skillDir, "skill.yaml");
18344
18429
  try {
18345
- const yamlContent = await fs36.readFile(yamlPath, "utf-8");
18430
+ const yamlContent = await fs37.readFile(yamlPath, "utf-8");
18346
18431
  const m = yamlContent.match(/^name:\s*(.+)$/m);
18347
18432
  const d = yamlContent.match(/^description:\s*(.+)$/m);
18348
18433
  skills.push({ id: entryName, name: m?.[1]?.trim() || entryName, description: d?.[1]?.trim() || "", path: skillDir, scope });
@@ -18361,7 +18446,7 @@ function createSkillRoutes() {
18361
18446
  const projectId = c.req.query("projectId");
18362
18447
  let workspaceSkills = [];
18363
18448
  if (projectId) {
18364
- const wsSkillsDir = path44.join(projectId, ".halo", "skills");
18449
+ const wsSkillsDir = path45.join(projectId, ".halo", "skills");
18365
18450
  workspaceSkills = await scanSkillsDir(wsSkillsDir, "workspace");
18366
18451
  }
18367
18452
  const wsIds = new Set(workspaceSkills.map((s) => s.id));
@@ -18384,19 +18469,19 @@ function createSkillRoutes() {
18384
18469
  if (scope === "workspace") {
18385
18470
  if (!body.projectId)
18386
18471
  return c.json({ error: "projectId required for workspace skills" }, 400);
18387
- baseDir = path44.join(body.projectId, ".halo", "skills");
18472
+ baseDir = path45.join(body.projectId, ".halo", "skills");
18388
18473
  } else {
18389
18474
  baseDir = GLOBAL_SKILLS_DIR2;
18390
18475
  }
18391
- const skillDir = path44.join(baseDir, id);
18476
+ const skillDir = path45.join(baseDir, id);
18392
18477
  try {
18393
- await fs36.access(skillDir);
18478
+ await fs37.access(skillDir);
18394
18479
  return c.json({ error: "Skill already exists" }, 409);
18395
18480
  } catch {
18396
18481
  }
18397
18482
  await ensureDir3(skillDir);
18398
- await fs36.writeFile(path44.join(skillDir, "SKILL.md"), toSkillMd(body.name, body.description ?? ""), "utf-8");
18399
- const settingsPath = scope === "workspace" && body.projectId ? path44.join(body.projectId, ".halo", "settings.yaml") : GLOBAL_SETTINGS_PATH;
18483
+ await fs37.writeFile(path45.join(skillDir, "SKILL.md"), toSkillMd(body.name, body.description ?? ""), "utf-8");
18484
+ const settingsPath = scope === "workspace" && body.projectId ? path45.join(body.projectId, ".halo", "settings.yaml") : GLOBAL_SETTINGS_PATH;
18400
18485
  await addSkillSettings(settingsPath, id);
18401
18486
  const skill = { id, name: body.name, description: body.description ?? "", path: skillDir, scope };
18402
18487
  return c.json({ skill }, 201);
@@ -18409,17 +18494,17 @@ function createSkillRoutes() {
18409
18494
  const projectId = c.req.query("projectId");
18410
18495
  let skillDir;
18411
18496
  if (scope === "workspace" && projectId) {
18412
- skillDir = path44.join(projectId, ".halo", "skills", id);
18497
+ skillDir = path45.join(projectId, ".halo", "skills", id);
18413
18498
  } else {
18414
- skillDir = path44.join(GLOBAL_SKILLS_DIR2, id);
18499
+ skillDir = path45.join(GLOBAL_SKILLS_DIR2, id);
18415
18500
  }
18416
18501
  try {
18417
- await fs36.access(skillDir);
18502
+ await fs37.access(skillDir);
18418
18503
  } catch {
18419
18504
  return c.json({ error: "Skill not found" }, 404);
18420
18505
  }
18421
- await fs36.rm(skillDir, { recursive: true, force: true });
18422
- const settingsPath = scope === "workspace" && projectId ? path44.join(projectId, ".halo", "settings.yaml") : GLOBAL_SETTINGS_PATH;
18506
+ await fs37.rm(skillDir, { recursive: true, force: true });
18507
+ const settingsPath = scope === "workspace" && projectId ? path45.join(projectId, ".halo", "settings.yaml") : GLOBAL_SETTINGS_PATH;
18423
18508
  await removeSkillSettings(settingsPath, id);
18424
18509
  return c.json({ ok: true });
18425
18510
  });
@@ -18443,30 +18528,30 @@ var init_skills = __esm({
18443
18528
  init_db();
18444
18529
  init_workspace_path();
18445
18530
  init_mtime_cache();
18446
- GLOBAL_SETTINGS_PATH = path44.join(homedir20(), ".halo", "secrets", "settings.yaml");
18531
+ GLOBAL_SETTINGS_PATH = path45.join(homedir20(), ".halo", "secrets", "settings.yaml");
18447
18532
  _skillMdCache = createMtimeCache();
18448
18533
  }
18449
18534
  });
18450
18535
 
18451
18536
  // packages/server/dist/routes/settings.js
18452
18537
  import { Hono as Hono6 } from "hono";
18453
- import fs37 from "node:fs/promises";
18454
- import path45 from "node:path";
18538
+ import fs38 from "node:fs/promises";
18539
+ import path46 from "node:path";
18455
18540
  import { homedir as homedir21 } from "node:os";
18456
18541
  import YAML12 from "yaml";
18457
18542
  async function readSettingsFile(filePath) {
18458
18543
  try {
18459
- const content = await fs37.readFile(filePath, "utf-8");
18544
+ const content = await fs38.readFile(filePath, "utf-8");
18460
18545
  return YAML12.parse(content) ?? {};
18461
18546
  } catch {
18462
18547
  return {};
18463
18548
  }
18464
18549
  }
18465
18550
  async function writeSettingsFile(filePath, data) {
18466
- await fs37.mkdir(path45.dirname(filePath), { recursive: true });
18467
- await fs37.writeFile(filePath, YAML12.stringify(data, { lineWidth: 120 }), { encoding: "utf-8", mode: 384 });
18551
+ await fs38.mkdir(path46.dirname(filePath), { recursive: true });
18552
+ await fs38.writeFile(filePath, YAML12.stringify(data, { lineWidth: 120 }), { encoding: "utf-8", mode: 384 });
18468
18553
  try {
18469
- await fs37.chmod(filePath, 384);
18554
+ await fs38.chmod(filePath, 384);
18470
18555
  } catch {
18471
18556
  }
18472
18557
  }
@@ -18580,8 +18665,8 @@ function detectOrphans(tree, declared) {
18580
18665
  if (!block || typeof block !== "object")
18581
18666
  continue;
18582
18667
  for (const k of Object.keys(block)) {
18583
- const path65 = `${namespaceId}.${kindWord}.${k}`;
18584
- if (!declaredMap.has(path65)) {
18668
+ const path66 = `${namespaceId}.${kindWord}.${k}`;
18669
+ if (!declaredMap.has(path66)) {
18585
18670
  out.push({ namespaceId, kind: kindWord === "params" ? "param" : "secret", key: k });
18586
18671
  }
18587
18672
  }
@@ -18596,7 +18681,7 @@ function createSettingsRoutes() {
18596
18681
  const globalTree = await readSettingsFile(GLOBAL_SETTINGS_PATH2);
18597
18682
  let workspaceTree = null;
18598
18683
  if (projectId) {
18599
- workspaceTree = await readSettingsFile(path45.join(projectId, ".halo", "settings.yaml"));
18684
+ workspaceTree = await readSettingsFile(path46.join(projectId, ".halo", "settings.yaml"));
18600
18685
  }
18601
18686
  const declared = loadSettingsSchema(projectId ?? void 0);
18602
18687
  const sections = resolveSections(declared, globalTree, workspaceTree);
@@ -18646,7 +18731,7 @@ function createSettingsRoutes() {
18646
18731
  const offending = rejectGlobalOnlyAtWorkspace("workspace", flattenDotted(body.settings));
18647
18732
  if (offending)
18648
18733
  return c.json({ error: `${offending} is global-only and cannot be set per workspace` }, 400);
18649
- const wsPath = path45.join(projectPath, ".halo", "settings.yaml");
18734
+ const wsPath = path46.join(projectPath, ".halo", "settings.yaml");
18650
18735
  await writeSettingsFile(wsPath, body.settings);
18651
18736
  } else {
18652
18737
  await writeSettingsFile(GLOBAL_SETTINGS_PATH2, body.settings);
@@ -18666,7 +18751,7 @@ function createSettingsRoutes() {
18666
18751
  const offending = rejectGlobalOnlyAtWorkspace("workspace", [body.key]);
18667
18752
  if (offending)
18668
18753
  return c.json({ error: `${offending} is global-only and cannot be set per workspace` }, 400);
18669
- filePath = path45.join(projectPath, ".halo", "settings.yaml");
18754
+ filePath = path46.join(projectPath, ".halo", "settings.yaml");
18670
18755
  } else {
18671
18756
  filePath = GLOBAL_SETTINGS_PATH2;
18672
18757
  }
@@ -18695,7 +18780,7 @@ function createSettingsRoutes() {
18695
18780
  const projectPath = await resolveProjectPath(body.projectId);
18696
18781
  if (!projectPath)
18697
18782
  return c.json({ error: "Project not found" }, 404);
18698
- filePath = path45.join(projectPath, ".halo", "settings.yaml");
18783
+ filePath = path46.join(projectPath, ".halo", "settings.yaml");
18699
18784
  } else {
18700
18785
  filePath = GLOBAL_SETTINGS_PATH2;
18701
18786
  }
@@ -18721,39 +18806,39 @@ var init_settings = __esm({
18721
18806
  "use strict";
18722
18807
  init_settings_schema();
18723
18808
  init_workspace_path();
18724
- GLOBAL_SETTINGS_PATH2 = path45.join(homedir21(), ".halo", "secrets", "settings.yaml");
18809
+ GLOBAL_SETTINGS_PATH2 = path46.join(homedir21(), ".halo", "secrets", "settings.yaml");
18725
18810
  _changeListeners = [];
18726
18811
  }
18727
18812
  });
18728
18813
 
18729
18814
  // packages/server/dist/evolution/archive.js
18730
- import fs38 from "node:fs";
18731
- import path46 from "node:path";
18815
+ import fs39 from "node:fs";
18816
+ import path47 from "node:path";
18732
18817
  import { pipeline } from "node:stream/promises";
18733
18818
  import JSZip from "jszip";
18734
18819
  import { eq as eq7, and as and5, isNull as isNull4, lte, isNotNull as isNotNull3, lt as lt2 } from "drizzle-orm";
18735
18820
  async function zipDir(srcDir, outZip) {
18736
- if (!fs38.existsSync(srcDir))
18821
+ if (!fs39.existsSync(srcDir))
18737
18822
  return false;
18738
- fs38.mkdirSync(path46.dirname(outZip), { recursive: true });
18823
+ fs39.mkdirSync(path47.dirname(outZip), { recursive: true });
18739
18824
  const zip = new JSZip();
18740
18825
  const walk = (dir, rel) => {
18741
- const entries = fs38.readdirSync(dir, { withFileTypes: true });
18826
+ const entries = fs39.readdirSync(dir, { withFileTypes: true });
18742
18827
  if (entries.length === 0 && rel) {
18743
18828
  zip.folder(rel);
18744
18829
  return;
18745
18830
  }
18746
18831
  for (const e of entries) {
18747
- const abs = path46.join(dir, e.name);
18832
+ const abs = path47.join(dir, e.name);
18748
18833
  const childRel = rel ? `${rel}/${e.name}` : e.name;
18749
18834
  if (e.isDirectory()) {
18750
18835
  walk(abs, childRel);
18751
18836
  } else if (e.isFile()) {
18752
- zip.file(childRel, fs38.readFileSync(abs), { date: fs38.statSync(abs).mtime });
18837
+ zip.file(childRel, fs39.readFileSync(abs), { date: fs39.statSync(abs).mtime });
18753
18838
  } else if (e.isSymbolicLink()) {
18754
18839
  try {
18755
- if (fs38.statSync(abs).isFile())
18756
- zip.file(childRel, fs38.readFileSync(abs));
18840
+ if (fs39.statSync(abs).isFile())
18841
+ zip.file(childRel, fs39.readFileSync(abs));
18757
18842
  } catch {
18758
18843
  }
18759
18844
  }
@@ -18761,7 +18846,7 @@ async function zipDir(srcDir, outZip) {
18761
18846
  };
18762
18847
  try {
18763
18848
  walk(srcDir, "");
18764
- await pipeline(zip.generateNodeStream({ type: "nodebuffer", streamFiles: true, compression: "DEFLATE" }), fs38.createWriteStream(outZip));
18849
+ await pipeline(zip.generateNodeStream({ type: "nodebuffer", streamFiles: true, compression: "DEFLATE" }), fs39.createWriteStream(outZip));
18765
18850
  return true;
18766
18851
  } catch (err) {
18767
18852
  rmFile(outZip);
@@ -18771,20 +18856,20 @@ async function zipDir(srcDir, outZip) {
18771
18856
  }
18772
18857
  function rmDir(dir) {
18773
18858
  try {
18774
- fs38.rmSync(dir, { recursive: true, force: true });
18859
+ fs39.rmSync(dir, { recursive: true, force: true });
18775
18860
  } catch {
18776
18861
  }
18777
18862
  }
18778
18863
  function rmFile(p) {
18779
18864
  try {
18780
- fs38.rmSync(p, { force: true });
18865
+ fs39.rmSync(p, { force: true });
18781
18866
  } catch {
18782
18867
  }
18783
18868
  }
18784
18869
  async function archiveRun(workspacePath, runId, errors) {
18785
18870
  const runDir = wsEvoRunDir(workspacePath, runId);
18786
18871
  const outZip = wsEvoArchivedRunZip(workspacePath, runId);
18787
- if (!fs38.existsSync(runDir)) {
18872
+ if (!fs39.existsSync(runDir)) {
18788
18873
  return true;
18789
18874
  }
18790
18875
  if (!await zipDir(runDir, outZip)) {
@@ -18797,7 +18882,7 @@ async function archiveRun(workspacePath, runId, errors) {
18797
18882
  async function archiveApply(workspacePath, applyId, errors) {
18798
18883
  const applyDir = wsEvoApplyDir(workspacePath, applyId);
18799
18884
  const outZip = wsEvoArchivedApplyZip(workspacePath, applyId);
18800
- if (!fs38.existsSync(applyDir))
18885
+ if (!fs39.existsSync(applyDir))
18801
18886
  return true;
18802
18887
  if (!await zipDir(applyDir, outZip)) {
18803
18888
  errors.push(`zip failed for apply ${applyId}`);
@@ -18924,8 +19009,8 @@ var init_raw_sqlite = __esm({
18924
19009
  // packages/server/dist/routes/evolution.js
18925
19010
  import { Hono as Hono7 } from "hono";
18926
19011
  import { eq as eq8, desc as desc2, lt as lt3, and as and6, or, sql } from "drizzle-orm";
18927
- import fs39 from "node:fs/promises";
18928
- import path47 from "node:path";
19012
+ import fs40 from "node:fs/promises";
19013
+ import path48 from "node:path";
18929
19014
  function createEvolutionRoutes() {
18930
19015
  const router = new Hono7();
18931
19016
  router.get("/evolution/runs", async (c) => {
@@ -18990,33 +19075,33 @@ function createEvolutionRoutes() {
18990
19075
  const dir = wsEvoRunDir(row.workspacePath, id);
18991
19076
  let patchMd = null;
18992
19077
  try {
18993
- patchMd = await fs39.readFile(path47.join(dir, "patch.md"), "utf-8");
19078
+ patchMd = await fs40.readFile(path48.join(dir, "patch.md"), "utf-8");
18994
19079
  } catch {
18995
19080
  }
18996
19081
  let scoreJson = null;
18997
19082
  try {
18998
- const raw = await fs39.readFile(path47.join(dir, "score.json"), "utf-8");
19083
+ const raw = await fs40.readFile(path48.join(dir, "score.json"), "utf-8");
18999
19084
  scoreJson = JSON.parse(raw);
19000
19085
  } catch {
19001
19086
  }
19002
19087
  let wrapperLog = null;
19003
19088
  try {
19004
- wrapperLog = await fs39.readFile(evoWrapperLogFile(id), "utf-8");
19089
+ wrapperLog = await fs40.readFile(evoWrapperLogFile(id), "utf-8");
19005
19090
  } catch {
19006
19091
  }
19007
19092
  let subCliLog = null;
19008
19093
  try {
19009
- subCliLog = await fs39.readFile(path47.join(dir, "sub-cli.log"), "utf-8");
19094
+ subCliLog = await fs40.readFile(path48.join(dir, "sub-cli.log"), "utf-8");
19010
19095
  } catch {
19011
19096
  }
19012
19097
  let skipReasonMd = null;
19013
19098
  try {
19014
- skipReasonMd = await fs39.readFile(path47.join(dir, ".skip.md"), "utf-8");
19099
+ skipReasonMd = await fs40.readFile(path48.join(dir, ".skip.md"), "utf-8");
19015
19100
  } catch {
19016
19101
  }
19017
19102
  let snapshotSummary = null;
19018
19103
  try {
19019
- const raw = await fs39.readFile(path47.join(dir, "source-snapshot.json"), "utf-8");
19104
+ const raw = await fs40.readFile(path48.join(dir, "source-snapshot.json"), "utf-8");
19020
19105
  const parsed = JSON.parse(raw);
19021
19106
  const msgs = parsed.rawMessages ?? [];
19022
19107
  const firstUser = msgs.find((m) => m.role === "user");
@@ -19504,12 +19589,12 @@ var init_session_archive2 = __esm({
19504
19589
  import Database4 from "better-sqlite3";
19505
19590
  import { drizzle as drizzle3 } from "drizzle-orm/better-sqlite3";
19506
19591
  import { sqliteTable as sqliteTable3, text as text3, integer as integer3 } from "drizzle-orm/sqlite-core";
19507
- import path48 from "node:path";
19508
- import fs40 from "node:fs";
19592
+ import path49 from "node:path";
19593
+ import fs41 from "node:fs";
19509
19594
  function createChannelDb(secretsDir) {
19510
- const channelsDir = path48.join(secretsDir, "channels");
19511
- fs40.mkdirSync(channelsDir, { recursive: true });
19512
- const dbPath = path48.join(channelsDir, "channels.db");
19595
+ const channelsDir = path49.join(secretsDir, "channels");
19596
+ fs41.mkdirSync(channelsDir, { recursive: true });
19597
+ const dbPath = path49.join(channelsDir, "channels.db");
19513
19598
  const sqlite = new Database4(dbPath);
19514
19599
  sqlite.pragma("journal_mode = WAL");
19515
19600
  sqlite.exec(CREATE_SQL2);
@@ -19559,11 +19644,11 @@ CREATE INDEX IF NOT EXISTS idx_channel_accounts_type ON channel_accounts(channel
19559
19644
  });
19560
19645
 
19561
19646
  // packages/server/dist/channels/shared/accounts.js
19562
- import fs41 from "node:fs";
19563
- import path49 from "node:path";
19647
+ import fs42 from "node:fs";
19648
+ import path50 from "node:path";
19564
19649
  import { eq as eq10, and as and7 } from "drizzle-orm";
19565
19650
  function resolveAccountWorkspace(account) {
19566
- if (!fs41.existsSync(account.workspacePath))
19651
+ if (!fs42.existsSync(account.workspacePath))
19567
19652
  return null;
19568
19653
  ensureWorkspaceHalo(account.workspacePath);
19569
19654
  return account.workspacePath;
@@ -19576,9 +19661,9 @@ function accessLevelError(value, allowed) {
19576
19661
  return `accessLevel must be one of: ${allowed.join(", ")}`;
19577
19662
  }
19578
19663
  function validateWorkspaceBody(workspacePath) {
19579
- if (!path49.isAbsolute(workspacePath))
19664
+ if (!path50.isAbsolute(workspacePath))
19580
19665
  return "workspacePath must be absolute";
19581
- if (!fs41.existsSync(workspacePath))
19666
+ if (!fs42.existsSync(workspacePath))
19582
19667
  return "workspace path not found";
19583
19668
  ensureWorkspaceHalo(workspacePath);
19584
19669
  return null;
@@ -19878,22 +19963,22 @@ var init_session_types = __esm({
19878
19963
 
19879
19964
  // packages/server/dist/routes/halo-city.js
19880
19965
  import { Hono as Hono10 } from "hono";
19881
- import fs42 from "node:fs";
19882
- import path50 from "node:path";
19966
+ import fs43 from "node:fs";
19967
+ import path51 from "node:path";
19883
19968
  import Database5 from "better-sqlite3";
19884
19969
  function scanSkills2(dir) {
19885
19970
  let names;
19886
19971
  try {
19887
- names = fs42.readdirSync(dir);
19972
+ names = fs43.readdirSync(dir);
19888
19973
  } catch {
19889
19974
  return [];
19890
19975
  }
19891
19976
  const out = [];
19892
19977
  for (const entry of names) {
19893
- const skillMd = path50.join(dir, entry, "SKILL.md");
19978
+ const skillMd = path51.join(dir, entry, "SKILL.md");
19894
19979
  let stat;
19895
19980
  try {
19896
- stat = fs42.statSync(skillMd);
19981
+ stat = fs43.statSync(skillMd);
19897
19982
  if (!stat.isFile())
19898
19983
  continue;
19899
19984
  } catch {
@@ -19902,7 +19987,7 @@ function scanSkills2(dir) {
19902
19987
  let meta = _skillCache.get(skillMd, stat.mtimeMs);
19903
19988
  if (!meta) {
19904
19989
  try {
19905
- const { name, description, command } = parseSkillFrontmatter(fs42.readFileSync(skillMd, "utf-8"));
19990
+ const { name, description, command } = parseSkillFrontmatter(fs43.readFileSync(skillMd, "utf-8"));
19906
19991
  meta = _skillCache.set(skillMd, stat.mtimeMs, { id: entry, name: name || entry, description: description ?? "", command });
19907
19992
  } catch {
19908
19993
  continue;
@@ -19988,10 +20073,10 @@ function snapshotWorkspace(sm, wsPath, label) {
19988
20073
  messageCount: meta.messageCount
19989
20074
  };
19990
20075
  });
19991
- const wsSkills = scanSkills2(path50.join(wsPath, ".halo", "skills"));
20076
+ const wsSkills = scanSkills2(path51.join(wsPath, ".halo", "skills"));
19992
20077
  return {
19993
20078
  path: wsPath,
19994
- key: path50.basename(wsPath) || wsPath,
20079
+ key: path51.basename(wsPath) || wsPath,
19995
20080
  label,
19996
20081
  sessions,
19997
20082
  counts,
@@ -20002,15 +20087,15 @@ function snapshotWorkspace(sm, wsPath, label) {
20002
20087
  function roReader(rawPath) {
20003
20088
  let wsPath;
20004
20089
  try {
20005
- wsPath = fs42.realpathSync(rawPath);
20090
+ wsPath = fs43.realpathSync(rawPath);
20006
20091
  } catch {
20007
20092
  return null;
20008
20093
  }
20009
20094
  const cached = _roReaders.get(wsPath);
20010
20095
  if (cached)
20011
20096
  return cached;
20012
- const dbPath = path50.join(wsPath, ".halo", "halo.db");
20013
- if (!fs42.existsSync(dbPath))
20097
+ const dbPath = path51.join(wsPath, ".halo", "halo.db");
20098
+ if (!fs43.existsSync(dbPath))
20014
20099
  return null;
20015
20100
  try {
20016
20101
  const db = new Database5(dbPath, { readonly: true, fileMustExist: true });
@@ -20038,7 +20123,7 @@ function dropRoReader(rawPath) {
20038
20123
  return;
20039
20124
  let wsPath = rawPath;
20040
20125
  try {
20041
- wsPath = fs42.realpathSync(rawPath);
20126
+ wsPath = fs43.realpathSync(rawPath);
20042
20127
  } catch {
20043
20128
  }
20044
20129
  const r = _roReaders.get(wsPath);
@@ -20086,12 +20171,12 @@ function snapshotWorkspaceReadonly(wsPath, label) {
20086
20171
  });
20087
20172
  return {
20088
20173
  path: wsPath,
20089
- key: path50.basename(wsPath) || wsPath,
20174
+ key: path51.basename(wsPath) || wsPath,
20090
20175
  label,
20091
20176
  sessions,
20092
20177
  counts,
20093
20178
  totalSessions: counts.total,
20094
- skills: scanSkills2(path50.join(wsPath, ".halo", "skills"))
20179
+ skills: scanSkills2(path51.join(wsPath, ".halo", "skills"))
20095
20180
  };
20096
20181
  }
20097
20182
  function readonlySessionCounts(wsPath, limit) {
@@ -20135,7 +20220,7 @@ function workspaceBirth(wsPath) {
20135
20220
  let b = _birthCache.get(wsPath);
20136
20221
  if (b === void 0) {
20137
20222
  try {
20138
- b = fs42.statSync(wsPath).birthtimeMs || fs42.statSync(wsPath).ctimeMs;
20223
+ b = fs43.statSync(wsPath).birthtimeMs || fs43.statSync(wsPath).ctimeMs;
20139
20224
  } catch {
20140
20225
  b = 0;
20141
20226
  }
@@ -20147,18 +20232,18 @@ function discoverWorkspaces(registry2) {
20147
20232
  const out = /* @__PURE__ */ new Map();
20148
20233
  const add = (p, label) => {
20149
20234
  try {
20150
- const resolved = fs42.realpathSync(p);
20235
+ const resolved = fs43.realpathSync(p);
20151
20236
  if (!out.has(resolved))
20152
20237
  out.set(resolved, label);
20153
20238
  } catch {
20154
20239
  }
20155
20240
  };
20156
20241
  for (const { workspacePath } of registry2.list())
20157
- add(workspacePath, path50.basename(workspacePath));
20242
+ add(workspacePath, path51.basename(workspacePath));
20158
20243
  try {
20159
20244
  const rows = getChannelDb().select().from(channelAccounts).all();
20160
20245
  for (const row of rows)
20161
- add(row.workspacePath, path50.basename(row.workspacePath));
20246
+ add(row.workspacePath, path51.basename(row.workspacePath));
20162
20247
  } catch {
20163
20248
  }
20164
20249
  return out;
@@ -20177,7 +20262,7 @@ function createShowRoutes(registry2) {
20177
20262
  return a.response;
20178
20263
  const { account } = a;
20179
20264
  const globalView = account.accessLevel === "full" || account.accessLevel === "observer";
20180
- const targets = globalView ? discoverWorkspaces(registry2) : /* @__PURE__ */ new Map([[account.workspacePath, path50.basename(account.workspacePath)]]);
20265
+ const targets = globalView ? discoverWorkspaces(registry2) : /* @__PURE__ */ new Map([[account.workspacePath, path51.basename(account.workspacePath)]]);
20181
20266
  const workspaces = [];
20182
20267
  for (const [wsPath, label] of targets) {
20183
20268
  try {
@@ -20211,7 +20296,7 @@ function createShowRoutes(registry2) {
20211
20296
  if (!globalView) {
20212
20297
  let allowed = false;
20213
20298
  try {
20214
- allowed = fs42.realpathSync(wsPath) === fs42.realpathSync(account.workspacePath);
20299
+ allowed = fs43.realpathSync(wsPath) === fs43.realpathSync(account.workspacePath);
20215
20300
  } catch {
20216
20301
  }
20217
20302
  if (!allowed)
@@ -20405,14 +20490,14 @@ var init_commands3 = __esm({
20405
20490
 
20406
20491
  // packages/server/dist/routes/agentcore.js
20407
20492
  import { Hono as Hono13 } from "hono";
20408
- import path51 from "node:path";
20409
- import fs43 from "node:fs";
20493
+ import path52 from "node:path";
20494
+ import fs44 from "node:fs";
20410
20495
  function haloSessionId(runtimeSessionId) {
20411
20496
  return `agentcore_${runtimeSessionId.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 80)}`;
20412
20497
  }
20413
20498
  function userWorkspace(base, runtimeSessionId) {
20414
- const dir = path51.join(base, "users", runtimeSessionId.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 80));
20415
- fs43.mkdirSync(dir, { recursive: true });
20499
+ const dir = path52.join(base, "users", runtimeSessionId.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 80));
20500
+ fs44.mkdirSync(dir, { recursive: true });
20416
20501
  return dir;
20417
20502
  }
20418
20503
  async function ensureSession(sm, workspace, sessionId) {
@@ -20669,8 +20754,8 @@ var init_agentcore = __esm({
20669
20754
  });
20670
20755
 
20671
20756
  // packages/server/dist/ws/file-watcher.js
20672
- import fs44 from "node:fs";
20673
- import path52 from "node:path";
20757
+ import fs45 from "node:fs";
20758
+ import path53 from "node:path";
20674
20759
  function enqueueNativeOp(op) {
20675
20760
  const result = nativeOpChain.then(op);
20676
20761
  nativeOpChain = result.catch(() => {
@@ -20678,12 +20763,12 @@ function enqueueNativeOp(op) {
20678
20763
  return result;
20679
20764
  }
20680
20765
  function isUnwatchablePath(absPath) {
20681
- const norm = path52.resolve(absPath);
20682
- if (norm === "/" || norm === path52.parse(norm).root)
20766
+ const norm = path53.resolve(absPath);
20767
+ if (norm === "/" || norm === path53.parse(norm).root)
20683
20768
  return "filesystem root";
20684
20769
  const banned = ["/System", "/Library", "/private", "/usr", "/bin", "/sbin", "/etc"];
20685
20770
  for (const b of banned) {
20686
- if (norm === b || norm.startsWith(b + path52.sep))
20771
+ if (norm === b || norm.startsWith(b + path53.sep))
20687
20772
  return `system path ${b}`;
20688
20773
  }
20689
20774
  return null;
@@ -20779,7 +20864,7 @@ var init_file_watcher = __esm({
20779
20864
  }
20780
20865
  let root = workspaceRoot;
20781
20866
  try {
20782
- root = fs44.realpathSync(workspaceRoot);
20867
+ root = fs45.realpathSync(workspaceRoot);
20783
20868
  } catch {
20784
20869
  }
20785
20870
  if (this.subscription && this.workspaceRoot === root)
@@ -20823,10 +20908,10 @@ var init_file_watcher = __esm({
20823
20908
  const root = this.workspaceRoot;
20824
20909
  if (!root)
20825
20910
  return;
20826
- const raw = path52.relative(root, absPath);
20911
+ const raw = path53.relative(root, absPath);
20827
20912
  if (!raw || raw.startsWith(".."))
20828
20913
  return;
20829
- const rel = raw.split(path52.sep).join("/");
20914
+ const rel = raw.split(path53.sep).join("/");
20830
20915
  if (IS_WIN32 && rel.split("/").some((seg) => IGNORED_SEGMENT_SET.has(seg)))
20831
20916
  return;
20832
20917
  let action;
@@ -20837,7 +20922,7 @@ var init_file_watcher = __esm({
20837
20922
  } else {
20838
20923
  let isDir = false;
20839
20924
  try {
20840
- isDir = fs44.statSync(absPath).isDirectory();
20925
+ isDir = fs45.statSync(absPath).isDirectory();
20841
20926
  } catch {
20842
20927
  }
20843
20928
  action = isDir ? "addDir" : "add";
@@ -20913,8 +20998,8 @@ var init_file_watcher = __esm({
20913
20998
  });
20914
20999
 
20915
21000
  // packages/server/dist/ws/git-dir-watcher.js
20916
- import fs45 from "node:fs";
20917
- import path53 from "node:path";
21001
+ import fs46 from "node:fs";
21002
+ import path54 from "node:path";
20918
21003
  var GitDirWatcher;
20919
21004
  var init_git_dir_watcher = __esm({
20920
21005
  "packages/server/dist/ws/git-dir-watcher.js"() {
@@ -20938,8 +21023,8 @@ var init_git_dir_watcher = __esm({
20938
21023
  return;
20939
21024
  this.stop();
20940
21025
  this.workspaceRoot = workspaceRoot;
20941
- const gitDir = path53.join(workspaceRoot, ".git");
20942
- if (fs45.existsSync(gitDir)) {
21026
+ const gitDir = path54.join(workspaceRoot, ".git");
21027
+ if (fs46.existsSync(gitDir)) {
20943
21028
  this.watchGitDir(gitDir);
20944
21029
  } else {
20945
21030
  this.watchParentForGit(workspaceRoot);
@@ -20948,7 +21033,7 @@ var init_git_dir_watcher = __esm({
20948
21033
  /** Watch the `.git` dir for index/HEAD changes (steady state). */
20949
21034
  watchGitDir(gitDir) {
20950
21035
  try {
20951
- this.watcher = fs45.watch(gitDir, { persistent: true, recursive: false }, (_eventType, filename) => {
21036
+ this.watcher = fs46.watch(gitDir, { persistent: true, recursive: false }, (_eventType, filename) => {
20952
21037
  this.handleGitEvent(filename);
20953
21038
  });
20954
21039
  this.watcher.on("error", (err) => {
@@ -20967,7 +21052,7 @@ var init_git_dir_watcher = __esm({
20967
21052
  * Exists solely during the non-repo window; upgraded away on first `.git`. */
20968
21053
  watchParentForGit(workspaceRoot) {
20969
21054
  try {
20970
- this.watcher = fs45.watch(workspaceRoot, { persistent: true, recursive: false }, (_eventType, filename) => {
21055
+ this.watcher = fs46.watch(workspaceRoot, { persistent: true, recursive: false }, (_eventType, filename) => {
20971
21056
  this.handleParentEvent(filename);
20972
21057
  });
20973
21058
  this.watcher.on("error", (err) => {
@@ -21003,8 +21088,8 @@ var init_git_dir_watcher = __esm({
21003
21088
  const root = this.workspaceRoot;
21004
21089
  if (!root)
21005
21090
  return;
21006
- const gitDir = path53.join(root, ".git");
21007
- if (!fs45.existsSync(gitDir))
21091
+ const gitDir = path54.join(root, ".git");
21092
+ if (!fs46.existsSync(gitDir))
21008
21093
  return;
21009
21094
  this.closeWatcher();
21010
21095
  this.watchGitDir(gitDir);
@@ -21320,7 +21405,7 @@ var init_terminal_manager = __esm({
21320
21405
  });
21321
21406
 
21322
21407
  // packages/server/dist/ws/handler.js
21323
- import path54 from "node:path";
21408
+ import path55 from "node:path";
21324
21409
  function setupWebSocketHandler(deps) {
21325
21410
  const { wss: wss2, registry: registry2 } = deps;
21326
21411
  const clients = /* @__PURE__ */ new Set();
@@ -21342,7 +21427,7 @@ function setupWebSocketHandler(deps) {
21342
21427
  ackedChatIds.add(id);
21343
21428
  }
21344
21429
  function resolveProjectPath2(projectId) {
21345
- if (path54.isAbsolute(projectId))
21430
+ if (path55.isAbsolute(projectId))
21346
21431
  return projectId;
21347
21432
  return null;
21348
21433
  }
@@ -21968,11 +22053,11 @@ var init_handler = __esm({
21968
22053
  import Database6 from "better-sqlite3";
21969
22054
  import { drizzle as drizzle4 } from "drizzle-orm/better-sqlite3";
21970
22055
  import { sqliteTable as sqliteTable4, text as text4, integer as integer4 } from "drizzle-orm/sqlite-core";
21971
- import path55 from "node:path";
21972
- import fs46 from "node:fs";
22056
+ import path56 from "node:path";
22057
+ import fs47 from "node:fs";
21973
22058
  function createCronDb(globalDir2) {
21974
- fs46.mkdirSync(globalDir2, { recursive: true });
21975
- const dbPath = path55.join(globalDir2, "cron.db");
22059
+ fs47.mkdirSync(globalDir2, { recursive: true });
22060
+ const dbPath = path56.join(globalDir2, "cron.db");
21976
22061
  const sqlite = new Database6(dbPath);
21977
22062
  sqlite.pragma("journal_mode = WAL");
21978
22063
  sqlite.exec(CREATE_SQL3);
@@ -22170,8 +22255,8 @@ var init_child_env = __esm({
22170
22255
 
22171
22256
  // packages/server/dist/cron/runner.js
22172
22257
  import { spawn as spawn3, execFileSync } from "node:child_process";
22173
- import fs47 from "node:fs";
22174
- import path56 from "node:path";
22258
+ import fs48 from "node:fs";
22259
+ import path57 from "node:path";
22175
22260
  import { homedir as homedir23 } from "node:os";
22176
22261
  import { Cron } from "croner";
22177
22262
  import { eq as eq11, lt as lt4 } from "drizzle-orm";
@@ -22242,10 +22327,10 @@ function isCronCliProcess(pid, jobId) {
22242
22327
  }
22243
22328
  }
22244
22329
  function logsDir() {
22245
- return path56.join(homedir23(), ".halo", "global", "logs", "cron");
22330
+ return path57.join(homedir23(), ".halo", "global", "logs", "cron");
22246
22331
  }
22247
22332
  function logPathFor(runId) {
22248
- return path56.join(logsDir(), `${runId}.log`);
22333
+ return path57.join(logsDir(), `${runId}.log`);
22249
22334
  }
22250
22335
  function newRunId() {
22251
22336
  return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${Math.random().toString(36).slice(2, 8)}`;
@@ -22296,7 +22381,7 @@ function sweepOrphanRuns(opts) {
22296
22381
  }
22297
22382
  }
22298
22383
  function startCronDaemon() {
22299
- fs47.mkdirSync(logsDir(), { recursive: true });
22384
+ fs48.mkdirSync(logsDir(), { recursive: true });
22300
22385
  sweepOrphanRuns();
22301
22386
  reloadAll();
22302
22387
  _daemonTimers.push(setInterval(pruneOldLogs, DAY_MS2));
@@ -22453,7 +22538,7 @@ async function runJob(jobId, triggerKind) {
22453
22538
  const runId = newRunId();
22454
22539
  const startedAt = Date.now();
22455
22540
  const logPath = logPathFor(runId);
22456
- fs47.mkdirSync(logsDir(), { recursive: true });
22541
+ fs48.mkdirSync(logsDir(), { recursive: true });
22457
22542
  if (_inflight.has(jobId)) {
22458
22543
  db.insert(cronRuns).values({
22459
22544
  id: runId,
@@ -22488,7 +22573,7 @@ async function runJob(jobId, triggerKind) {
22488
22573
  }).run();
22489
22574
  broadcast({ type: "cron:run_changed", jobId, runId, status: "running" });
22490
22575
  const targets = parseTargets(job.targets);
22491
- if (!fs47.existsSync(job.workspacePath)) {
22576
+ if (!fs48.existsSync(job.workspacePath)) {
22492
22577
  finalize(runId, jobId, {
22493
22578
  status: "failed",
22494
22579
  output: null,
@@ -22502,7 +22587,7 @@ async function runJob(jobId, triggerKind) {
22502
22587
  const sessionId = `cron-${job.id}`;
22503
22588
  const args = ["cli", "-a", job.agentId, "-s", sessionId, "-w", job.workspacePath];
22504
22589
  const [spawnBin, spawnArgs] = process.platform === "win32" && cli.endsWith(".cmd") ? ["cmd.exe", ["/c", cli, ...args]] : [cli, args];
22505
- const teeStream = fs47.createWriteStream(logPath, { flags: "a" });
22590
+ const teeStream = fs48.createWriteStream(logPath, { flags: "a" });
22506
22591
  teeStream.write(`=== ${(/* @__PURE__ */ new Date()).toISOString()} cron run ${runId} job=${jobId} trigger=${triggerKind} ===
22507
22592
  `);
22508
22593
  teeStream.write(`agent=${job.agentId} workspace=${job.workspacePath} schedule=${job.schedule}
@@ -22674,15 +22759,15 @@ function pruneOldRuns(jobId) {
22674
22759
  }
22675
22760
  function pruneOldLogs() {
22676
22761
  const dir = logsDir();
22677
- if (!fs47.existsSync(dir))
22762
+ if (!fs48.existsSync(dir))
22678
22763
  return;
22679
22764
  const cutoff = Date.now() - LOG_RETENTION_DAYS * DAY_MS2;
22680
- for (const name of fs47.readdirSync(dir)) {
22681
- const p = path56.join(dir, name);
22765
+ for (const name of fs48.readdirSync(dir)) {
22766
+ const p = path57.join(dir, name);
22682
22767
  try {
22683
- const stat = fs47.statSync(p);
22768
+ const stat = fs48.statSync(p);
22684
22769
  if (stat.mtimeMs < cutoff)
22685
- fs47.rmSync(p, { force: true });
22770
+ fs48.rmSync(p, { force: true });
22686
22771
  } catch {
22687
22772
  }
22688
22773
  }
@@ -22715,7 +22800,7 @@ var init_runner = __esm({
22715
22800
  // packages/server/dist/routes/cron.js
22716
22801
  import { Hono as Hono14 } from "hono";
22717
22802
  import { eq as eq12, desc as desc3, lt as lt5, and as and8 } from "drizzle-orm";
22718
- import fs48 from "node:fs/promises";
22803
+ import fs49 from "node:fs/promises";
22719
22804
  import { Cron as Cron2 } from "croner";
22720
22805
  function newJobId() {
22721
22806
  return `cron-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -22911,7 +22996,7 @@ function createCronRoutes() {
22911
22996
  return c.json({ log: null });
22912
22997
  let log2 = null;
22913
22998
  try {
22914
- log2 = await fs48.readFile(row.logPath, "utf-8");
22999
+ log2 = await fs49.readFile(row.logPath, "utf-8");
22915
23000
  } catch {
22916
23001
  }
22917
23002
  return c.json({ log: log2 });
@@ -23213,7 +23298,7 @@ var init_ticker = __esm({
23213
23298
  });
23214
23299
 
23215
23300
  // packages/server/dist/evolution/spawn.js
23216
- import path57 from "node:path";
23301
+ import path58 from "node:path";
23217
23302
  import { spawn as spawn4 } from "node:child_process";
23218
23303
  import { fileURLToPath as fileURLToPath2 } from "node:url";
23219
23304
  var __dirname2, WRAPPER_PATH, realEvoSpawner;
@@ -23221,8 +23306,8 @@ var init_spawn = __esm({
23221
23306
  "packages/server/dist/evolution/spawn.js"() {
23222
23307
  "use strict";
23223
23308
  init_child_env();
23224
- __dirname2 = path57.dirname(fileURLToPath2(import.meta.url));
23225
- WRAPPER_PATH = path57.resolve(__dirname2, "evo-wrapper.js");
23309
+ __dirname2 = path58.dirname(fileURLToPath2(import.meta.url));
23310
+ WRAPPER_PATH = path58.resolve(__dirname2, "evo-wrapper.js");
23226
23311
  realEvoSpawner = (mode, id) => {
23227
23312
  const child = spawn4(process.execPath, [WRAPPER_PATH, `--mode=${mode}`, `--id=${id}`], {
23228
23313
  detached: true,
@@ -24009,7 +24094,7 @@ var init_handler2 = __esm({
24009
24094
  // packages/server/dist/routes/telegram.js
24010
24095
  import { Hono as Hono15 } from "hono";
24011
24096
  import { Bot as Bot2 } from "grammy";
24012
- import fs49 from "node:fs";
24097
+ import fs50 from "node:fs";
24013
24098
  function createTelegramRoutes(deps) {
24014
24099
  const { db, channel } = deps;
24015
24100
  const app2 = new Hono15();
@@ -24019,7 +24104,7 @@ function createTelegramRoutes(deps) {
24019
24104
  accountId: acc.accountId,
24020
24105
  botUsername: acc.botUsername,
24021
24106
  workspacePath: acc.workspacePath,
24022
- workspaceMissing: !fs49.existsSync(acc.workspacePath),
24107
+ workspaceMissing: !fs50.existsSync(acc.workspacePath),
24023
24108
  label: acc.label,
24024
24109
  enabled: acc.enabled,
24025
24110
  accessLevel: acc.accessLevel,
@@ -24743,8 +24828,8 @@ var init_cdn = __esm({
24743
24828
  });
24744
24829
 
24745
24830
  // packages/server/dist/channels/wechat/send-media.js
24746
- import fs50 from "node:fs/promises";
24747
- import path58 from "node:path";
24831
+ import fs51 from "node:fs/promises";
24832
+ import path59 from "node:path";
24748
24833
  function inferKind(filePath) {
24749
24834
  const cls = classifyMedia(filePath);
24750
24835
  if (cls === "image")
@@ -24756,7 +24841,7 @@ function inferKind(filePath) {
24756
24841
  async function sendMediaFile(params) {
24757
24842
  const { baseUrl, token, toUserId, contextToken, filePath } = params;
24758
24843
  const kind = params.kind ?? inferKind(filePath);
24759
- const plaintext = await fs50.readFile(filePath);
24844
+ const plaintext = await fs51.readFile(filePath);
24760
24845
  const prep = prepareUpload(plaintext);
24761
24846
  console.log(`[wechat] sendMediaFile: path=${filePath} kind=${kind} rawsize=${prep.rawsize} filesize=${prep.filesize}`);
24762
24847
  const mediaType = kind === "image" ? UploadMediaType.IMAGE : kind === "video" ? UploadMediaType.VIDEO : UploadMediaType.FILE;
@@ -24794,7 +24879,7 @@ async function sendMediaFile(params) {
24794
24879
  aesKeyBase64,
24795
24880
  fileSizeCiphertext: prep.filesize,
24796
24881
  fileSize: prep.rawsize,
24797
- fileName: path58.basename(filePath)
24882
+ fileName: path59.basename(filePath)
24798
24883
  });
24799
24884
  const clientId = `halo-media-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
24800
24885
  const body = {
@@ -24957,8 +25042,8 @@ var init_login = __esm({
24957
25042
  });
24958
25043
 
24959
25044
  // packages/server/dist/channels/wechat/handler.js
24960
- import fs51 from "node:fs";
24961
- import path59 from "node:path";
25045
+ import fs52 from "node:fs";
25046
+ import path60 from "node:path";
24962
25047
  import QRCode from "qrcode";
24963
25048
  function wxRoute(fromUserId, contextToken) {
24964
25049
  return (prev) => ({
@@ -25347,7 +25432,7 @@ async function handleSlashCommand(args) {
25347
25432
  await reply(login.message);
25348
25433
  return true;
25349
25434
  }
25350
- const tmpPath = path59.join(tempDir(), `halo-qr-${login.sessionKey}.png`);
25435
+ const tmpPath = path60.join(tempDir(), `halo-qr-${login.sessionKey}.png`);
25351
25436
  await QRCode.toFile(tmpPath, login.qrcodeUrl, { width: 256, margin: 2 });
25352
25437
  await sendMediaFile({
25353
25438
  baseUrl: account.baseUrl,
@@ -25356,7 +25441,7 @@ async function handleSlashCommand(args) {
25356
25441
  contextToken,
25357
25442
  filePath: tmpPath
25358
25443
  });
25359
- await fs51.promises.unlink(tmpPath).catch(() => {
25444
+ await fs52.promises.unlink(tmpPath).catch(() => {
25360
25445
  });
25361
25446
  await reply(t("wechat.qr_sent", lang, { level, path: account.workspacePath }));
25362
25447
  waitLogin({ sessionKey: login.sessionKey }).then(async (result2) => {
@@ -25436,8 +25521,8 @@ var init_handler3 = __esm({
25436
25521
 
25437
25522
  // packages/server/dist/routes/wechat.js
25438
25523
  import { Hono as Hono16 } from "hono";
25439
- import path60 from "node:path";
25440
- import fs52 from "node:fs";
25524
+ import path61 from "node:path";
25525
+ import fs53 from "node:fs";
25441
25526
  function createWechatRoutes(deps) {
25442
25527
  const { db, channel } = deps;
25443
25528
  const app2 = new Hono16();
@@ -25446,7 +25531,7 @@ function createWechatRoutes(deps) {
25446
25531
  return {
25447
25532
  ...stripSecrets(acc),
25448
25533
  workspacePath: acc.workspacePath,
25449
- workspaceMissing: !fs52.existsSync(acc.workspacePath)
25534
+ workspaceMissing: !fs53.existsSync(acc.workspacePath)
25450
25535
  };
25451
25536
  });
25452
25537
  return c.json({ accounts });
@@ -25462,7 +25547,7 @@ function createWechatRoutes(deps) {
25462
25547
  return c.json({ error: "sessionKey required" }, 400);
25463
25548
  if (!body.workspacePath)
25464
25549
  return c.json({ error: "workspacePath required" }, 400);
25465
- if (!path60.isAbsolute(body.workspacePath))
25550
+ if (!path61.isAbsolute(body.workspacePath))
25466
25551
  return c.json({ error: "workspacePath must be absolute" }, 400);
25467
25552
  const levelError = accessLevelError(body.accessLevel, CHAT_ACCESS_LEVELS);
25468
25553
  if (levelError)
@@ -25666,14 +25751,14 @@ function createWebChannel(deps) {
25666
25751
  return findActiveSessionId(sm, accountId, prefix, activeOverrides2, "full") ?? void 0;
25667
25752
  }
25668
25753
  function resolveWorkspace(account, override) {
25669
- let path65 = account.workspacePath;
25754
+ let path66 = account.workspacePath;
25670
25755
  if (override && override !== account.workspacePath) {
25671
25756
  if (account.accessLevel !== "full") {
25672
25757
  return { ok: false, error: "workspace override requires a full-access token" };
25673
25758
  }
25674
- path65 = override;
25759
+ path66 = override;
25675
25760
  }
25676
- const resolved = resolveAccountWorkspace({ ...account, workspacePath: path65 });
25761
+ const resolved = resolveAccountWorkspace({ ...account, workspacePath: path66 });
25677
25762
  if (!resolved)
25678
25763
  return { ok: false, error: "workspace not found" };
25679
25764
  return { ok: true, path: resolved };
@@ -26056,7 +26141,7 @@ var init_handler4 = __esm({
26056
26141
  import { Hono as Hono17 } from "hono";
26057
26142
  import { streamSSE } from "hono/streaming";
26058
26143
  import crypto4 from "node:crypto";
26059
- import fs53 from "node:fs";
26144
+ import fs54 from "node:fs";
26060
26145
  function createWebRoutes(deps) {
26061
26146
  const { db, channel } = deps;
26062
26147
  const app2 = new Hono17();
@@ -26066,7 +26151,7 @@ function createWebRoutes(deps) {
26066
26151
  accountId: acc.accountId,
26067
26152
  token: acc.token,
26068
26153
  workspacePath: acc.workspacePath,
26069
- workspaceMissing: !fs53.existsSync(acc.workspacePath),
26154
+ workspaceMissing: !fs54.existsSync(acc.workspacePath),
26070
26155
  label: acc.label,
26071
26156
  enabled: acc.enabled,
26072
26157
  accessLevel: acc.accessLevel,
@@ -26212,27 +26297,27 @@ function createWebRoutes(deps) {
26212
26297
  const filePath = c.req.query("path");
26213
26298
  if (!filePath)
26214
26299
  return c.json({ error: "path required" }, 400);
26215
- const path65 = await import("node:path");
26216
- const resolved = path65.default.resolve(account.workspacePath, filePath);
26217
- const root = path65.default.resolve(account.workspacePath);
26218
- if (resolved !== root && !resolved.startsWith(root + path65.default.sep)) {
26300
+ const path66 = await import("node:path");
26301
+ const resolved = path66.default.resolve(account.workspacePath, filePath);
26302
+ const root = path66.default.resolve(account.workspacePath);
26303
+ if (resolved !== root && !resolved.startsWith(root + path66.default.sep)) {
26219
26304
  return c.json({ error: "path traversal not allowed" }, 403);
26220
26305
  }
26221
26306
  let real;
26222
26307
  let realRoot;
26223
26308
  try {
26224
- real = fs53.realpathSync(resolved);
26225
- realRoot = fs53.realpathSync(root);
26309
+ real = fs54.realpathSync(resolved);
26310
+ realRoot = fs54.realpathSync(root);
26226
26311
  } catch {
26227
26312
  return c.json({ error: "file not found" }, 404);
26228
26313
  }
26229
- if (real !== realRoot && !real.startsWith(realRoot + path65.default.sep)) {
26314
+ if (real !== realRoot && !real.startsWith(realRoot + path66.default.sep)) {
26230
26315
  return c.json({ error: "path traversal not allowed" }, 403);
26231
26316
  }
26232
- const stat = fs53.statSync(real);
26317
+ const stat = fs54.statSync(real);
26233
26318
  if (stat.isDirectory())
26234
26319
  return c.json({ error: "cannot serve directory" }, 400);
26235
- const ext = path65.default.extname(real).toLowerCase();
26320
+ const ext = path66.default.extname(real).toLowerCase();
26236
26321
  const nonImageMimes = {
26237
26322
  ".mp4": "video/mp4",
26238
26323
  ".webm": "video/webm",
@@ -26240,8 +26325,8 @@ function createWebRoutes(deps) {
26240
26325
  ".txt": "text/plain"
26241
26326
  };
26242
26327
  const contentType = imageMimeFromExt(ext) || nonImageMimes[ext] || "application/octet-stream";
26243
- const fileName = path65.default.basename(resolved);
26244
- const data = fs53.readFileSync(real);
26328
+ const fileName = path66.default.basename(resolved);
26329
+ const data = fs54.readFileSync(real);
26245
26330
  c.header("content-type", contentType);
26246
26331
  c.header("content-disposition", `inline; filename="${fileName}"`);
26247
26332
  return c.body(data);
@@ -26582,10 +26667,10 @@ async function authTest(botToken) {
26582
26667
  return slackPost("auth.test", botToken, {});
26583
26668
  }
26584
26669
  async function uploadFile(args) {
26585
- const fs58 = await import("node:fs");
26586
- const path65 = await import("node:path");
26587
- const filename = args.filename ?? path65.basename(args.filePath);
26588
- const stat = fs58.statSync(args.filePath);
26670
+ const fs59 = await import("node:fs");
26671
+ const path66 = await import("node:path");
26672
+ const filename = args.filename ?? path66.basename(args.filePath);
26673
+ const stat = fs59.statSync(args.filePath);
26589
26674
  if (!stat.isFile())
26590
26675
  throw new Error(`[slack:uploadFile] not a regular file: ${args.filePath}`);
26591
26676
  const length = stat.size;
@@ -26603,7 +26688,7 @@ async function uploadFile(args) {
26603
26688
  if (step1Parsed.ok === false || !step1Parsed.upload_url || !step1Parsed.file_id) {
26604
26689
  throw new Error(`[slack:files.getUploadURLExternal] ${step1Parsed.error ?? "unknown"}`);
26605
26690
  }
26606
- const buf = fs58.readFileSync(args.filePath);
26691
+ const buf = fs59.readFileSync(args.filePath);
26607
26692
  const step2 = await fetch(step1Parsed.upload_url, {
26608
26693
  method: "POST",
26609
26694
  body: buf
@@ -26748,7 +26833,7 @@ var init_api2 = __esm({
26748
26833
  });
26749
26834
 
26750
26835
  // packages/server/dist/channels/slack/handler.js
26751
- import path61 from "node:path";
26836
+ import path62 from "node:path";
26752
26837
  import { WebSocket } from "ws";
26753
26838
  function slashToBang(text5, commandNames) {
26754
26839
  if (commandNames.length === 0)
@@ -26853,7 +26938,7 @@ function startSlackChannel(deps) {
26853
26938
  const route = bridge.getRoute(sessionId);
26854
26939
  if (!account || !route)
26855
26940
  return;
26856
- const resolved = path61.resolve(filePath);
26941
+ const resolved = path62.resolve(filePath);
26857
26942
  if (!isMediaPathAllowed(resolved, account.workspacePath)) {
26858
26943
  console.log(`[slack] sendMedia blocked: ${filePath} not under workspace`);
26859
26944
  return;
@@ -26872,7 +26957,7 @@ function startSlackChannel(deps) {
26872
26957
  channel: route.channelId,
26873
26958
  threadTs: route.replyTs,
26874
26959
  text: t("handler.upload_failed", getLang(account), {
26875
- name: path61.basename(filePath),
26960
+ name: path62.basename(filePath),
26876
26961
  error: err instanceof Error ? err.message : String(err)
26877
26962
  })
26878
26963
  }).catch(() => {
@@ -27129,7 +27214,7 @@ var init_handler5 = __esm({
27129
27214
 
27130
27215
  // packages/server/dist/routes/slack.js
27131
27216
  import { Hono as Hono18 } from "hono";
27132
- import fs54 from "node:fs";
27217
+ import fs55 from "node:fs";
27133
27218
  function createSlackRoutes(deps) {
27134
27219
  const { db, channel } = deps;
27135
27220
  const app2 = new Hono18();
@@ -27152,7 +27237,7 @@ function createSlackRoutes(deps) {
27152
27237
  botUserId: a.botUserId,
27153
27238
  teamId: a.teamId,
27154
27239
  workspacePath: a.workspacePath,
27155
- workspaceMissing: !fs54.existsSync(a.workspacePath),
27240
+ workspaceMissing: !fs55.existsSync(a.workspacePath),
27156
27241
  label: a.label,
27157
27242
  enabled: a.enabled,
27158
27243
  accessLevel: a.accessLevel,
@@ -27684,10 +27769,10 @@ async function replyMessage(args) {
27684
27769
  return { message_id: parsed.data?.message_id ?? "" };
27685
27770
  }
27686
27771
  async function uploadImage(args) {
27687
- const fs58 = await import("node:fs");
27688
- const path65 = await import("node:path");
27689
- const buf = fs58.readFileSync(args.filePath);
27690
- const filename = path65.basename(args.filePath);
27772
+ const fs59 = await import("node:fs");
27773
+ const path66 = await import("node:path");
27774
+ const buf = fs59.readFileSync(args.filePath);
27775
+ const filename = path66.basename(args.filePath);
27691
27776
  const send = async (token2) => {
27692
27777
  const form = new FormData();
27693
27778
  form.append("image_type", "message");
@@ -27718,10 +27803,10 @@ async function uploadImage(args) {
27718
27803
  return { imageKey: parsed.data.image_key };
27719
27804
  }
27720
27805
  async function uploadFile2(args) {
27721
- const fs58 = await import("node:fs");
27722
- const path65 = await import("node:path");
27723
- const buf = fs58.readFileSync(args.filePath);
27724
- const filename = args.filename ?? path65.basename(args.filePath);
27806
+ const fs59 = await import("node:fs");
27807
+ const path66 = await import("node:path");
27808
+ const buf = fs59.readFileSync(args.filePath);
27809
+ const filename = args.filename ?? path66.basename(args.filePath);
27725
27810
  const send = async (token2) => {
27726
27811
  const form = new FormData();
27727
27812
  form.append("file_type", args.fileType);
@@ -27827,7 +27912,7 @@ var init_api3 = __esm({
27827
27912
  });
27828
27913
 
27829
27914
  // packages/server/dist/channels/feishu/handler.js
27830
- import path62 from "node:path";
27915
+ import path63 from "node:path";
27831
27916
  import * as Lark from "@larksuiteoapi/node-sdk";
27832
27917
  function buildSessionPrefixForThread2(chatId, rootId) {
27833
27918
  return sessionPrefix("feishu", `${chatId}:${rootId}`);
@@ -27950,7 +28035,7 @@ function startFeishuChannel(deps) {
27950
28035
  const route = bridge.getRoute(sessionId);
27951
28036
  if (!account || !route)
27952
28037
  return;
27953
- const resolved = path62.resolve(filePath);
28038
+ const resolved = path63.resolve(filePath);
27954
28039
  if (!isMediaPathAllowed(resolved, account.workspacePath)) {
27955
28040
  console.log(`[feishu] sendMedia blocked: ${filePath} not under workspace`);
27956
28041
  return;
@@ -27963,7 +28048,7 @@ function startFeishuChannel(deps) {
27963
28048
  account,
27964
28049
  ...route,
27965
28050
  text: t("handler.upload_failed", getLang(account), {
27966
- name: path62.basename(filePath),
28051
+ name: path63.basename(filePath),
27967
28052
  error: err instanceof Error ? err.message : String(err)
27968
28053
  })
27969
28054
  }).catch(() => {
@@ -28127,7 +28212,7 @@ async function replyToInbound(args) {
28127
28212
  async function sendFeishuMedia(args) {
28128
28213
  const { account, inboundMessageId, isP2P, chatId, filePath } = args;
28129
28214
  const cls = classifyMedia(filePath);
28130
- const ext = path62.extname(filePath).toLowerCase();
28215
+ const ext = path63.extname(filePath).toLowerCase();
28131
28216
  if (cls === "image") {
28132
28217
  const { imageKey } = await uploadImage({ appId: account.appId, appSecret: account.appSecret, filePath });
28133
28218
  await replyToInboundRaw({
@@ -28253,7 +28338,7 @@ var init_handler6 = __esm({
28253
28338
 
28254
28339
  // packages/server/dist/routes/feishu.js
28255
28340
  import { Hono as Hono19 } from "hono";
28256
- import fs55 from "node:fs";
28341
+ import fs56 from "node:fs";
28257
28342
  function createFeishuRoutes(deps) {
28258
28343
  const { db, channel } = deps;
28259
28344
  const app2 = new Hono19();
@@ -28277,7 +28362,7 @@ function createFeishuRoutes(deps) {
28277
28362
  botOpenId: a.botOpenId,
28278
28363
  hasEncryptKey: !!a.encryptKey,
28279
28364
  workspacePath: a.workspacePath,
28280
- workspaceMissing: !fs55.existsSync(a.workspacePath),
28365
+ workspaceMissing: !fs56.existsSync(a.workspacePath),
28281
28366
  label: a.label,
28282
28367
  enabled: a.enabled,
28283
28368
  accessLevel: a.accessLevel,
@@ -28665,14 +28750,14 @@ function passwordStrengthError(pw) {
28665
28750
  }
28666
28751
  function authMiddleware() {
28667
28752
  return async (c, next) => {
28668
- const path65 = c.req.path;
28669
- if (PUBLIC_PATHS.some((p) => path65 === p || path65.startsWith(p + "?"))) {
28753
+ const path66 = c.req.path;
28754
+ if (PUBLIC_PATHS.some((p) => path66 === p || path66.startsWith(p + "?"))) {
28670
28755
  return next();
28671
28756
  }
28672
28757
  const token = getCookie(c, COOKIE_NAME);
28673
28758
  const payload = validateToken(token);
28674
28759
  if (!payload) {
28675
- if (!path65.startsWith("/api/")) {
28760
+ if (!path66.startsWith("/api/")) {
28676
28761
  return next();
28677
28762
  }
28678
28763
  return c.json({ error: "Unauthorized" }, 401);
@@ -28711,17 +28796,17 @@ import { compress } from "hono/compress";
28711
28796
  import { serve } from "@hono/node-server";
28712
28797
  import { serveStatic } from "@hono/node-server/serve-static";
28713
28798
  import { WebSocketServer } from "ws";
28714
- import path63 from "node:path";
28715
- import fs56 from "node:fs";
28716
- import { spawnSync as spawnSync3 } from "node:child_process";
28799
+ import path64 from "node:path";
28800
+ import fs57 from "node:fs";
28801
+ import { spawnSync as spawnSync4 } from "node:child_process";
28717
28802
  import { homedir as homedir24 } from "node:os";
28718
28803
  import { fileURLToPath as fileURLToPath3 } from "node:url";
28719
28804
  function findProjectRoot() {
28720
28805
  let dir = process.cwd();
28721
28806
  for (let i = 0; i < 32; i++) {
28722
- if (fs56.existsSync(path63.join(dir, "pnpm-workspace.yaml")))
28807
+ if (fs57.existsSync(path64.join(dir, "pnpm-workspace.yaml")))
28723
28808
  return dir;
28724
- const parent = path63.dirname(dir);
28809
+ const parent = path64.dirname(dir);
28725
28810
  if (parent === dir)
28726
28811
  break;
28727
28812
  dir = parent;
@@ -28730,15 +28815,15 @@ function findProjectRoot() {
28730
28815
  return process.cwd();
28731
28816
  }
28732
28817
  function acquireSingleInstanceLock(lockFile) {
28733
- fs56.mkdirSync(path63.dirname(lockFile), { recursive: true });
28734
- const fd = fs56.openSync(lockFile, fs56.constants.O_RDWR | fs56.constants.O_CREAT, 420);
28735
- const result = spawnSync3("flock", ["-n", "3"], {
28818
+ fs57.mkdirSync(path64.dirname(lockFile), { recursive: true });
28819
+ const fd = fs57.openSync(lockFile, fs57.constants.O_RDWR | fs57.constants.O_CREAT, 420);
28820
+ const result = spawnSync4("flock", ["-n", "3"], {
28736
28821
  stdio: ["ignore", "ignore", "ignore", fd]
28737
28822
  });
28738
28823
  if (result.error) {
28739
28824
  const content = readLockPid(fd);
28740
28825
  if (content > 0 && isProcessAlive(content)) {
28741
- fs56.closeSync(fd);
28826
+ fs57.closeSync(fd);
28742
28827
  console.error(`[Server] another halo server is already running (pid ${content}). kill it first.`);
28743
28828
  process.exit(1);
28744
28829
  }
@@ -28747,19 +28832,19 @@ function acquireSingleInstanceLock(lockFile) {
28747
28832
  if (content > 0 && !isProcessAlive(content)) {
28748
28833
  console.warn(`[Server] removing stale server.lock (pid ${content} not running)`);
28749
28834
  } else {
28750
- fs56.closeSync(fd);
28835
+ fs57.closeSync(fd);
28751
28836
  console.error(`[Server] another halo server is already running${content > 0 ? ` (pid ${content})` : ""}. kill it first.`);
28752
28837
  process.exit(1);
28753
28838
  }
28754
28839
  }
28755
- fs56.ftruncateSync(fd);
28756
- fs56.writeSync(fd, String(process.pid));
28757
- fs56.fsyncSync(fd);
28840
+ fs57.ftruncateSync(fd);
28841
+ fs57.writeSync(fd, String(process.pid));
28842
+ fs57.fsyncSync(fd);
28758
28843
  }
28759
28844
  function readLockPid(fd) {
28760
28845
  try {
28761
28846
  const buf = Buffer.alloc(32);
28762
- const n = fs56.readSync(fd, buf, 0, 32, 0);
28847
+ const n = fs57.readSync(fd, buf, 0, 32, 0);
28763
28848
  return parseInt(buf.subarray(0, n).toString("utf-8").trim(), 10) || 0;
28764
28849
  } catch {
28765
28850
  return 0;
@@ -28768,7 +28853,7 @@ function readLockPid(fd) {
28768
28853
  function isProcessAlive(pid) {
28769
28854
  if (process.platform === "win32") {
28770
28855
  try {
28771
- const out = spawnSync3("tasklist", ["/FI", `PID eq ${pid}`, "/NH", "/FO", "CSV"], {
28856
+ const out = spawnSync4("tasklist", ["/FI", `PID eq ${pid}`, "/NH", "/FO", "CSV"], {
28772
28857
  encoding: "utf-8",
28773
28858
  windowsHide: true
28774
28859
  });
@@ -28787,16 +28872,16 @@ function isProcessAlive(pid) {
28787
28872
  }
28788
28873
  }
28789
28874
  function readGitSha() {
28790
- if (!fs56.existsSync(path63.join(PROJECT_ROOT, "pnpm-workspace.yaml")) || !fs56.existsSync(path63.join(PROJECT_ROOT, ".git")))
28875
+ if (!fs57.existsSync(path64.join(PROJECT_ROOT, "pnpm-workspace.yaml")) || !fs57.existsSync(path64.join(PROJECT_ROOT, ".git")))
28791
28876
  return null;
28792
28877
  try {
28793
- const rev = spawnSync3("git", ["rev-parse", "--short", "HEAD"], { cwd: PROJECT_ROOT, encoding: "utf-8" });
28878
+ const rev = spawnSync4("git", ["rev-parse", "--short", "HEAD"], { cwd: PROJECT_ROOT, encoding: "utf-8" });
28794
28879
  if (rev.status !== 0)
28795
28880
  return null;
28796
28881
  const sha = rev.stdout.trim();
28797
28882
  if (!sha)
28798
28883
  return null;
28799
- const dirty = spawnSync3("git", ["status", "--porcelain"], { cwd: PROJECT_ROOT, encoding: "utf-8" }).stdout.trim().length > 0;
28884
+ const dirty = spawnSync4("git", ["status", "--porcelain"], { cwd: PROJECT_ROOT, encoding: "utf-8" }).stdout.trim().length > 0;
28800
28885
  return dirty ? `${sha}-dirty` : sha;
28801
28886
  } catch {
28802
28887
  return null;
@@ -28804,14 +28889,14 @@ function readGitSha() {
28804
28889
  }
28805
28890
  function resolveFrontendDir() {
28806
28891
  if (process.env.HALO_FRONTEND_DIR)
28807
- return path63.resolve(process.env.HALO_FRONTEND_DIR);
28808
- const bundled = path63.resolve(__dirname3, "..", "admin-out");
28809
- if (fs56.existsSync(path63.join(bundled, "index.html")))
28892
+ return path64.resolve(process.env.HALO_FRONTEND_DIR);
28893
+ const bundled = path64.resolve(__dirname3, "..", "admin-out");
28894
+ if (fs57.existsSync(path64.join(bundled, "index.html")))
28810
28895
  return bundled;
28811
- const sibling = path63.resolve(__dirname3, "..", "..", "admin", "out");
28812
- if (fs56.existsSync(path63.join(sibling, "index.html")))
28896
+ const sibling = path64.resolve(__dirname3, "..", "..", "admin", "out");
28897
+ if (fs57.existsSync(path64.join(sibling, "index.html")))
28813
28898
  return sibling;
28814
- return path63.resolve(PROJECT_ROOT, "packages", "admin", "out");
28899
+ return path64.resolve(PROJECT_ROOT, "packages", "admin", "out");
28815
28900
  }
28816
28901
  async function gracefulShutdown(signal) {
28817
28902
  console.log(`[Server] Received ${signal}, shutting down gracefully...`);
@@ -28867,13 +28952,13 @@ var init_dist2 = __esm({
28867
28952
  init_sandbox();
28868
28953
  init_init();
28869
28954
  init_git_ssh();
28870
- __dirname3 = path63.dirname(fileURLToPath3(import.meta.url));
28955
+ __dirname3 = path64.dirname(fileURLToPath3(import.meta.url));
28871
28956
  PORT = config.server.port;
28872
- HALO_VERSION = "1.1.1";
28957
+ HALO_VERSION = "1.1.2";
28873
28958
  PROJECT_ROOT = findProjectRoot();
28874
- HALO_HOME4 = path63.join(homedir24(), ".halo");
28959
+ HALO_HOME4 = path64.join(homedir24(), ".halo");
28875
28960
  GIT_SHA = readGitSha();
28876
- if (!fs56.existsSync(path63.join(HALO_HOME4, "global", ".template-version"))) {
28961
+ if (!fs57.existsSync(path64.join(HALO_HOME4, "global", ".template-version"))) {
28877
28962
  process.stderr.write("\x1B[31m[Server] ~/.halo/global/ not initialized. Run `halo setup` first.\x1B[0m\n");
28878
28963
  process.exit(1);
28879
28964
  }
@@ -28894,7 +28979,7 @@ var init_dist2 = __esm({
28894
28979
  process.exit(1);
28895
28980
  }
28896
28981
  if (!AGENTCORE)
28897
- acquireSingleInstanceLock(path63.join(HALO_HOME4, "global", "server.lock"));
28982
+ acquireSingleInstanceLock(path64.join(HALO_HOME4, "global", "server.lock"));
28898
28983
  {
28899
28984
  const declared = new Set(commandRegistry.listDescriptors().filter((d) => d.source === "builtin").map((d) => d.slashName));
28900
28985
  const dispatched = new Set(DISPATCH_COMMANDS);
@@ -28963,10 +29048,10 @@ var init_dist2 = __esm({
28963
29048
  app.route("/api", evolutionRoutes);
28964
29049
  cronRoutes = createCronRoutes();
28965
29050
  app.route("/api", cronRoutes);
28966
- channelDb = createChannelDb(path63.join(HALO_HOME4, "secrets"));
29051
+ channelDb = createChannelDb(path64.join(HALO_HOME4, "secrets"));
28967
29052
  setChannelDb(channelDb);
28968
- setEvoDb(createEvoDb(path63.join(HALO_HOME4, "global")));
28969
- setCronDb(createCronDb(path63.join(HALO_HOME4, "global")));
29053
+ setEvoDb(createEvoDb(path64.join(HALO_HOME4, "global")));
29054
+ setCronDb(createCronDb(path64.join(HALO_HOME4, "global")));
28970
29055
  setEvoSpawner(realEvoSpawner);
28971
29056
  if (!AGENTCORE) {
28972
29057
  startEvoTicker();
@@ -28992,11 +29077,11 @@ var init_dist2 = __esm({
28992
29077
  console.log(`[Server] AgentCore runtime mode \u2014 workspace: ${config.server.agentcoreWorkspace}`);
28993
29078
  }
28994
29079
  FRONTEND_DIR = resolveFrontendDir();
28995
- app.use("/*", serveStatic({ root: path63.relative(process.cwd(), FRONTEND_DIR) }));
29080
+ app.use("/*", serveStatic({ root: path64.relative(process.cwd(), FRONTEND_DIR) }));
28996
29081
  app.get("/*", (c) => {
28997
- const indexPath = path63.join(FRONTEND_DIR, "index.html");
29082
+ const indexPath = path64.join(FRONTEND_DIR, "index.html");
28998
29083
  try {
28999
- const html = fs56.readFileSync(indexPath, "utf-8");
29084
+ const html = fs57.readFileSync(indexPath, "utf-8");
29000
29085
  return c.html(html);
29001
29086
  } catch {
29002
29087
  return c.text("Frontend not built. Run: cd packages/admin && npx next build", 503);
@@ -29767,11 +29852,11 @@ process.emitWarning = (warning, ...args) => {
29767
29852
 
29768
29853
  // packages/cli/src/index.ts
29769
29854
  init_server_supervisor();
29770
- import path64 from "node:path";
29771
- import fs57 from "node:fs";
29855
+ import path65 from "node:path";
29856
+ import fs58 from "node:fs";
29772
29857
  import { homedir as homedir25 } from "node:os";
29773
29858
  import { parseArgs as parseArgs2 } from "node:util";
29774
- var VERSION = "1.1.1";
29859
+ var VERSION = "1.1.2";
29775
29860
  var HELP_TOP = `Usage: halo <command> [options]
29776
29861
 
29777
29862
  Commands:
@@ -30028,8 +30113,8 @@ async function cmdSetup(argv = []) {
30028
30113
  const { buildBindProviderPlan: buildBindProviderPlan2, KEEP_CURRENT: KEEP_CURRENT2, AWS_CHAIN_PROVIDER: AWS_CHAIN_PROVIDER2 } = await Promise.resolve().then(() => (init_setup_bind_provider(), setup_bind_provider_exports));
30029
30114
  const { promptText: promptText2, promptPassword: promptPassword2, promptSelect: promptSelect2, promptMultiSelect: promptMultiSelect2 } = await Promise.resolve().then(() => (init_setup_prompts(), setup_prompts_exports));
30030
30115
  const nonInteractive = argv.includes("--non-interactive") || argv.includes("-y");
30031
- const haloHome2 = path64.join(homedir25(), ".halo");
30032
- const globalDir2 = path64.join(haloHome2, "global");
30116
+ const haloHome2 = path65.join(homedir25(), ".halo");
30117
+ const globalDir2 = path65.join(haloHome2, "global");
30033
30118
  ensureHaloHome2(haloHome2);
30034
30119
  process.stderr.write(`[setup] ${haloHome2}/ ready
30035
30120
  `);
@@ -30332,10 +30417,10 @@ async function cmdUpgrade() {
30332
30417
  );
30333
30418
  process.exit(1);
30334
30419
  }
30335
- const { spawnSync: spawnSync4 } = await import("node:child_process");
30420
+ const { spawnSync: spawnSync5 } = await import("node:child_process");
30336
30421
  process.stderr.write(`Checking npm for the latest ${PKG}\u2026
30337
30422
  `);
30338
- const view = spawnSync4("npm", ["view", PKG, "version"], { encoding: "utf-8" });
30423
+ const view = spawnSync5("npm", ["view", PKG, "version"], { encoding: "utf-8" });
30339
30424
  if (view.error || view.status !== 0) {
30340
30425
  const err = view.error?.message ?? view.stderr?.trim() ?? `exit ${view.status}`;
30341
30426
  process.stderr.write(`halo: \`npm view\` failed \u2014 ${err}
@@ -30353,10 +30438,40 @@ async function cmdUpgrade() {
30353
30438
  `);
30354
30439
  return;
30355
30440
  }
30441
+ const { ALLOW_SCRIPTS: ALLOW_SCRIPTS2, verifyNativeModules: verifyNativeModules2 } = await Promise.resolve().then(() => (init_upgrade_verify(), upgrade_verify_exports));
30356
30442
  process.stderr.write(`Upgrading ${installed} \u2192 ${latest}\u2026
30357
30443
  `);
30358
- const install = spawnSync4("npm", ["install", "-g", `${PKG}@latest`], { stdio: "inherit" });
30444
+ const install = spawnSync5(
30445
+ "npm",
30446
+ ["install", "-g", `--allow-scripts=${ALLOW_SCRIPTS2}`, `${PKG}@latest`],
30447
+ { stdio: "inherit" }
30448
+ );
30359
30449
  if (install.status === 0) {
30450
+ const npmRoot = spawnSync5("npm", ["root", "-g"], {
30451
+ encoding: "utf-8",
30452
+ shell: process.platform === "win32"
30453
+ });
30454
+ const rootDir = npmRoot.status === 0 ? npmRoot.stdout.trim() : "";
30455
+ if (!rootDir) {
30456
+ process.stderr.write(`halo: \`npm root -g\` failed \u2014 cannot verify the new install
30457
+ `);
30458
+ process.exit(1);
30459
+ }
30460
+ const broken = verifyNativeModules2(path65.join(rootDir, PKG));
30461
+ if (broken.length > 0) {
30462
+ process.stderr.write(
30463
+ `
30464
+ halo: upgrade installed, but native module(s) failed to load: ${broken.join(", ")}
30465
+ This usually means npm blocked the module's install scripts (npm 12+ allowScripts policy \u2014 check \`npm -v\`).
30466
+
30467
+ Fix \u2014 reinstall with the scripts allowed:
30468
+ sudo npm install -g --allow-scripts=${ALLOW_SCRIPTS2} ${PKG}@latest
30469
+ Or persist the allowance first, then reinstall:
30470
+ npm config set allow-scripts=${ALLOW_SCRIPTS2} --location=user
30471
+ `
30472
+ );
30473
+ process.exit(1);
30474
+ }
30360
30475
  process.stderr.write(
30361
30476
  `
30362
30477
  Upgraded to ${latest}. Run \`halo server restart\` (or your service manager's restart) to pick up the new templates.
@@ -30373,7 +30488,7 @@ halo: upgrade failed (npm exited ${install.status}). ${hint}
30373
30488
  async function cmdAgents(flags) {
30374
30489
  const { initRuntime: initRuntime2, listAgents: listAgents2 } = await Promise.resolve().then(() => (init_harness(), harness_exports));
30375
30490
  await initRuntime2();
30376
- const workspace = path64.resolve(flags.workspace ?? process.cwd());
30491
+ const workspace = path65.resolve(flags.workspace ?? process.cwd());
30377
30492
  const agents = await listAgents2(workspace);
30378
30493
  if (agents.length === 0) {
30379
30494
  process.stderr.write("No agents found.\n");
@@ -30389,7 +30504,7 @@ async function cmdAgents(flags) {
30389
30504
  async function cmdSessions(flags) {
30390
30505
  const { initRuntime: initRuntime2, listSessions: listSessions2 } = await Promise.resolve().then(() => (init_harness(), harness_exports));
30391
30506
  await initRuntime2();
30392
- const workspace = path64.resolve(flags.workspace ?? process.cwd());
30507
+ const workspace = path65.resolve(flags.workspace ?? process.cwd());
30393
30508
  const sessions = listSessions2(workspace);
30394
30509
  if (sessions.length === 0) {
30395
30510
  process.stderr.write("No sessions found.\n");
@@ -30502,11 +30617,11 @@ ${stdinContent}` : stdinContent;
30502
30617
  harness.destroy();
30503
30618
  }
30504
30619
  }
30505
- var SERVER_LOCK_PATH = path64.join(homedir25(), ".halo", "global", "server.lock");
30506
- var SERVER_LOG_PATH = path64.join(homedir25(), ".halo", "logs", "server.log");
30620
+ var SERVER_LOCK_PATH = path65.join(homedir25(), ".halo", "global", "server.lock");
30621
+ var SERVER_LOG_PATH = path65.join(homedir25(), ".halo", "logs", "server.log");
30507
30622
  function readServerPid() {
30508
30623
  try {
30509
- const txt = fs57.readFileSync(SERVER_LOCK_PATH, "utf-8").trim();
30624
+ const txt = fs58.readFileSync(SERVER_LOCK_PATH, "utf-8").trim();
30510
30625
  const n = parseInt(txt, 10);
30511
30626
  return Number.isFinite(n) && n > 0 ? n : null;
30512
30627
  } catch {
@@ -30593,8 +30708,8 @@ async function cmdServerStart(argv, _unused) {
30593
30708
  return;
30594
30709
  }
30595
30710
  const { spawn: spawn5 } = await import("node:child_process");
30596
- fs57.mkdirSync(path64.dirname(SERVER_LOG_PATH), { recursive: true });
30597
- const out = fs57.openSync(SERVER_LOG_PATH, "a");
30711
+ fs58.mkdirSync(path65.dirname(SERVER_LOG_PATH), { recursive: true });
30712
+ const out = fs58.openSync(SERVER_LOG_PATH, "a");
30598
30713
  const child = spawn5(process.argv[0], [process.argv[1], "server", "start"], {
30599
30714
  detached: true,
30600
30715
  stdio: ["ignore", out, out],
@@ -30695,7 +30810,7 @@ async function cmdServerLogs(argv) {
30695
30810
  lines: { type: "string", short: "n" }
30696
30811
  }
30697
30812
  });
30698
- if (!fs57.existsSync(SERVER_LOG_PATH)) {
30813
+ if (!fs58.existsSync(SERVER_LOG_PATH)) {
30699
30814
  process.stderr.write(`No log file at ${SERVER_LOG_PATH}
30700
30815
  `);
30701
30816
  return;
@@ -30713,7 +30828,7 @@ function exitMissingSetup() {
30713
30828
  process.exit(1);
30714
30829
  }
30715
30830
  function isHaloHomeReady() {
30716
- return fs57.existsSync(path64.join(homedir25(), ".halo", "global", ".template-version"));
30831
+ return fs58.existsSync(path65.join(homedir25(), ".halo", "global", ".template-version"));
30717
30832
  }
30718
30833
  async function main2() {
30719
30834
  const argv = process.argv.slice(2);