@xopcai/xopc 0.0.100 → 0.0.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/feishu/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/src/adapters/streaming-adapter.js +1 -1
- package/dist/extensions/telegram/src/inbound-processor.js +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/src/update-offset-store.js +1 -1
- package/dist/extensions/telegram/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/api/api.js +7 -5
- package/dist/extensions/weixin/src/api/api.js.map +1 -1
- package/dist/extensions/weixin/src/auth/accounts.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js +1 -1
- package/dist/gateway/static/root/assets/{agents-C5jtBK-H.js → agents-DQ0mQxIA.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-smJl-qf_.js → apps-page-Uv4kOmDE.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-DBAxQXb-.js → attachment-preview-renderer-CybMSIoS.js} +2 -2
- package/dist/gateway/static/root/assets/{attachment-process-heavy-Csq3TrrP.js → attachment-process-heavy-RnGYy9bC.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-CH6KgFK6.js → channels-settings-BEtP53V6.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-CV70xvY7.js → channels-status-swr-CxTtS1hI.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-api-CgBO8B7I.js → cron-api-CznlPIZT.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-Be0cW1cR.js → cron-page-Dx6dE3eD.js} +1 -1
- package/dist/gateway/static/root/assets/{dist-P8HRGUuh.js → dist-DH4IuRV0.js} +1 -1
- package/dist/gateway/static/root/assets/{docx-preview-Dxj1EkCO.js → docx-preview-CajIk2FR.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-BowuycYu.js → extension-debug-page-BNkhvRQW.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-27-Zpv8O.js → extension-page-CcIGQah2.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DDFa_rIq.js → extension-settings-page-DuDCSb3J.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-BWLssxEb.js → field-primitives-BUwUiDf5.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-BVuYNYT_.js → heartbeat-config-api-BjFW1cl-.js} +1 -1
- package/dist/gateway/static/root/assets/{index-CRDAmr3W.js → index-DKgHC1Ta.js} +9 -9
- package/dist/gateway/static/root/assets/{logs-page-BDX8IrDQ.js → logs-page-CiG0m81R.js} +1 -1
- package/dist/gateway/static/root/assets/{note-detail-page-BmQ9KeiA.js → note-detail-page-DG3DVFTf.js} +2 -2
- package/dist/gateway/static/root/assets/{note-time-14I9Sgqh.js → note-time-DjlllCF3.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-BMfP0FFf.js → notes-page-D40ZpS0Y.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-C3scyChq.js → sessions-page-BvtdbmW8.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-advanced-gate-2ODuCjHP.js → settings-advanced-gate-BQamRjkV.js} +2 -2
- package/dist/gateway/static/root/assets/{settings-form-section-DUoGKAiW.js → settings-form-section-C3_sEFfD.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-CttAfOWV.js → settings-page-D972_jaS.js} +2 -2
- package/dist/gateway/static/root/assets/{share-preview-page-B77xqiiI.js → share-preview-page-CKCTMo0_.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-B_bAZGzB.js → skills-page-QwFCB4yV.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-BgCPrmxU.js → theme-store-B6KXPHgK.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-CCGejlcX.js → utils-DCm11_KW.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-DZzzihcY.js → voice-api-key-field-CywiB7Dw.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-page.utils-D5RMAmT4.js → workflow-page.utils-g57BRfEd.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-DEYpC6Bv.js → workflows-page-Be7l_SWw.js} +1 -1
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +23 -7
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/agent-scope.js +1 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/context/workspace-state.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +1 -0
- package/dist/src/agent/embedded/run-for-session.js.map +1 -1
- package/dist/src/agent/goals/goal-run-store.js +4 -4
- package/dist/src/agent/goals/persistent-goal-service.js +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/image/load-image-media.js +2 -2
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +2 -2
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
- package/dist/src/agent/memory/dreaming/events.js +1 -1
- package/dist/src/agent/memory/dreaming/last-run.js +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
- package/dist/src/agent/memory/dreaming/preview.js +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
- package/dist/src/agent/memory/dreaming/utils.js +1 -1
- package/dist/src/agent/memory/memory-flush.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
- package/dist/src/agent/reply/post-compaction-context.js +1 -1
- package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
- package/dist/src/agent/sandbox/path-policy.js +2 -2
- package/dist/src/agent/service.js +4 -4
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/config.js +1 -1
- package/dist/src/agent/skills/hub-hash.js +2 -2
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +2 -2
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/dreaming-tool.js +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/workflow-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/agent/workflow/catalog.js +1 -1
- package/dist/src/auth/credentials.js +3 -4
- package/dist/src/auth/credentials.js.map +1 -1
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/browser/cache-dir-policy.js +1 -1
- package/dist/src/browser/cdp-local-launcher.js +2 -2
- package/dist/src/browser/providers/browser-ext-install.js +3 -3
- package/dist/src/browser/providers/cloakbrowser.js +4 -4
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.js +1 -1
- package/dist/src/channels/outbound/persist-store.js +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +1 -1
- package/dist/src/channels/pairing/pairing-store.js +2 -2
- package/dist/src/chat-commands/agent-edit.js +2 -2
- package/dist/src/chat-commands/builtins/config.js +2 -2
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/bin.d.ts +1 -0
- package/dist/src/cli/bin.js +1 -0
- package/dist/src/cli/bin.js.map +1 -1
- package/dist/src/cli/commands/config.js +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/logs.js +1 -1
- package/dist/src/cli/commands/image.js +1 -1
- package/dist/src/cli/commands/init.js +3 -3
- package/dist/src/cli/utils/init-workspace-core.js +2 -2
- package/dist/src/commands/agents.config.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +5 -5
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +2 -2
- package/dist/src/config/paths-state.js +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/workspace-path.js +1 -1
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/daemon/constants.js +1 -1
- package/dist/src/daemon/install-plan.js +2 -2
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/schtasks.js +2 -2
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/extensions/bundle-mcp.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.js +1 -1
- package/dist/src/extensions/lockfile.js +2 -2
- package/dist/src/extensions/update.js +1 -1
- package/dist/src/gateway/agents-admin.js +3 -3
- package/dist/src/gateway/file-path-classifier.js +2 -2
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/lib/extension-store.js +2 -2
- package/dist/src/gateway/hono/lib/static-ui.js +2 -2
- package/dist/src/gateway/hono/oauth-async.js +36 -3
- package/dist/src/gateway/hono/oauth-async.js.map +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +1 -1
- package/dist/src/gateway/hono/routes/host-fs.js +2 -2
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/notes.js +9 -22
- package/dist/src/gateway/hono/routes/notes.js.map +1 -1
- package/dist/src/gateway/hono/routes/shares.js +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +2 -2
- package/dist/src/gateway/lock.js +3 -3
- package/dist/src/gateway/ports.js +1 -1
- package/dist/src/gateway/service/agent-runner.js +2 -2
- package/dist/src/gateway/service/marketplace-service.js +2 -2
- package/dist/src/gateway/service.js +1 -1
- package/dist/src/gateway/workspace-fs-file-list.js +1 -1
- package/dist/src/infra/brew.js +1 -1
- package/dist/src/infra/http-proxy-env.d.ts +4 -0
- package/dist/src/infra/http-proxy-env.js +23 -0
- package/dist/src/infra/http-proxy-env.js.map +1 -0
- package/dist/src/infra/package-json.js +1 -1
- package/dist/src/infra/package-update-steps.js +1 -1
- package/dist/src/infra/path-env.js +2 -2
- package/dist/src/infra/restart.js +2 -2
- package/dist/src/infra/stable-node-path.js +1 -1
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/infra/update-global.js +1 -1
- package/dist/src/infra/update-lock.js +3 -3
- package/dist/src/infra/update-runner.js +1 -1
- package/dist/src/infra/update-startup.js +2 -2
- package/dist/src/infra/write-file-atomic.js +2 -2
- package/dist/src/media/store.js +1 -1
- package/dist/src/notes/note-attachment-sync.d.ts +2 -3
- package/dist/src/notes/note-attachment-sync.js +2 -27
- package/dist/src/notes/note-attachment-sync.js.map +1 -1
- package/dist/src/notes/note-index-meta.d.ts +6 -2
- package/dist/src/notes/note-index-meta.js +11 -16
- package/dist/src/notes/note-index-meta.js.map +1 -1
- package/dist/src/notes/note-markdown.d.ts +61 -0
- package/dist/src/notes/note-markdown.js +177 -0
- package/dist/src/notes/note-markdown.js.map +1 -0
- package/dist/src/notes/service.d.ts +9 -7
- package/dist/src/notes/service.js +163 -312
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/notes/store.js +4 -5
- package/dist/src/notes/store.js.map +1 -1
- package/dist/src/notes/types.d.ts +29 -58
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/session/init-session-turn.js +2 -2
- package/dist/src/session/resolve-session.js +3 -3
- package/dist/src/session/session-title.js +2 -2
- package/dist/src/session/store.js +2 -2
- package/dist/src/share/share-auto.js +2 -2
- package/dist/src/share/share-store.js +3 -3
- package/dist/src/share/share-thumbnail.js +2 -2
- package/dist/src/share/share-zip.js +1 -1
- package/dist/src/share/site-share-store.js +3 -3
- package/dist/src/share/site-static-serve.js +1 -1
- package/dist/src/storage/sqlite/connection.js +2 -2
- package/dist/src/storage/sqlite/memory-index-repository.js +2 -2
- package/dist/src/storage/sqlite/migrations/003_add_note_markdown_meta.sql +4 -0
- package/dist/src/storage/sqlite/migrations/discover.js +1 -1
- package/dist/src/storage/sqlite/migrations/runner.d.ts +2 -2
- package/dist/src/storage/sqlite/migrations/runner.js +5 -5
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +2 -2
- package/dist/src/storage/sqlite/notes-repository.js +18 -6
- package/dist/src/storage/sqlite/notes-repository.js.map +1 -1
- package/dist/src/storage/sqlite/schema.js +4 -4
- package/dist/src/storage/sqlite/schema.sql +5 -1
- package/dist/src/tui/clipboard-image.js +3 -3
- package/dist/src/tui/theme-manager.js +1 -1
- package/dist/src/tui/tui-keybindings-file.js +1 -1
- package/dist/src/tui/tui-scoped-models.js +2 -2
- package/dist/src/tui/tui-settings.js +1 -1
- package/dist/src/tui/tui.js +3 -3
- package/dist/src/tunnel/frpc-binary.js +3 -3
- package/dist/src/tunnel/frpc-config.js +1 -1
- package/dist/src/tunnel/frpc-extract.js +1 -1
- package/dist/src/tunnel/tunnel-state.js +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/dist/src/workflows/store/event-store.js +1 -1
- package/dist/src/workflows/store/run-store.js +1 -1
- package/package.json +3 -3
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
3
|
import { getDefaultModelSync, init_providers, resolveModel } from "../providers/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { buildNoteAttachmentRef } from "./attachment-ref.js";
|
|
5
|
+
import { parseNoteMarkdown } from "./note-markdown.js";
|
|
6
|
+
import { NotesStore } from "./store.js";
|
|
5
7
|
import { partitionAttachmentsByReference } from "./note-attachment-sync.js";
|
|
6
8
|
import { randomUUID } from "node:crypto";
|
|
7
9
|
import { complete } from "@earendil-works/pi-ai";
|
|
@@ -9,278 +11,136 @@ import { complete } from "@earendil-works/pi-ai";
|
|
|
9
11
|
init_providers();
|
|
10
12
|
init_logger();
|
|
11
13
|
const log = createLogger("NotesService");
|
|
12
|
-
|
|
14
|
+
const MAX_SNAPSHOTS_PER_NOTE = 50;
|
|
15
|
+
const SNAPSHOT_THROTTLE_MS = 6e4;
|
|
16
|
+
function inferKind(markdown, hasAttachments, attachments) {
|
|
13
17
|
if (hasAttachments && attachments?.length && attachments.every((item) => item.type === "audio")) return "voice";
|
|
14
18
|
if (hasAttachments) return "media";
|
|
15
|
-
if (!
|
|
16
|
-
const lower =
|
|
17
|
-
if (/^(todo|task|remind|buy|call|email|meet|finish|submit|send)\b/i.test(lower) || /\b(明天|今天|记得|别忘|待办|提醒)\b/.test(
|
|
18
|
-
if (/^https?:\/\//.test(
|
|
19
|
+
if (!markdown) return "thought";
|
|
20
|
+
const lower = markdown.toLowerCase();
|
|
21
|
+
if (/^(todo|task|remind|buy|call|email|meet|finish|submit|send)\b/i.test(lower) || /\b(明天|今天|记得|别忘|待办|提醒)\b/.test(markdown)) return "todo";
|
|
22
|
+
if (/^https?:\/\//.test(markdown.trim())) return "bookmark";
|
|
19
23
|
return "thought";
|
|
20
24
|
}
|
|
21
|
-
function deriveDefaultTitle(
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
return Array.from(normalizedText).slice(0, 10).join("");
|
|
25
|
-
}
|
|
26
|
-
function createBlockId() {
|
|
27
|
-
return `block_${Date.now()}_${randomUUID().slice(0, 8)}`;
|
|
28
|
-
}
|
|
29
|
-
const IMAGE_MARKDOWN = /^!\[([^\]]*)\]\(([^)]+)\)$/;
|
|
30
|
-
function noteTextToBlocks(text, noteId) {
|
|
31
|
-
if (!text?.trim()) return void 0;
|
|
32
|
-
const now = Date.now();
|
|
33
|
-
return text.split(/\n{2,}/).map((part) => {
|
|
34
|
-
const trimmed = part.trim();
|
|
35
|
-
if (noteId) {
|
|
36
|
-
const imageMatch = trimmed.match(IMAGE_MARKDOWN);
|
|
37
|
-
if (imageMatch) {
|
|
38
|
-
const attachmentId = attachmentIdFromTarget(imageMatch[2], noteId);
|
|
39
|
-
if (attachmentId) return {
|
|
40
|
-
id: createBlockId(),
|
|
41
|
-
type: "image",
|
|
42
|
-
attachmentId,
|
|
43
|
-
alt: imageMatch[1] || void 0,
|
|
44
|
-
createdAt: now,
|
|
45
|
-
updatedAt: now
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
id: createBlockId(),
|
|
51
|
-
type: "paragraph",
|
|
52
|
-
text: trimmed,
|
|
53
|
-
createdAt: now,
|
|
54
|
-
updatedAt: now
|
|
55
|
-
};
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
function blocksToPlainText(blocks, noteId) {
|
|
59
|
-
if (!blocks?.length) return void 0;
|
|
60
|
-
return blocks.map((block) => {
|
|
61
|
-
if (block.type === "divider") return "---";
|
|
62
|
-
if (block.type === "todo") return `${block.checked ? "[x]" : "[ ]"} ${block.text}`;
|
|
63
|
-
if (block.type === "image") {
|
|
64
|
-
if (noteId) return `})`;
|
|
65
|
-
return block.alt ?? "";
|
|
66
|
-
}
|
|
67
|
-
return block.text;
|
|
68
|
-
}).filter((text) => text.trim().length > 0).join("\n\n");
|
|
69
|
-
}
|
|
70
|
-
function createAiOrganizedBlocks(blocks, instruction) {
|
|
71
|
-
const now = Date.now();
|
|
72
|
-
const lines = (blocksToPlainText(blocks) || "").split(/\n+/).map((line) => line.trim()).filter(Boolean);
|
|
73
|
-
const wantsTodos = /待办|todo|task|行动|提醒/i.test(instruction);
|
|
74
|
-
const wantsSummary = /摘要|总结|summary|压缩/i.test(instruction);
|
|
75
|
-
if (wantsTodos) {
|
|
76
|
-
const candidates = lines.filter((line) => /要|需|记得|todo|task|完成|提交|联系|跟进|提醒/i.test(line));
|
|
77
|
-
return (candidates.length ? candidates : lines).slice(0, 12).map((line) => ({
|
|
78
|
-
id: createBlockId(),
|
|
79
|
-
type: "todo",
|
|
80
|
-
text: line.replace(/^[-*\d.\s\[\]x]+/i, "").trim(),
|
|
81
|
-
checked: false,
|
|
82
|
-
createdAt: now,
|
|
83
|
-
updatedAt: now
|
|
84
|
-
}));
|
|
85
|
-
}
|
|
86
|
-
if (wantsSummary) {
|
|
87
|
-
const summary = lines.join(" ").slice(0, 220);
|
|
88
|
-
return [{
|
|
89
|
-
id: createBlockId(),
|
|
90
|
-
type: "paragraph",
|
|
91
|
-
text: summary,
|
|
92
|
-
createdAt: now,
|
|
93
|
-
updatedAt: now
|
|
94
|
-
}];
|
|
95
|
-
}
|
|
96
|
-
const titleText = lines[0]?.slice(0, 40) || "整理后的笔记";
|
|
97
|
-
const bodyLines = lines.slice(1).length ? lines.slice(1) : lines;
|
|
98
|
-
return [{
|
|
99
|
-
id: createBlockId(),
|
|
100
|
-
type: "heading",
|
|
101
|
-
text: titleText,
|
|
102
|
-
level: 2,
|
|
103
|
-
createdAt: now,
|
|
104
|
-
updatedAt: now
|
|
105
|
-
}, ...bodyLines.map((line) => ({
|
|
106
|
-
id: createBlockId(),
|
|
107
|
-
type: "bulletList",
|
|
108
|
-
text: line.replace(/^[-*\d.\s]+/, "").trim(),
|
|
109
|
-
indent: 0,
|
|
110
|
-
createdAt: now,
|
|
111
|
-
updatedAt: now
|
|
112
|
-
}))];
|
|
25
|
+
function deriveDefaultTitle(markdown) {
|
|
26
|
+
const normalized = parseNoteMarkdown(markdown ?? "").plainText.trim().replace(/\s+/g, " ");
|
|
27
|
+
return normalized ? Array.from(normalized).slice(0, 10).join("") : void 0;
|
|
113
28
|
}
|
|
114
|
-
function splitMeaningfulLines(
|
|
115
|
-
return (
|
|
29
|
+
function splitMeaningfulLines(markdown) {
|
|
30
|
+
return parseNoteMarkdown(markdown ?? "").plainText.split(/\n|。|;|;/).map((line) => line.trim()).filter(Boolean);
|
|
116
31
|
}
|
|
117
|
-
function summarizeIdea(
|
|
118
|
-
|
|
119
|
-
return Array.from(joined || "这个想法").slice(0, 90).join("");
|
|
32
|
+
function summarizeIdea(markdown) {
|
|
33
|
+
return splitMeaningfulLines(markdown).slice(0, 3).join(";").slice(0, 140);
|
|
120
34
|
}
|
|
121
35
|
function inferCatalysisStage(note) {
|
|
122
|
-
const text = `${note.title ?? ""}\n${note.
|
|
123
|
-
if (
|
|
124
|
-
if (
|
|
125
|
-
if (
|
|
126
|
-
if (
|
|
127
|
-
return "
|
|
36
|
+
const text = `${note.title ?? ""}\n${note.markdown}`;
|
|
37
|
+
if (/上线|发布|launch|ship/i.test(text)) return "shipped";
|
|
38
|
+
if (/验证|访谈|用户|测试|validate/i.test(text)) return "validating";
|
|
39
|
+
if (/开发|实现|构建|build|mvp/i.test(text)) return "developing";
|
|
40
|
+
if (/想法|机会|问题|idea|seed/i.test(text)) return "seed";
|
|
41
|
+
return "incubating";
|
|
128
42
|
}
|
|
129
43
|
function buildCatalysisReport(note) {
|
|
130
|
-
const
|
|
131
|
-
const summary = summarizeIdea(note.text ?? note.title);
|
|
44
|
+
const summary = summarizeIdea(note.markdown || note.title);
|
|
132
45
|
const title = note.title?.trim() || summary.slice(0, 28) || "未命名想法";
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
46
|
+
const actions = [
|
|
47
|
+
{
|
|
48
|
+
kind: "research",
|
|
49
|
+
text: `验证「${title}」的真实需求和目标用户`
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
kind: "task",
|
|
53
|
+
text: `拆解「${title}」的最小可行下一步`
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
kind: "chat",
|
|
57
|
+
text: `继续和 AI 讨论「${title}」的方案`
|
|
58
|
+
}
|
|
59
|
+
];
|
|
136
60
|
return {
|
|
137
61
|
originalNoteId: note.id,
|
|
138
|
-
generatedAt,
|
|
62
|
+
generatedAt: Date.now(),
|
|
139
63
|
title,
|
|
140
|
-
valueHypothesis:
|
|
141
|
-
targetUsers:
|
|
64
|
+
valueHypothesis: summary || "这条笔记可能包含一个待澄清的想法。",
|
|
65
|
+
targetUsers: ["待确认的核心用户"],
|
|
142
66
|
keyQuestions: [
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
"
|
|
67
|
+
"用户为什么现在需要它?",
|
|
68
|
+
"最小可验证版本是什么?",
|
|
69
|
+
"如何判断它值得继续投入?"
|
|
146
70
|
],
|
|
147
71
|
mvpPath: [
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
],
|
|
152
|
-
risks: ["想法还停留在概念层,缺少明确使用场景。", "下一步过大时容易变成长期搁置的项目。"],
|
|
153
|
-
nextActions: [
|
|
154
|
-
{
|
|
155
|
-
kind: "chat",
|
|
156
|
-
text: "和 AI 继续深聊这个想法,收敛成问题陈述。"
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
kind: "research",
|
|
160
|
-
text: "补充 3 个相似产品、案例或用户反馈。"
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
kind: "task",
|
|
164
|
-
text: "写下今天能完成的一个最小推进动作。"
|
|
165
|
-
}
|
|
72
|
+
"定义目标用户",
|
|
73
|
+
"写出最小场景",
|
|
74
|
+
"完成一次验证"
|
|
166
75
|
],
|
|
167
|
-
|
|
76
|
+
risks: ["需求不够明确", "执行路径过大"],
|
|
77
|
+
nextActions: actions,
|
|
78
|
+
confidence: summary ? .58 : .35
|
|
168
79
|
};
|
|
169
80
|
}
|
|
170
|
-
function stripJsonCodeFence(raw) {
|
|
171
|
-
return raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
|
|
172
|
-
}
|
|
173
|
-
function extractJsonObject(raw) {
|
|
174
|
-
if (!raw.trim()) return null;
|
|
175
|
-
const text = stripJsonCodeFence(raw);
|
|
176
|
-
try {
|
|
177
|
-
const data = JSON.parse(text);
|
|
178
|
-
return data && typeof data === "object" && !Array.isArray(data) ? data : null;
|
|
179
|
-
} catch {
|
|
180
|
-
const start = text.indexOf("{");
|
|
181
|
-
const end = text.lastIndexOf("}");
|
|
182
|
-
if (start < 0 || end <= start) return null;
|
|
183
|
-
try {
|
|
184
|
-
const data = JSON.parse(text.slice(start, end + 1));
|
|
185
|
-
return data && typeof data === "object" && !Array.isArray(data) ? data : null;
|
|
186
|
-
} catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
function extractAssistantText(result) {
|
|
192
|
-
if (!Array.isArray(result.content)) return "";
|
|
193
|
-
return result.content.filter((block) => {
|
|
194
|
-
return !!block && typeof block === "object" && block.type === "text";
|
|
195
|
-
}).map((block) => block.text).join("").trim();
|
|
196
|
-
}
|
|
197
|
-
function stringArray(value, fallback, limit) {
|
|
198
|
-
if (!Array.isArray(value)) return fallback;
|
|
199
|
-
const items = value.map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean).slice(0, limit);
|
|
200
|
-
return items.length ? items : fallback;
|
|
201
|
-
}
|
|
202
|
-
function catalysisActions(value, fallback) {
|
|
203
|
-
if (!Array.isArray(value)) return fallback;
|
|
204
|
-
const allowed = new Set([
|
|
205
|
-
"task",
|
|
206
|
-
"workflow",
|
|
207
|
-
"research",
|
|
208
|
-
"share",
|
|
209
|
-
"chat"
|
|
210
|
-
]);
|
|
211
|
-
const items = [];
|
|
212
|
-
for (const item of value) {
|
|
213
|
-
if (!item || typeof item !== "object") continue;
|
|
214
|
-
const row = item;
|
|
215
|
-
const kind = typeof row.kind === "string" && allowed.has(row.kind) ? row.kind : "task";
|
|
216
|
-
const text = typeof row.text === "string" ? row.text.trim() : "";
|
|
217
|
-
if (text) items.push({
|
|
218
|
-
kind,
|
|
219
|
-
text
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
return items.length ? items.slice(0, 5) : fallback;
|
|
223
|
-
}
|
|
224
81
|
function normalizeAiCatalysisReport(note, data) {
|
|
225
82
|
const fallback = buildCatalysisReport(note);
|
|
226
|
-
const
|
|
227
|
-
const
|
|
83
|
+
const stringArray = (value, fallbackValue) => Array.isArray(value) ? value.filter((item) => typeof item === "string").slice(0, 12) : fallbackValue;
|
|
84
|
+
const actions = Array.isArray(data.nextActions) ? data.nextActions.map((item) => {
|
|
85
|
+
if (!item || typeof item !== "object") return null;
|
|
86
|
+
const row = item;
|
|
87
|
+
const kind = row.kind === "task" || row.kind === "workflow" || row.kind === "research" || row.kind === "share" || row.kind === "chat" ? row.kind : "task";
|
|
88
|
+
return typeof row.text === "string" && row.text.trim() ? {
|
|
89
|
+
kind,
|
|
90
|
+
text: row.text.trim()
|
|
91
|
+
} : null;
|
|
92
|
+
}).filter((item) => Boolean(item)).slice(0, 12) : fallback.nextActions;
|
|
228
93
|
return {
|
|
229
94
|
originalNoteId: note.id,
|
|
230
95
|
generatedAt: Date.now(),
|
|
231
|
-
title: typeof data.title === "string" && data.title.trim() ? data.title.trim()
|
|
96
|
+
title: typeof data.title === "string" && data.title.trim() ? data.title.trim() : fallback.title,
|
|
232
97
|
valueHypothesis: typeof data.valueHypothesis === "string" && data.valueHypothesis.trim() ? data.valueHypothesis.trim() : fallback.valueHypothesis,
|
|
233
|
-
targetUsers: stringArray(data.targetUsers, fallback.targetUsers
|
|
234
|
-
keyQuestions: stringArray(data.keyQuestions, fallback.keyQuestions
|
|
235
|
-
mvpPath: stringArray(data.mvpPath, fallback.mvpPath
|
|
236
|
-
risks: stringArray(data.risks, fallback.risks
|
|
237
|
-
nextActions:
|
|
238
|
-
confidence:
|
|
98
|
+
targetUsers: stringArray(data.targetUsers, fallback.targetUsers),
|
|
99
|
+
keyQuestions: stringArray(data.keyQuestions, fallback.keyQuestions),
|
|
100
|
+
mvpPath: stringArray(data.mvpPath, fallback.mvpPath),
|
|
101
|
+
risks: stringArray(data.risks, fallback.risks),
|
|
102
|
+
nextActions: actions,
|
|
103
|
+
confidence: typeof data.confidence === "number" ? Math.max(0, Math.min(1, data.confidence)) : fallback.confidence
|
|
239
104
|
};
|
|
240
105
|
}
|
|
241
106
|
function buildCatalysisPrompt(note) {
|
|
242
|
-
return
|
|
107
|
+
return `请把下面的 Markdown 笔记催化成可执行的想法报告,输出 JSON:\n{\n "title": string,\n "valueHypothesis": string,\n "targetUsers": string[],\n "keyQuestions": string[],\n "mvpPath": string[],\n "risks": string[],\n "nextActions": [{"text": string, "kind": "task"|"workflow"|"research"|"share"|"chat"}],\n "confidence": number\n}\n\n标题:${note.title?.trim() || "未命名笔记"}\n\nMarkdown:\n${note.markdown.trim() || "(无正文)"}`;
|
|
243
108
|
}
|
|
244
109
|
async function buildAiCatalysisReport(note, config) {
|
|
245
|
-
const
|
|
110
|
+
const response = await complete(resolveModel(getDefaultModelSync(config) ?? "openai/gpt-4o-mini"), { messages: [{
|
|
246
111
|
role: "user",
|
|
247
112
|
content: buildCatalysisPrompt(note),
|
|
248
113
|
timestamp: Date.now()
|
|
249
|
-
}] }, {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
114
|
+
}] }, { temperature: .2 });
|
|
115
|
+
let responseText = "";
|
|
116
|
+
if (Array.isArray(response.content)) {
|
|
117
|
+
for (const part of response.content) if (part && typeof part === "object" && part.type === "text") responseText += String(part.text || "");
|
|
118
|
+
}
|
|
119
|
+
const raw = responseText.trim().replace(/^```json\s*/i, "").replace(/```$/i, "").trim();
|
|
120
|
+
return normalizeAiCatalysisReport(note, JSON.parse(raw));
|
|
255
121
|
}
|
|
256
|
-
const SNAPSHOT_THROTTLE_MS = 6e4;
|
|
257
|
-
const MAX_SNAPSHOTS_PER_NOTE = 30;
|
|
258
122
|
var NotesService = class {
|
|
259
|
-
store;
|
|
260
123
|
lastSnapshotAt = /* @__PURE__ */ new Map();
|
|
261
|
-
constructor(store) {
|
|
124
|
+
constructor(store = new NotesStore()) {
|
|
262
125
|
this.store = store;
|
|
263
126
|
}
|
|
264
127
|
async initialize() {
|
|
265
128
|
await this.store.initialize();
|
|
266
|
-
log.debug("NotesService initialized");
|
|
267
129
|
}
|
|
268
|
-
async quickCapture(
|
|
269
|
-
const now = Date.now();
|
|
130
|
+
async quickCapture(markdown, source) {
|
|
270
131
|
const id = randomUUID();
|
|
271
|
-
const
|
|
132
|
+
const now = Date.now();
|
|
272
133
|
const note = {
|
|
273
134
|
id,
|
|
274
|
-
title: deriveDefaultTitle(
|
|
275
|
-
kind: inferKind(
|
|
135
|
+
title: deriveDefaultTitle(markdown),
|
|
136
|
+
kind: inferKind(markdown),
|
|
276
137
|
status: "inbox",
|
|
277
|
-
|
|
278
|
-
blocks,
|
|
138
|
+
markdown,
|
|
279
139
|
createdAt: now,
|
|
280
140
|
updatedAt: now,
|
|
281
141
|
capturedVia: source,
|
|
282
|
-
|
|
283
|
-
|
|
142
|
+
remoteVersion: 1,
|
|
143
|
+
localVersion: 1
|
|
284
144
|
};
|
|
285
145
|
await this.store.addNote(note);
|
|
286
146
|
log.debug({
|
|
@@ -290,24 +150,25 @@ var NotesService = class {
|
|
|
290
150
|
return note;
|
|
291
151
|
}
|
|
292
152
|
async createNote(params) {
|
|
293
|
-
const now = Date.now();
|
|
294
153
|
const id = randomUUID();
|
|
295
|
-
const
|
|
296
|
-
const
|
|
154
|
+
const now = Date.now();
|
|
155
|
+
const markdown = params.markdown ?? "";
|
|
297
156
|
const note = {
|
|
298
157
|
id,
|
|
299
|
-
title: params.title,
|
|
300
|
-
kind: params.kind
|
|
158
|
+
title: params.title ?? deriveDefaultTitle(markdown),
|
|
159
|
+
kind: params.kind ?? inferKind(markdown),
|
|
301
160
|
status: "inbox",
|
|
302
|
-
|
|
303
|
-
|
|
161
|
+
markdown,
|
|
162
|
+
attachments: [],
|
|
304
163
|
createdAt: now,
|
|
305
164
|
updatedAt: now,
|
|
306
165
|
capturedVia: params.capturedVia,
|
|
307
166
|
tags: params.tags,
|
|
308
167
|
pinned: params.pinned,
|
|
309
|
-
|
|
310
|
-
|
|
168
|
+
groupId: params.groupId,
|
|
169
|
+
taskMeta: params.taskMeta,
|
|
170
|
+
remoteVersion: 1,
|
|
171
|
+
localVersion: 1
|
|
311
172
|
};
|
|
312
173
|
await this.store.addNote(note);
|
|
313
174
|
log.debug({
|
|
@@ -322,25 +183,16 @@ var NotesService = class {
|
|
|
322
183
|
async updateNote(id, patch, trigger = "edit") {
|
|
323
184
|
const existing = await this.store.getNote(id);
|
|
324
185
|
if (!existing) return null;
|
|
325
|
-
const contentTouched = patch.
|
|
186
|
+
const contentTouched = patch.markdown !== void 0 || patch.title !== void 0;
|
|
326
187
|
if (contentTouched) await this.maybeSaveSnapshot(existing, trigger);
|
|
327
188
|
const normalizedPatch = { ...patch };
|
|
328
|
-
if (patch.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const merged = {
|
|
333
|
-
...existing,
|
|
334
|
-
...normalizedPatch,
|
|
335
|
-
id: existing.id,
|
|
336
|
-
createdAt: existing.createdAt,
|
|
337
|
-
updatedAt: Date.now()
|
|
338
|
-
};
|
|
339
|
-
const reconciled = await this.reconcileAttachments(merged);
|
|
340
|
-
normalizedPatch.attachments = reconciled.attachments;
|
|
341
|
-
normalizedPatch.kind = reconciled.kind;
|
|
189
|
+
if (patch.markdown !== void 0) normalizedPatch.kind = patch.kind ?? inferKind(patch.markdown, Boolean(existing.attachments?.length), existing.attachments);
|
|
190
|
+
if (contentTouched || patch.attachments) {
|
|
191
|
+
normalizedPatch.remoteVersion = (existing.remoteVersion ?? 1) + 1;
|
|
192
|
+
normalizedPatch.localVersion = patch.localVersion ?? (existing.localVersion ?? 1) + 1;
|
|
342
193
|
}
|
|
343
|
-
|
|
194
|
+
const updatedRaw = await this.store.updateNote(id, normalizedPatch);
|
|
195
|
+
return updatedRaw ? this.reconcileAttachments(updatedRaw) : null;
|
|
344
196
|
}
|
|
345
197
|
async reconcileAttachments(note) {
|
|
346
198
|
const { kept, removed } = partitionAttachmentsByReference(note);
|
|
@@ -351,10 +203,12 @@ var NotesService = class {
|
|
|
351
203
|
removedIds: removed.map((attachment) => attachment.id)
|
|
352
204
|
}, "Pruned orphan note attachments");
|
|
353
205
|
const hasAttachments = kept.length > 0;
|
|
354
|
-
return {
|
|
206
|
+
return await this.store.updateNote(note.id, {
|
|
207
|
+
attachments: kept,
|
|
208
|
+
kind: inferKind(note.markdown, hasAttachments, kept)
|
|
209
|
+
}) ?? {
|
|
355
210
|
...note,
|
|
356
|
-
attachments:
|
|
357
|
-
kind: inferKind(note.text, hasAttachments, kept)
|
|
211
|
+
attachments: kept
|
|
358
212
|
};
|
|
359
213
|
}
|
|
360
214
|
async syncNote(id, patch, baseRemoteVersion) {
|
|
@@ -363,8 +217,7 @@ var NotesService = class {
|
|
|
363
217
|
note: null,
|
|
364
218
|
conflict: false
|
|
365
219
|
};
|
|
366
|
-
|
|
367
|
-
if (baseRemoteVersion !== void 0 && baseRemoteVersion < currentRemoteVersion) return {
|
|
220
|
+
if (baseRemoteVersion !== void 0 && existing.remoteVersion !== void 0 && baseRemoteVersion < existing.remoteVersion) return {
|
|
368
221
|
note: existing,
|
|
369
222
|
conflict: true
|
|
370
223
|
};
|
|
@@ -373,18 +226,29 @@ var NotesService = class {
|
|
|
373
226
|
conflict: false
|
|
374
227
|
};
|
|
375
228
|
}
|
|
376
|
-
async createAiEditPatch(id, instruction,
|
|
229
|
+
async createAiEditPatch(id, instruction, markdownOverride) {
|
|
377
230
|
const note = await this.store.getNote(id);
|
|
378
231
|
if (!note) return null;
|
|
379
|
-
const
|
|
232
|
+
const source = markdownOverride ?? note.markdown;
|
|
233
|
+
const parsed = parseNoteMarkdown(source, id);
|
|
234
|
+
const wantsTodos = /待办|todo|task|行动|提醒/i.test(instruction);
|
|
235
|
+
const wantsSummary = /摘要|总结|summary|压缩/i.test(instruction);
|
|
236
|
+
let generated = "";
|
|
237
|
+
if (wantsTodos) {
|
|
238
|
+
const lines = source.split(/\n|。|;|;/).map((line) => line.trim()).filter(Boolean);
|
|
239
|
+
generated = (lines.length ? lines : [parsed.plainText]).slice(0, 12).map((line) => `- [ ] ${line.replace(/^[-*\d.\s\[\]x]+/i, "").trim()}`).join("\n");
|
|
240
|
+
} else if (wantsSummary) generated = `> [!SUMMARY]\n> ${parsed.plainText.slice(0, 220)}`;
|
|
241
|
+
else generated = `${source.trimEnd()}\n\n## AI 整理\n\n${instruction.trim()}`;
|
|
380
242
|
return {
|
|
381
243
|
message: "AI edit patch generated",
|
|
382
244
|
patch: {
|
|
383
245
|
id: randomUUID(),
|
|
384
|
-
summary:
|
|
246
|
+
summary: instruction,
|
|
385
247
|
operations: [{
|
|
386
|
-
type: "
|
|
387
|
-
|
|
248
|
+
type: "replaceRange",
|
|
249
|
+
from: 0,
|
|
250
|
+
to: source.length,
|
|
251
|
+
markdown: generated || source
|
|
388
252
|
}]
|
|
389
253
|
}
|
|
390
254
|
};
|
|
@@ -402,27 +266,24 @@ var NotesService = class {
|
|
|
402
266
|
}, "AI note catalysis failed; using local fallback report");
|
|
403
267
|
report = buildCatalysisReport(note);
|
|
404
268
|
}
|
|
405
|
-
const existingDeep = note.aiDeep;
|
|
406
|
-
const catalysis = {
|
|
407
|
-
...existingDeep?.catalysis,
|
|
408
|
-
status: "catalyzed",
|
|
409
|
-
stage: inferCatalysisStage(note),
|
|
410
|
-
lastCatalyzedAt: report.generatedAt,
|
|
411
|
-
confidence: report.confidence,
|
|
412
|
-
report
|
|
413
|
-
};
|
|
414
269
|
const updated = await this.updateNote(id, {
|
|
270
|
+
aiDeep: {
|
|
271
|
+
...note.aiDeep,
|
|
272
|
+
processedAt: Date.now(),
|
|
273
|
+
catalysis: {
|
|
274
|
+
status: "catalyzed",
|
|
275
|
+
stage: inferCatalysisStage(note),
|
|
276
|
+
lastCatalyzedAt: Date.now(),
|
|
277
|
+
confidence: report.confidence,
|
|
278
|
+
report,
|
|
279
|
+
...note.aiDeep?.catalysis
|
|
280
|
+
}
|
|
281
|
+
},
|
|
415
282
|
ai: {
|
|
416
283
|
...note.ai,
|
|
417
284
|
intent: note.ai?.intent ?? "idea",
|
|
418
285
|
summary: note.ai?.summary ?? report.valueHypothesis
|
|
419
286
|
},
|
|
420
|
-
aiDeep: {
|
|
421
|
-
...existingDeep,
|
|
422
|
-
processedAt: report.generatedAt,
|
|
423
|
-
insights: report.valueHypothesis,
|
|
424
|
-
catalysis
|
|
425
|
-
},
|
|
426
287
|
status: note.status === "inbox" ? "processed" : note.status
|
|
427
288
|
}, "ai_edit");
|
|
428
289
|
return updated ? {
|
|
@@ -433,22 +294,20 @@ var NotesService = class {
|
|
|
433
294
|
async recordCatalysisFeedback(id, feedback) {
|
|
434
295
|
const note = await this.store.getNote(id);
|
|
435
296
|
if (!note) return null;
|
|
436
|
-
const now = Date.now();
|
|
437
297
|
return this.updateNote(id, { aiDeep: {
|
|
438
298
|
...note.aiDeep,
|
|
439
|
-
processedAt: now,
|
|
299
|
+
processedAt: Date.now(),
|
|
440
300
|
catalysis: {
|
|
441
301
|
status: note.aiDeep?.catalysis?.status ?? "catalyzed",
|
|
442
302
|
...note.aiDeep?.catalysis,
|
|
443
303
|
feedback
|
|
444
304
|
}
|
|
445
|
-
} }
|
|
305
|
+
} });
|
|
446
306
|
}
|
|
447
307
|
async linkNoteThread(id, sessionKey) {
|
|
448
308
|
const note = await this.store.getNote(id);
|
|
449
309
|
if (!note) return null;
|
|
450
310
|
const existingKeys = note.aiDeep?.catalysis?.linkedSessionKeys ?? [];
|
|
451
|
-
const linkedSessionKeys = Array.from(new Set([sessionKey, ...existingKeys]));
|
|
452
311
|
return this.updateNote(id, { aiDeep: {
|
|
453
312
|
...note.aiDeep,
|
|
454
313
|
processedAt: Date.now(),
|
|
@@ -456,32 +315,28 @@ var NotesService = class {
|
|
|
456
315
|
status: note.aiDeep?.catalysis?.status ?? "none",
|
|
457
316
|
...note.aiDeep?.catalysis,
|
|
458
317
|
sourceSessionKey: note.aiDeep?.catalysis?.sourceSessionKey ?? sessionKey,
|
|
459
|
-
linkedSessionKeys
|
|
318
|
+
linkedSessionKeys: Array.from(new Set([...existingKeys, sessionKey]))
|
|
460
319
|
}
|
|
461
320
|
} }, "sync");
|
|
462
321
|
}
|
|
463
322
|
async listNoteThreads(id) {
|
|
464
323
|
const note = await this.store.getNote(id);
|
|
465
324
|
if (!note) return null;
|
|
466
|
-
|
|
467
|
-
return Array.from(new Set(keys));
|
|
325
|
+
return Array.from(new Set([note.aiDeep?.catalysis?.sourceSessionKey, ...note.aiDeep?.catalysis?.linkedSessionKeys ?? []].filter((key) => Boolean(key))));
|
|
468
326
|
}
|
|
469
327
|
async appendTextToNote(id, content, heading = "AI 讨论沉淀") {
|
|
470
328
|
const note = await this.store.getNote(id);
|
|
471
329
|
if (!note) return null;
|
|
472
330
|
const trimmed = content.trim();
|
|
473
331
|
if (!trimmed) return note;
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
return this.updateNote(id, {
|
|
332
|
+
const current = note.markdown.trimEnd();
|
|
333
|
+
const nextMarkdown = `${current}${current ? "\n\n" : ""}## ${heading}\n\n${trimmed}\n`;
|
|
334
|
+
return this.updateNote(id, { markdown: nextMarkdown }, "ai_edit");
|
|
477
335
|
}
|
|
478
336
|
async deleteNote(id) {
|
|
479
|
-
const
|
|
480
|
-
if (
|
|
481
|
-
|
|
482
|
-
this.lastSnapshotAt.delete(id);
|
|
483
|
-
}
|
|
484
|
-
return deleted;
|
|
337
|
+
const removed = await this.store.deleteNote(id);
|
|
338
|
+
if (removed) await this.store.deleteAllSnapshots(id);
|
|
339
|
+
return removed;
|
|
485
340
|
}
|
|
486
341
|
async listNotes(query = {}) {
|
|
487
342
|
return this.store.listNotes(query);
|
|
@@ -492,7 +347,7 @@ var NotesService = class {
|
|
|
492
347
|
const { relativePath, size } = await this.store.saveAttachment(noteId, file.name, file.buffer);
|
|
493
348
|
const attachment = {
|
|
494
349
|
id: randomUUID(),
|
|
495
|
-
type:
|
|
350
|
+
type: file.mimeType.startsWith("image/") ? "image" : file.mimeType.startsWith("audio/") ? "audio" : file.mimeType.startsWith("video/") ? "video" : "file",
|
|
496
351
|
mimeType: file.mimeType,
|
|
497
352
|
fileName: file.name,
|
|
498
353
|
size,
|
|
@@ -526,17 +381,17 @@ var NotesService = class {
|
|
|
526
381
|
}
|
|
527
382
|
async restoreNoteSnapshot(noteId, timestamp) {
|
|
528
383
|
const snapshot = await this.store.getSnapshot(noteId, timestamp);
|
|
529
|
-
if (!snapshot) return null;
|
|
530
384
|
const existing = await this.store.getNote(noteId);
|
|
531
|
-
if (!existing) return null;
|
|
532
|
-
await this.
|
|
385
|
+
if (!snapshot || !existing) return null;
|
|
386
|
+
await this.maybeSaveSnapshot(existing, "restore");
|
|
533
387
|
this.lastSnapshotAt.set(noteId, Date.now());
|
|
534
388
|
await this.store.pruneSnapshots(noteId, MAX_SNAPSHOTS_PER_NOTE);
|
|
535
389
|
return this.store.updateNote(noteId, {
|
|
536
390
|
title: snapshot.title,
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
391
|
+
markdown: snapshot.markdown,
|
|
392
|
+
tags: snapshot.tags,
|
|
393
|
+
kind: snapshot.kind,
|
|
394
|
+
status: snapshot.status
|
|
540
395
|
});
|
|
541
396
|
}
|
|
542
397
|
async flush() {
|
|
@@ -545,18 +400,19 @@ var NotesService = class {
|
|
|
545
400
|
async moveToGroup(noteId, groupId) {
|
|
546
401
|
return this.updateNote(noteId, { groupId: groupId ?? void 0 });
|
|
547
402
|
}
|
|
548
|
-
async createTask(title, source,
|
|
403
|
+
async createTask(title, source, opts = {}) {
|
|
549
404
|
return this.createNote({
|
|
550
405
|
title,
|
|
406
|
+
markdown: `- [ ] ${title}`,
|
|
551
407
|
kind: "task",
|
|
552
408
|
capturedVia: source,
|
|
553
|
-
groupId:
|
|
409
|
+
groupId: opts.groupId,
|
|
554
410
|
taskMeta: {
|
|
555
411
|
done: false,
|
|
556
|
-
dueAt:
|
|
557
|
-
priority:
|
|
558
|
-
sourceSessionKey:
|
|
559
|
-
sourceNoteId:
|
|
412
|
+
dueAt: opts.dueAt,
|
|
413
|
+
priority: opts.priority,
|
|
414
|
+
sourceSessionKey: opts.sourceSessionKey,
|
|
415
|
+
sourceNoteId: opts.sourceNoteId
|
|
560
416
|
}
|
|
561
417
|
});
|
|
562
418
|
}
|
|
@@ -564,12 +420,13 @@ var NotesService = class {
|
|
|
564
420
|
const note = await this.store.getNote(noteId);
|
|
565
421
|
if (!note || note.kind !== "task") return null;
|
|
566
422
|
const done = !note.taskMeta?.done;
|
|
423
|
+
const markdown = note.markdown.replace(/^- \[[ xX]\]/m, `- [${done ? "x" : " "}]`);
|
|
567
424
|
return this.updateNote(noteId, {
|
|
425
|
+
markdown,
|
|
568
426
|
taskMeta: {
|
|
569
427
|
...note.taskMeta,
|
|
570
428
|
done
|
|
571
|
-
}
|
|
572
|
-
status: done ? "archived" : "processed"
|
|
429
|
+
}
|
|
573
430
|
});
|
|
574
431
|
}
|
|
575
432
|
async updateTaskMeta(noteId, patch) {
|
|
@@ -598,13 +455,7 @@ var NotesService = class {
|
|
|
598
455
|
await this.store.pruneSnapshots(note.id, MAX_SNAPSHOTS_PER_NOTE);
|
|
599
456
|
}
|
|
600
457
|
};
|
|
601
|
-
function inferAttachmentType(mimeType) {
|
|
602
|
-
if (mimeType.startsWith("image/")) return "image";
|
|
603
|
-
if (mimeType.startsWith("video/")) return "video";
|
|
604
|
-
if (mimeType.startsWith("audio/")) return "audio";
|
|
605
|
-
return "file";
|
|
606
|
-
}
|
|
607
458
|
//#endregion
|
|
608
|
-
export { NotesService };
|
|
459
|
+
export { NotesService, buildNoteAttachmentRef };
|
|
609
460
|
|
|
610
461
|
//# sourceMappingURL=service.js.map
|