@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/admin-out/404.html +1 -1
- package/admin-out/_next/static/chunks/app/page-2076ec04359bb395.js +1 -0
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +2 -2
- package/dist/index.js +589 -474
- package/package.json +1 -1
- package/admin-out/_next/static/chunks/app/page-686cd94fdf9df63b.js +0 -1
- /package/admin-out/_next/static/{Qre3-OObLUgfkv8BEU08q → bZcoAqHBQyOvzwS5c84EJ}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{Qre3-OObLUgfkv8BEU08q → bZcoAqHBQyOvzwS5c84EJ}/_ssgManifest.js +0 -0
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(
|
|
1663
|
-
|
|
1664
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
4132
|
-
const fullPath = this.validatePath(
|
|
4163
|
+
async readFile(path66) {
|
|
4164
|
+
const fullPath = this.validatePath(path66);
|
|
4133
4165
|
return readFile(fullPath, "utf-8");
|
|
4134
4166
|
}
|
|
4135
|
-
async writeFile(
|
|
4136
|
-
const fullPath = this.validatePath(
|
|
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(
|
|
4142
|
-
const fullPath = isAbsolute(
|
|
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 "${
|
|
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 "${
|
|
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
|
|
4330
|
-
if (!
|
|
4361
|
+
const path66 = isRenameOrCopy ? parts[2] : parts[1];
|
|
4362
|
+
if (!path66 || seen.has(path66))
|
|
4331
4363
|
continue;
|
|
4332
|
-
seen.add(
|
|
4364
|
+
seen.add(path66);
|
|
4333
4365
|
files.push({
|
|
4334
|
-
path:
|
|
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(
|
|
4514
|
+
async getFileDiff(path66, staged, from, commit) {
|
|
4483
4515
|
if (commit) {
|
|
4484
|
-
const parentPath = from ||
|
|
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}:${
|
|
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 ||
|
|
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([`:${
|
|
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(
|
|
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
|
|
9127
|
-
|
|
9128
|
-
|
|
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
|
|
14064
|
-
import
|
|
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 =
|
|
14078
|
-
const ext =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
14179
|
+
const fd = fs28.openSync(resolved, "r");
|
|
14106
14180
|
let n = 0;
|
|
14107
14181
|
try {
|
|
14108
|
-
n =
|
|
14182
|
+
n = fs28.readSync(fd, buf, 0, MAX_FILE_BYTES, 0);
|
|
14109
14183
|
} finally {
|
|
14110
|
-
|
|
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 =
|
|
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
|
|
14394
|
-
import
|
|
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(
|
|
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
|
-
|
|
14412
|
-
|
|
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 =
|
|
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
|
|
14532
|
-
import
|
|
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 =
|
|
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 =
|
|
14566
|
-
const prefix =
|
|
14567
|
-
const dirRel =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
14589
|
-
insertPath =
|
|
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 =
|
|
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
|
|
14613
|
-
return `${before}@${kind} ${
|
|
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
|
|
16160
|
+
import path37 from "node:path";
|
|
16087
16161
|
import os3 from "node:os";
|
|
16088
16162
|
function tempDir() {
|
|
16089
|
-
return
|
|
16163
|
+
return path37.resolve(os3.tmpdir());
|
|
16090
16164
|
}
|
|
16091
16165
|
function isInTempDir(filePath) {
|
|
16092
|
-
const resolved =
|
|
16166
|
+
const resolved = path37.resolve(filePath);
|
|
16093
16167
|
const tmp = tempDir();
|
|
16094
|
-
return resolved === tmp || resolved.startsWith(tmp +
|
|
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 =
|
|
16111
|
-
const ws =
|
|
16112
|
-
return resolved === ws || resolved.startsWith(ws +
|
|
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 =
|
|
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
|
|
16210
|
+
import fs31 from "node:fs/promises";
|
|
16137
16211
|
import { existsSync as existsSync2, realpathSync as realpathSync3 } from "node:fs";
|
|
16138
|
-
import
|
|
16212
|
+
import path38 from "node:path";
|
|
16139
16213
|
async function resolveProjectPath(projectId) {
|
|
16140
|
-
if (
|
|
16214
|
+
if (path38.isAbsolute(projectId)) {
|
|
16141
16215
|
try {
|
|
16142
|
-
await
|
|
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 =
|
|
16225
|
+
let prefix = path38.resolve(filePath);
|
|
16152
16226
|
const tail = [];
|
|
16153
16227
|
while (!existsSync2(prefix)) {
|
|
16154
|
-
const parent =
|
|
16228
|
+
const parent = path38.dirname(prefix);
|
|
16155
16229
|
if (parent === prefix)
|
|
16156
16230
|
break;
|
|
16157
|
-
tail.unshift(
|
|
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 ?
|
|
16240
|
+
return tail.length > 0 ? path38.join(realPrefix, ...tail) : realPrefix;
|
|
16167
16241
|
}
|
|
16168
16242
|
function validatePath(filePath, projectPath) {
|
|
16169
|
-
const resolved = realpathBounded2(
|
|
16243
|
+
const resolved = realpathBounded2(path38.resolve(projectPath, filePath));
|
|
16170
16244
|
const proj = realpathBounded2(projectPath);
|
|
16171
|
-
return resolved === proj || resolved.startsWith(proj +
|
|
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
|
|
16260
|
+
import fs32 from "node:fs/promises";
|
|
16187
16261
|
import { createReadStream } from "node:fs";
|
|
16188
16262
|
import { Readable } from "node:stream";
|
|
16189
|
-
import
|
|
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
|
|
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
|
|
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 =
|
|
16235
|
-
const relativePath =
|
|
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 ?
|
|
16341
|
+
const targetPath = relPath ? path39.resolve(projectPath, relPath) : projectPath;
|
|
16268
16342
|
try {
|
|
16269
|
-
const stat = await
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ?
|
|
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 =
|
|
16383
|
-
const destDir =
|
|
16384
|
-
await
|
|
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
|
|
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 =
|
|
16415
|
-
await
|
|
16416
|
-
const stat = await
|
|
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 =
|
|
16439
|
-
await
|
|
16512
|
+
const absolutePath = path39.resolve(projectPath, filePath);
|
|
16513
|
+
await fs32.mkdir(path39.dirname(absolutePath), { recursive: true });
|
|
16440
16514
|
try {
|
|
16441
|
-
await
|
|
16515
|
+
await fs32.access(absolutePath);
|
|
16442
16516
|
return c.json({ error: "File already exists" }, 409);
|
|
16443
16517
|
} catch {
|
|
16444
16518
|
}
|
|
16445
|
-
await
|
|
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 =
|
|
16468
|
-
await
|
|
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 =
|
|
16491
|
-
const absNew =
|
|
16492
|
-
await
|
|
16493
|
-
await
|
|
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 =
|
|
16516
|
-
if (absolutePath ===
|
|
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
|
|
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 =
|
|
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 ?
|
|
16558
|
-
const stat = await
|
|
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 =
|
|
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 =
|
|
16622
|
-
const stat = await
|
|
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 =
|
|
16718
|
+
const absolutePath = path39.resolve(projectPath, filePath);
|
|
16645
16719
|
try {
|
|
16646
|
-
const stat = await
|
|
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
|
|
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 =
|
|
16755
|
+
const resolved = path39.resolve(body.path);
|
|
16682
16756
|
let stat;
|
|
16683
16757
|
try {
|
|
16684
|
-
stat = await
|
|
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 ===
|
|
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 || !
|
|
16773
|
+
if (!target || !path39.isAbsolute(target))
|
|
16700
16774
|
return c.json({ exists: false, reason: "absolute path required" });
|
|
16701
16775
|
try {
|
|
16702
|
-
const stat = await
|
|
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 (!
|
|
16784
|
+
if (!path39.isAbsolute(target))
|
|
16711
16785
|
return c.json({ error: "absolute path required" }, 400);
|
|
16712
16786
|
try {
|
|
16713
|
-
const resolved =
|
|
16714
|
-
const entries = await
|
|
16715
|
-
const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path:
|
|
16716
|
-
return c.json({ path: resolved, parent:
|
|
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
|
|
16808
|
+
import fs33 from "node:fs/promises";
|
|
16735
16809
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
16736
|
-
import
|
|
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
|
|
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 =
|
|
16925
|
+
const absolutePath = path40.resolve(projectPath, filePath);
|
|
16852
16926
|
try {
|
|
16853
|
-
const stat = await
|
|
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
|
|
17060
|
-
import
|
|
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 (
|
|
17067
|
-
lines =
|
|
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
|
-
|
|
17151
|
+
fs34.writeFileSync(GIT_CREDENTIALS_PATH, kept.join("\n") + "\n", { encoding: "utf-8", mode: 384 });
|
|
17078
17152
|
try {
|
|
17079
|
-
|
|
17153
|
+
fs34.chmodSync(GIT_CREDENTIALS_PATH, 384);
|
|
17080
17154
|
} catch {
|
|
17081
17155
|
}
|
|
17082
17156
|
}
|
|
17083
17157
|
function ensureCredentialHelper() {
|
|
17084
|
-
const current =
|
|
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
|
-
|
|
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 (!
|
|
17170
|
+
if (!fs34.existsSync(GIT_CREDENTIALS_PATH))
|
|
17097
17171
|
return [];
|
|
17098
|
-
const lines =
|
|
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 (!
|
|
17186
|
+
if (!fs34.existsSync(GIT_CREDENTIALS_PATH))
|
|
17113
17187
|
return;
|
|
17114
|
-
const lines =
|
|
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
|
-
|
|
17198
|
+
fs34.writeFileSync(GIT_CREDENTIALS_PATH, kept.length ? kept.join("\n") + "\n" : "", { encoding: "utf-8", mode: 384 });
|
|
17125
17199
|
try {
|
|
17126
|
-
|
|
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 =
|
|
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
|
|
17214
|
+
import fs35 from "node:fs";
|
|
17141
17215
|
import os4 from "node:os";
|
|
17142
|
-
import
|
|
17143
|
-
import { spawnSync as
|
|
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 =
|
|
17225
|
+
const fd = fs35.openSync(abs, "r");
|
|
17152
17226
|
try {
|
|
17153
17227
|
const buf = Buffer.alloc(64);
|
|
17154
|
-
const n =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
17251
|
+
const abs = path42.join(SSH_DIR, name);
|
|
17178
17252
|
try {
|
|
17179
|
-
if (!
|
|
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 =
|
|
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 =
|
|
17205
|
-
const askpass =
|
|
17206
|
-
|
|
17207
|
-
const r =
|
|
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 ${
|
|
17296
|
+
console.log(`[GitSsh] unlocked key ${path42.basename(keyPath)}`);
|
|
17223
17297
|
return { ok: true };
|
|
17224
17298
|
}
|
|
17225
|
-
console.log(`[GitSsh] unlock failed for ${
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
17650
|
-
const resolved =
|
|
17651
|
-
if (
|
|
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
|
|
17728
|
-
import
|
|
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
|
|
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
|
|
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 =
|
|
17920
|
+
const agentDir = path44.join(dir, entryName);
|
|
17836
17921
|
try {
|
|
17837
|
-
const stat = await
|
|
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 =
|
|
17928
|
+
const yamlPath = path44.join(agentDir, "agent.yaml");
|
|
17844
17929
|
try {
|
|
17845
|
-
const yamlStat = await
|
|
17846
|
-
const parsed = _yamlCache.get(yamlPath, yamlStat.mtimeMs) ?? _yamlCache.set(yamlPath, yamlStat.mtimeMs, parseAgentYaml(await
|
|
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 =
|
|
17868
|
-
const yamlPath =
|
|
17952
|
+
const defaultDir = path44.join(dir, "default");
|
|
17953
|
+
const yamlPath = path44.join(defaultDir, "agent.yaml");
|
|
17869
17954
|
try {
|
|
17870
|
-
await
|
|
17955
|
+
await fs36.access(yamlPath);
|
|
17871
17956
|
} catch {
|
|
17872
17957
|
await ensureDir2(defaultDir);
|
|
17873
|
-
await
|
|
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 =
|
|
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 =
|
|
18008
|
+
baseDir = path44.join(body.projectId, ".halo", "agents");
|
|
17924
18009
|
} else {
|
|
17925
18010
|
baseDir = GLOBAL_AGENTS_DIR4;
|
|
17926
18011
|
}
|
|
17927
|
-
const agentDir =
|
|
18012
|
+
const agentDir = path44.join(baseDir, id);
|
|
17928
18013
|
try {
|
|
17929
|
-
await
|
|
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
|
|
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
|
|
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
|
|
17949
|
-
await
|
|
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 =
|
|
18048
|
+
agentDir = path44.join(projectId, ".halo", "agents", id);
|
|
17964
18049
|
} else {
|
|
17965
|
-
agentDir =
|
|
18050
|
+
agentDir = path44.join(GLOBAL_AGENTS_DIR4, id);
|
|
17966
18051
|
}
|
|
17967
|
-
const yamlPath =
|
|
18052
|
+
const yamlPath = path44.join(agentDir, "agent.yaml");
|
|
17968
18053
|
try {
|
|
17969
|
-
const content = await
|
|
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 =
|
|
18071
|
+
agentDir = path44.join(body.projectId, ".halo", "agents", id);
|
|
17987
18072
|
} else {
|
|
17988
|
-
agentDir =
|
|
18073
|
+
agentDir = path44.join(GLOBAL_AGENTS_DIR4, id);
|
|
17989
18074
|
}
|
|
17990
|
-
const yamlPath =
|
|
18075
|
+
const yamlPath = path44.join(agentDir, "agent.yaml");
|
|
17991
18076
|
try {
|
|
17992
|
-
await
|
|
18077
|
+
await fs36.access(agentDir);
|
|
17993
18078
|
} catch {
|
|
17994
18079
|
return c.json({ error: "Agent not found" }, 404);
|
|
17995
18080
|
}
|
|
17996
|
-
await
|
|
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 =
|
|
18093
|
+
agentDir = path44.join(projectId, ".halo", "agents", id);
|
|
18009
18094
|
} else {
|
|
18010
|
-
agentDir =
|
|
18095
|
+
agentDir = path44.join(GLOBAL_AGENTS_DIR4, id);
|
|
18011
18096
|
}
|
|
18012
18097
|
try {
|
|
18013
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
18183
|
+
return path44.join(homedir19(), ".halo", "global", "internal-sessions", agentId);
|
|
18099
18184
|
}
|
|
18100
|
-
const base = projectId ?
|
|
18101
|
-
return
|
|
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
|
|
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
|
|
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 =
|
|
18235
|
+
const filePath = path44.join(dir, `${sessionId}.json`);
|
|
18151
18236
|
try {
|
|
18152
|
-
const raw = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
18248
|
-
import
|
|
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
|
|
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
|
|
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
|
|
18280
|
-
await
|
|
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
|
|
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
|
|
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 =
|
|
18410
|
+
const skillDir = path45.join(dir, entryName);
|
|
18326
18411
|
try {
|
|
18327
|
-
const stat = await
|
|
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 =
|
|
18418
|
+
const skillMdPath = path45.join(skillDir, "SKILL.md");
|
|
18334
18419
|
try {
|
|
18335
|
-
const mdStat = await
|
|
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
|
|
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 =
|
|
18428
|
+
const yamlPath = path45.join(skillDir, "skill.yaml");
|
|
18344
18429
|
try {
|
|
18345
|
-
const yamlContent = await
|
|
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 =
|
|
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 =
|
|
18472
|
+
baseDir = path45.join(body.projectId, ".halo", "skills");
|
|
18388
18473
|
} else {
|
|
18389
18474
|
baseDir = GLOBAL_SKILLS_DIR2;
|
|
18390
18475
|
}
|
|
18391
|
-
const skillDir =
|
|
18476
|
+
const skillDir = path45.join(baseDir, id);
|
|
18392
18477
|
try {
|
|
18393
|
-
await
|
|
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
|
|
18399
|
-
const settingsPath = scope === "workspace" && body.projectId ?
|
|
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 =
|
|
18497
|
+
skillDir = path45.join(projectId, ".halo", "skills", id);
|
|
18413
18498
|
} else {
|
|
18414
|
-
skillDir =
|
|
18499
|
+
skillDir = path45.join(GLOBAL_SKILLS_DIR2, id);
|
|
18415
18500
|
}
|
|
18416
18501
|
try {
|
|
18417
|
-
await
|
|
18502
|
+
await fs37.access(skillDir);
|
|
18418
18503
|
} catch {
|
|
18419
18504
|
return c.json({ error: "Skill not found" }, 404);
|
|
18420
18505
|
}
|
|
18421
|
-
await
|
|
18422
|
-
const settingsPath = scope === "workspace" && projectId ?
|
|
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 =
|
|
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
|
|
18454
|
-
import
|
|
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
|
|
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
|
|
18467
|
-
await
|
|
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
|
|
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
|
|
18584
|
-
if (!declaredMap.has(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
18731
|
-
import
|
|
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 (!
|
|
18821
|
+
if (!fs39.existsSync(srcDir))
|
|
18737
18822
|
return false;
|
|
18738
|
-
|
|
18823
|
+
fs39.mkdirSync(path47.dirname(outZip), { recursive: true });
|
|
18739
18824
|
const zip = new JSZip();
|
|
18740
18825
|
const walk = (dir, rel) => {
|
|
18741
|
-
const entries =
|
|
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 =
|
|
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,
|
|
18837
|
+
zip.file(childRel, fs39.readFileSync(abs), { date: fs39.statSync(abs).mtime });
|
|
18753
18838
|
} else if (e.isSymbolicLink()) {
|
|
18754
18839
|
try {
|
|
18755
|
-
if (
|
|
18756
|
-
zip.file(childRel,
|
|
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" }),
|
|
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
|
-
|
|
18859
|
+
fs39.rmSync(dir, { recursive: true, force: true });
|
|
18775
18860
|
} catch {
|
|
18776
18861
|
}
|
|
18777
18862
|
}
|
|
18778
18863
|
function rmFile(p) {
|
|
18779
18864
|
try {
|
|
18780
|
-
|
|
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 (!
|
|
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 (!
|
|
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
|
|
18928
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
19508
|
-
import
|
|
19592
|
+
import path49 from "node:path";
|
|
19593
|
+
import fs41 from "node:fs";
|
|
19509
19594
|
function createChannelDb(secretsDir) {
|
|
19510
|
-
const channelsDir =
|
|
19511
|
-
|
|
19512
|
-
const dbPath =
|
|
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
|
|
19563
|
-
import
|
|
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 (!
|
|
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 (!
|
|
19664
|
+
if (!path50.isAbsolute(workspacePath))
|
|
19580
19665
|
return "workspacePath must be absolute";
|
|
19581
|
-
if (!
|
|
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
|
|
19882
|
-
import
|
|
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 =
|
|
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 =
|
|
19978
|
+
const skillMd = path51.join(dir, entry, "SKILL.md");
|
|
19894
19979
|
let stat;
|
|
19895
19980
|
try {
|
|
19896
|
-
stat =
|
|
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(
|
|
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(
|
|
20076
|
+
const wsSkills = scanSkills2(path51.join(wsPath, ".halo", "skills"));
|
|
19992
20077
|
return {
|
|
19993
20078
|
path: wsPath,
|
|
19994
|
-
key:
|
|
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 =
|
|
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 =
|
|
20013
|
-
if (!
|
|
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 =
|
|
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:
|
|
20174
|
+
key: path51.basename(wsPath) || wsPath,
|
|
20090
20175
|
label,
|
|
20091
20176
|
sessions,
|
|
20092
20177
|
counts,
|
|
20093
20178
|
totalSessions: counts.total,
|
|
20094
|
-
skills: scanSkills2(
|
|
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 =
|
|
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 =
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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
|
|
20409
|
-
import
|
|
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 =
|
|
20415
|
-
|
|
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
|
|
20673
|
-
import
|
|
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 =
|
|
20682
|
-
if (norm === "/" || norm ===
|
|
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 +
|
|
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 =
|
|
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 =
|
|
20911
|
+
const raw = path53.relative(root, absPath);
|
|
20827
20912
|
if (!raw || raw.startsWith(".."))
|
|
20828
20913
|
return;
|
|
20829
|
-
const rel = raw.split(
|
|
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 =
|
|
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
|
|
20917
|
-
import
|
|
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 =
|
|
20942
|
-
if (
|
|
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 =
|
|
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 =
|
|
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 =
|
|
21007
|
-
if (!
|
|
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
|
|
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 (
|
|
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
|
|
21972
|
-
import
|
|
22056
|
+
import path56 from "node:path";
|
|
22057
|
+
import fs47 from "node:fs";
|
|
21973
22058
|
function createCronDb(globalDir2) {
|
|
21974
|
-
|
|
21975
|
-
const dbPath =
|
|
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
|
|
22174
|
-
import
|
|
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
|
|
22330
|
+
return path57.join(homedir23(), ".halo", "global", "logs", "cron");
|
|
22246
22331
|
}
|
|
22247
22332
|
function logPathFor(runId) {
|
|
22248
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
22762
|
+
if (!fs48.existsSync(dir))
|
|
22678
22763
|
return;
|
|
22679
22764
|
const cutoff = Date.now() - LOG_RETENTION_DAYS * DAY_MS2;
|
|
22680
|
-
for (const name of
|
|
22681
|
-
const p =
|
|
22765
|
+
for (const name of fs48.readdirSync(dir)) {
|
|
22766
|
+
const p = path57.join(dir, name);
|
|
22682
22767
|
try {
|
|
22683
|
-
const stat =
|
|
22768
|
+
const stat = fs48.statSync(p);
|
|
22684
22769
|
if (stat.mtimeMs < cutoff)
|
|
22685
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
23225
|
-
WRAPPER_PATH =
|
|
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
|
|
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: !
|
|
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
|
|
24747
|
-
import
|
|
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
|
|
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:
|
|
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
|
|
24961
|
-
import
|
|
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 =
|
|
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
|
|
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
|
|
25440
|
-
import
|
|
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: !
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
25759
|
+
path66 = override;
|
|
25675
25760
|
}
|
|
25676
|
-
const resolved = resolveAccountWorkspace({ ...account, workspacePath:
|
|
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
|
|
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: !
|
|
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
|
|
26216
|
-
const resolved =
|
|
26217
|
-
const root =
|
|
26218
|
-
if (resolved !== root && !resolved.startsWith(root +
|
|
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 =
|
|
26225
|
-
realRoot =
|
|
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 +
|
|
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 =
|
|
26317
|
+
const stat = fs54.statSync(real);
|
|
26233
26318
|
if (stat.isDirectory())
|
|
26234
26319
|
return c.json({ error: "cannot serve directory" }, 400);
|
|
26235
|
-
const ext =
|
|
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 =
|
|
26244
|
-
const data =
|
|
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
|
|
26586
|
-
const
|
|
26587
|
-
const filename = args.filename ??
|
|
26588
|
-
const stat =
|
|
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 =
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
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: !
|
|
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
|
|
27688
|
-
const
|
|
27689
|
-
const buf =
|
|
27690
|
-
const filename =
|
|
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
|
|
27722
|
-
const
|
|
27723
|
-
const buf =
|
|
27724
|
-
const filename = args.filename ??
|
|
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
|
|
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 =
|
|
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:
|
|
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 =
|
|
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
|
|
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: !
|
|
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
|
|
28669
|
-
if (PUBLIC_PATHS.some((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 (!
|
|
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
|
|
28715
|
-
import
|
|
28716
|
-
import { spawnSync as
|
|
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 (
|
|
28807
|
+
if (fs57.existsSync(path64.join(dir, "pnpm-workspace.yaml")))
|
|
28723
28808
|
return dir;
|
|
28724
|
-
const parent =
|
|
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
|
-
|
|
28734
|
-
const fd =
|
|
28735
|
-
const result =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28756
|
-
|
|
28757
|
-
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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
|
|
28808
|
-
const bundled =
|
|
28809
|
-
if (
|
|
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 =
|
|
28812
|
-
if (
|
|
28896
|
+
const sibling = path64.resolve(__dirname3, "..", "..", "admin", "out");
|
|
28897
|
+
if (fs57.existsSync(path64.join(sibling, "index.html")))
|
|
28813
28898
|
return sibling;
|
|
28814
|
-
return
|
|
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 =
|
|
28955
|
+
__dirname3 = path64.dirname(fileURLToPath3(import.meta.url));
|
|
28871
28956
|
PORT = config.server.port;
|
|
28872
|
-
HALO_VERSION = "1.1.
|
|
28957
|
+
HALO_VERSION = "1.1.2";
|
|
28873
28958
|
PROJECT_ROOT = findProjectRoot();
|
|
28874
|
-
HALO_HOME4 =
|
|
28959
|
+
HALO_HOME4 = path64.join(homedir24(), ".halo");
|
|
28875
28960
|
GIT_SHA = readGitSha();
|
|
28876
|
-
if (!
|
|
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(
|
|
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(
|
|
29051
|
+
channelDb = createChannelDb(path64.join(HALO_HOME4, "secrets"));
|
|
28967
29052
|
setChannelDb(channelDb);
|
|
28968
|
-
setEvoDb(createEvoDb(
|
|
28969
|
-
setCronDb(createCronDb(
|
|
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:
|
|
29080
|
+
app.use("/*", serveStatic({ root: path64.relative(process.cwd(), FRONTEND_DIR) }));
|
|
28996
29081
|
app.get("/*", (c) => {
|
|
28997
|
-
const indexPath =
|
|
29082
|
+
const indexPath = path64.join(FRONTEND_DIR, "index.html");
|
|
28998
29083
|
try {
|
|
28999
|
-
const html =
|
|
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
|
|
29771
|
-
import
|
|
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.
|
|
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 =
|
|
30032
|
-
const globalDir2 =
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
30506
|
-
var SERVER_LOG_PATH =
|
|
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 =
|
|
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
|
-
|
|
30597
|
-
const out =
|
|
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 (!
|
|
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
|
|
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);
|