hob-bot 1.4.0 → 1.5.0
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/README.md +19 -8
- package/README.zh-CN.md +19 -8
- package/bin/hob.mjs +8 -2
- package/dist/web/assets/{DesktopView-A11jmUwu.js → DesktopView-3QDYo0UU.js} +1 -1
- package/dist/web/assets/index-Dzxrt9T3.js +283 -0
- package/dist/web/assets/index-FUtuyxyO.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +2 -1
- package/server/src/actions.ts +2 -1
- package/server/src/boot.ts +8 -0
- package/server/src/config.ts +52 -11
- package/server/src/cron-text.ts +14 -1
- package/server/src/desktop-tools.ts +2 -1
- package/server/src/files.ts +27 -7
- package/server/src/http.ts +17 -5
- package/server/src/jobs.ts +3 -0
- package/server/src/knowledge.ts +9 -4
- package/server/src/layout-cli.ts +25 -0
- package/server/src/layout.ts +749 -0
- package/server/src/main.ts +3 -0
- package/server/src/mcp.ts +3 -2
- package/server/src/pages.ts +5 -3
- package/server/src/prompt.ts +4 -3
- package/server/src/push.ts +3 -2
- package/server/src/router-tools.ts +50 -14
- package/server/src/router.ts +3 -3
- package/server/src/routines.ts +171 -21
- package/server/src/search.ts +1 -1
- package/server/src/secrets.ts +3 -2
- package/server/src/settings.ts +7 -6
- package/server/src/store.ts +123 -40
- package/server/src/transcript.ts +11 -3
- package/server/src/usage-prices.ts +5 -4
- package/server/src/usage.ts +15 -5
- package/shared/types.ts +27 -1
- package/dist/web/assets/index-B-YlVbpo.js +0 -270
- package/dist/web/assets/index-DkSQ-ygR.css +0 -1
package/server/src/http.ts
CHANGED
|
@@ -20,13 +20,14 @@ import type {
|
|
|
20
20
|
ModelThinkingSaved,
|
|
21
21
|
ModelsView,
|
|
22
22
|
PushSubscriptionJson,
|
|
23
|
+
RoutineCheck,
|
|
23
24
|
SettingsPatch,
|
|
24
25
|
UsagePricePatch,
|
|
25
26
|
UsagePriceSaved,
|
|
26
27
|
} from "../../shared/types.ts";
|
|
27
28
|
import { parseSprite } from "../../shared/sprites.ts";
|
|
28
29
|
import { answerApproval, archiveAssistant, createFromDraft, dismissDraft } from "./actions.ts";
|
|
29
|
-
import { setPassword } from "./config.ts";
|
|
30
|
+
import { setPassword, assistantFilesDir, cacheDir } from "./config.ts";
|
|
30
31
|
import { newId } from "./ids.ts";
|
|
31
32
|
import { claudeStatus } from "./claude-status.ts";
|
|
32
33
|
import { checkDesktopUrl } from "./desktop.ts";
|
|
@@ -570,7 +571,7 @@ export function createApp(runtime: HobRuntime, options: { webDir?: string; home:
|
|
|
570
571
|
const path = await generateImage(
|
|
571
572
|
{
|
|
572
573
|
home: options.home,
|
|
573
|
-
dir: join(options.home, "previews"),
|
|
574
|
+
dir: join(cacheDir(options.home), "previews"),
|
|
574
575
|
config,
|
|
575
576
|
...(typeof body.apiKey === "string" && body.apiKey.trim() ? { apiKey: body.apiKey.trim() } : {}),
|
|
576
577
|
},
|
|
@@ -832,6 +833,8 @@ export function createApp(runtime: HobRuntime, options: { webDir?: string; home:
|
|
|
832
833
|
...(typeof body.dutyLong === "string" ? { dutyLong: body.dutyLong } : {}),
|
|
833
834
|
...(typeof body.sprite === "string" ? { sprite: body.sprite } : {}),
|
|
834
835
|
...(typeof body.pinned === "boolean" ? { pinned: body.pinned } : {}),
|
|
836
|
+
// 置顶区里的位置:拖动头像换位置后按 0、1、2… 存,取整且不为负
|
|
837
|
+
...(typeof body.pinOrder === "number" && Number.isFinite(body.pinOrder) ? { pinOrder: Math.max(0, Math.round(body.pinOrder)) } : {}),
|
|
835
838
|
...(typeof body.hidden === "boolean" ? { hidden: body.hidden } : {}),
|
|
836
839
|
...(typeof body.notify === "boolean" ? { notify: body.notify } : {}),
|
|
837
840
|
...(models ? { models } : {}),
|
|
@@ -898,6 +901,8 @@ export function createApp(runtime: HobRuntime, options: { webDir?: string; home:
|
|
|
898
901
|
name?: string;
|
|
899
902
|
instruction?: string;
|
|
900
903
|
schedules?: string[];
|
|
904
|
+
check?: RoutineCheck;
|
|
905
|
+
once?: boolean;
|
|
901
906
|
};
|
|
902
907
|
if (!body.assistantId || !body.name || !body.instruction || !body.schedules?.length) {
|
|
903
908
|
return c.json({ error: "缺字段:assistantId、name、instruction、schedules" }, 400);
|
|
@@ -908,6 +913,8 @@ export function createApp(runtime: HobRuntime, options: { webDir?: string; home:
|
|
|
908
913
|
name: body.name,
|
|
909
914
|
instruction: body.instruction,
|
|
910
915
|
schedules: body.schedules,
|
|
916
|
+
...(body.check ? { check: body.check } : {}),
|
|
917
|
+
...(body.once ? { once: true } : {}),
|
|
911
918
|
});
|
|
912
919
|
return c.json({ routine });
|
|
913
920
|
} catch (error) {
|
|
@@ -922,6 +929,8 @@ export function createApp(runtime: HobRuntime, options: { webDir?: string; home:
|
|
|
922
929
|
schedules?: string[];
|
|
923
930
|
active?: boolean;
|
|
924
931
|
skill?: string | null;
|
|
932
|
+
check?: RoutineCheck | null;
|
|
933
|
+
once?: boolean;
|
|
925
934
|
};
|
|
926
935
|
try {
|
|
927
936
|
const routine = routines.update(c.req.param("id"), body);
|
|
@@ -943,8 +952,8 @@ export function createApp(runtime: HobRuntime, options: { webDir?: string; home:
|
|
|
943
952
|
|
|
944
953
|
app.post("/api/routines/:id/run", async (c) => {
|
|
945
954
|
try {
|
|
946
|
-
const
|
|
947
|
-
return c.json(
|
|
955
|
+
const result = await routines.runNow(c.req.param("id"));
|
|
956
|
+
return c.json(result);
|
|
948
957
|
} catch (error) {
|
|
949
958
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 404);
|
|
950
959
|
}
|
|
@@ -1053,8 +1062,11 @@ export function createApp(runtime: HobRuntime, options: { webDir?: string; home:
|
|
|
1053
1062
|
});
|
|
1054
1063
|
|
|
1055
1064
|
app.post("/api/uploads", async (c) => {
|
|
1065
|
+
const assistantId = (c.req.query("assistantId") ?? "").trim();
|
|
1066
|
+
if (!assistantId) return c.json({ error: "缺少 assistantId:上传的文件要放在某个助手的目录下" }, 400);
|
|
1067
|
+
if (!store.assistant(assistantId)) return c.json({ error: `助手不存在:${assistantId}` }, 404);
|
|
1056
1068
|
const body = await c.req.parseBody({ all: true });
|
|
1057
|
-
const uploads =
|
|
1069
|
+
const uploads = assistantFilesDir(options.home, assistantId, "uploads");
|
|
1058
1070
|
mkdirSync(uploads, { recursive: true });
|
|
1059
1071
|
const saved: { path: string; name: string }[] = [];
|
|
1060
1072
|
const values = Object.values(body).flatMap((value) => (Array.isArray(value) ? value : [value]));
|
package/server/src/jobs.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
} from "../../shared/types.ts";
|
|
19
19
|
import { IMAGE_TYPES, isImagePath } from "../../shared/media.ts";
|
|
20
20
|
import { newId } from "./ids.ts";
|
|
21
|
+
import { resolveMovedPath } from "./layout.ts";
|
|
21
22
|
import { availableDesktopTools, type DesktopToolSession } from "./desktop-tools.ts";
|
|
22
23
|
import { imageConfigured } from "./image.ts";
|
|
23
24
|
import { boundKnowledge, findKnowledgeTool, searchKnowledgeTool } from "./knowledge-tools.ts";
|
|
@@ -220,6 +221,8 @@ export class JobManager {
|
|
|
220
221
|
cwd: job.cwd ?? "",
|
|
221
222
|
save: (stepId, text) => this.store.writeStepOutput(jobId, stepId, text),
|
|
222
223
|
mcpServers: mcpServerDisplay(this.runtime.home),
|
|
224
|
+
// 迁移前写进会话的绝对路径:换成新位置后,详情页里还是短文件名
|
|
225
|
+
resolvePath: (path) => resolveMovedPath(this.runtime.home, path),
|
|
223
226
|
});
|
|
224
227
|
return [...steps, ...(this.workers.get(jobId)?.queued ?? [])];
|
|
225
228
|
}
|
package/server/src/knowledge.ts
CHANGED
|
@@ -7,10 +7,15 @@ import type { EmbeddingSettings } from "../../shared/types.ts";
|
|
|
7
7
|
import { embed, embeddingKey, embeddingModel } from "./embedding.ts";
|
|
8
8
|
import { extractChunks, UnsupportedIndex, type IndexedChunk } from "./knowledge-index.ts";
|
|
9
9
|
import type { Bus } from "./bus.ts";
|
|
10
|
-
import { looksText } from "./files.ts";
|
|
10
|
+
import { looksText, isInPagesDir } from "./files.ts";
|
|
11
11
|
import { newId } from "./ids.ts";
|
|
12
12
|
import type { Store } from "./store.ts";
|
|
13
13
|
|
|
14
|
+
/** 从数据目录内的相对路径推任务 id:老布局 jobs/<jid>/…,新布局 assistants/<aid>/jobs/<jid>/… */
|
|
15
|
+
function jobIdFromPath(rel: string): string | undefined {
|
|
16
|
+
return /^(?:assistants[/\\][^/\\]+[/\\])?jobs[/\\]([^/\\]+)[/\\]/.exec(rel)?.[1];
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
type Entry = KnowledgeEntry & { sourceKey?: string };
|
|
15
20
|
type CollectInput = { origin: "output" | "chat"; path: string; name?: string; description?: string; assistantId: string; itemId?: string; jobId?: string };
|
|
16
21
|
type Query = { origin?: string; collection?: string; kind?: string; assistantId?: string; q?: string; before?: string; limit?: number };
|
|
@@ -188,7 +193,7 @@ export class KnowledgeStore {
|
|
|
188
193
|
const originalName = basename(input.path);
|
|
189
194
|
const name = input.name || (input.origin === "chat" ? originalName.replace(/^u-[^-]+-/, "") : originalName);
|
|
190
195
|
const kind = fileKind(originalName) ?? (looksText(data) ? "text" : "other");
|
|
191
|
-
const inPages =
|
|
196
|
+
const inPages = isInPagesDir(relative(this.home, resolve(input.path)));
|
|
192
197
|
const ext = extname(originalName).toLowerCase().replace(/[^.a-z0-9]/g, "");
|
|
193
198
|
const path = join(this.root, inPages ? "pages" : "blobs", `${hash}${inPages ? ".html" : ext}`);
|
|
194
199
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -201,8 +206,8 @@ export class KnowledgeStore {
|
|
|
201
206
|
: content;
|
|
202
207
|
writeFileSync(join(this.root, "text", `${hash}.txt`), Buffer.from(text).subarray(0, 64 * 1024), { mode: 0o600 });
|
|
203
208
|
}
|
|
204
|
-
const rel = relative(
|
|
205
|
-
const jobId = input.jobId || (
|
|
209
|
+
const rel = relative(this.home, resolve(input.path));
|
|
210
|
+
const jobId = input.jobId || jobIdFromPath(rel);
|
|
206
211
|
const description = input.description || (jobId ? this.store.job(jobId)?.title : undefined);
|
|
207
212
|
const now = this.timestamp(entries);
|
|
208
213
|
if (previous) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// HOB_HOME 布局迁移的命令行入口:npm run layout -- --dry-run | --restore
|
|
2
|
+
//
|
|
3
|
+
// 正常情况下启动时自动迁移;这两个开关给试跑和出事后的恢复用。
|
|
4
|
+
// --dry-run 只打印计划,什么都不动;--restore 从 backup-v1-* 恢复成老布局。
|
|
5
|
+
import { resolveHome } from "./config.ts";
|
|
6
|
+
import { migrateHome, restoreHome } from "./layout.ts";
|
|
7
|
+
|
|
8
|
+
const args = new Set(process.argv.slice(2));
|
|
9
|
+
const home = resolveHome();
|
|
10
|
+
const usage = "用法:npm run layout -- [--dry-run | --restore](npm 安装的用 hob layout [--dry-run | --restore])";
|
|
11
|
+
|
|
12
|
+
if (args.has("--help") || args.has("-h")) {
|
|
13
|
+
console.log(usage);
|
|
14
|
+
} else if (args.has("--dry-run")) {
|
|
15
|
+
console.log(`数据目录:${home}`);
|
|
16
|
+
const summary = migrateHome(home, { dryRun: true });
|
|
17
|
+
console.log(summary.status === "dry-run" ? "(dry-run 没有改动任何文件)" : `当前状态:${summary.status}`);
|
|
18
|
+
} else if (args.has("--restore")) {
|
|
19
|
+
console.log(`数据目录:${home}`);
|
|
20
|
+
const result = restoreHome(home);
|
|
21
|
+
console.log(`已恢复到老布局:${result.files} 个文件,备份在 ${result.backupDir}。下次启动会重新迁移。`);
|
|
22
|
+
} else {
|
|
23
|
+
console.error(usage);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|