dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Give a DSH agent Markji (maimemo) cards: local SQLite cache, plain-text search, queued edits, archive, background sync with crash-resume.
中文 | English
给 DeepSeek Harness(DSH)的 agent 用的墨墨记忆卡(Markji)插件。它在本地维护一份卡组 / 章节 / 卡片缓存,agent 可以搜索、阅读、新建、修改、归档卡片;所有修改先写入本地队列,再按「先拉取、三方合并、后推送」的顺序同步到云端,冲突单独报告,不影响其他卡片。
只注册 4 个按 action 组织的 markji_* 工具,只用 Node 内置模块实现,无第三方依赖:markji_decks(卡组 / 章节,含章节详情、新建、重命名、排序)、markji_cards(列表过滤、读写、语法、归档、移动)、markji_sync(同步、进度、状态、失败与冲突)、markji_file(文件)。写卡前用 markji_cards({ action: "syntax_help" }) 查语法,再用 action=validate 校验;批量读写用同一工具的 new_cards、updates、contents / cards。推送时还会检查正文里的图片 / 音频文件是否可用。
cache.db,索引、卡片正文、待推送队列、冲突和同步任务都在一个库里,日常搜索和阅读不请求 API。markji_cards 的 list、markji_decks 的 list / chapter_list 使用同一组过滤参数(字段集各自独立),不再有单独的 search action:query + query_mode:substring(默认,子串)、fuzzy(近似匹配,容忍少量错字并按相似度排序)、regex(JavaScript 正则,regex_flags 可设 imsu,默认 i)或 predicate(结构化条件树)。scope:卡片支持 all / deck / chapter,章节支持 all / deck,卡组支持 all / folder;不传时由 deck / chapter / folder_id 推断,显式冲突会报错。fields:指定 query 匹配哪些字段;卡片默认 text / id / deck_name / chapter_name,卡组默认 name / description / id,章节默认 name / id。name / description / id、更新时间、计数等),章节只认章节字段(没有 description,也不支持 deck_name,按父卡组筛选用 scope=deck),卡片保留更宽的条件(text / content / cloud / status 等)。不属于该 list 的字段或操作会报 invalid_argument,不会跨资源匹配。predicate:JSON 条件树,不是 JS。条件为 { field, op, value, case_sensitive?, flags? },组合为 { all: [...] } / { any: [...] } / { not: {...} };op 支持 eq、ne、contains、starts_with、ends_with、regex、in、not_in、gt、gte、lt、lte、exists。predicate 可与 query 同时使用(AND);query_mode=predicate 时必须提供 predicate,且不能同时给 query。条件只读自有属性,最大嵌套 8 层、最多 64 个节点。text 字段只含纯文本,结果干净可读。fuzzy 可用 min_score 调整阈值;query 只放一个短短语(通常 1-3 个词):fuzzy 空格分词时要求各项都近似命中;若 3 个以上词没有任何卡片全命中,会自动取查询里更稀有的词作为锚点,退化为部分匹配:返回 partial: true、fallback_terms 和每张卡的 matched_terms,优先展示命中罕用词的卡片,并在 hint 中说明。不要把多个概念硬拼成一个 query。markji_cards 的 action=syntax_help 可按格式或全部查询原始 Markji 语法;action=validate 支持单条 content、多条 contents 或按 cards 批量校验,只检查正文,不生成或改写内容。action=get 一次读取多张卡,action=create 的 new_cards 可在一次调用中创建跨章节 / 卡组的卡片,action=update 的 updates 一次排队多张卡的写入;markji_sync 推送前再次校验正文,并批量查询 Pic / Audio 引用的文件 id。语法错误、文件缺失或查询失败都会记录到该卡并保留队列,不会只写一半。dsh-tool-jobs 时,markji_sync({ action: "start" }) 还会把任务登记为当前会话拥有的 DSH 后台任务,返回 job_id;任务出现在「正在运行的后台任务」中,可用 job_output 查看,结束后由 DSH 发送完成通知。终止后台任务会在下一个检查点取消同步,任务变为 interrupted,仍可 resume。markji_cards({ action: "archive" }) 把卡片移入同卡组「归档」章节并在正文插入归档说明,重复调用幂等;markji_decks 支持章节详情、新建、重命名和排序(provider 不提供章节删除接口)。node:sqlite,无需启动参数)从 GitHub 获取源码后,把目录安装为 DSH bundle:
git clone https://github.com/Oxmyxo/dsh-markji-bridge.git dsh-markji-bridgeplugin_manager install_bundle target=<仓库目录的绝对路径>升级:
git pullplugin_manager remove_bundle target=dsh-markji-bridge
plugin_manager install_bundle target=<仓库目录的绝对路径>只改了 lib/ 里的代码时,停用再启用插件即可加载新代码:
plugin_manager set_plugin target=include:dsh-markji-bridge enabled=false
plugin_manager set_plugin target=include:dsh-markji-bridge enabled=true在 DSH 主机进程的环境变量里设置 Markji OpenAPI token(在墨墨开放平台获取):
# Windows:设置用户环境变量后重启 DSH
setx MAIMEMO_MARKJI_OPENAPI_TOKEN "<你的 token>"# macOS / Linux:写入 shell 配置后重开终端
export MAIMEMO_MARKJI_OPENAPI_TOKEN="<你的 token>"token 不要提交到仓库或写进配置文件。插件配置里的 apiKey 可以覆盖 token,但会以明文保存在 profile 配置中,一般不需要。
markji_decks({ action: "list", refresh: true })
markji_decks({ action: "get", deck: "<deck_id>" })
markji_decks({ action: "chapter_get", chapter: "<chapter_id>", with_cards: true })
markji_cards({ action: "list", query: "排序", deck_name: "算法" })
markji_cards({ action: "list", query: "变量初始话", query_mode: "fuzzy" })
markji_cards({ action: "list", query: "^SPI\s+时钟", query_mode: "regex", fields: ["text"] })
markji_cards({ action: "list", scope: "deck", deck: "<deck_id>", predicate: { all: [{ field: "status", op: "eq", value: "dirty" }, { field: "updated_time", op: "gte", value: "2026-10-01" }] } })
markji_cards({ action: "get", card: "<card_id>" })
markji_cards({ action: "get", cards: ["<card_id_1>", "<card_id_2>"] })
markji_cards({ action: "syntax_help" })
markji_cards({ action: "syntax_help", format: "picture" })
markji_cards({ action: "validate", content: "<Markji 富文本>" })
markji_cards({ action: "validate", cards: ["<card_id_1>", "<card_id_2>"] })
markji_cards({ action: "validate", contents: ["<Markji 富文本 1>", "<Markji 富文本 2>"] })
markji_sync({ action: "start", mode: "auto" })首次调用会拉取云端数据(卡片多时需要一点时间),之后读操作走本地缓存。状态和进度用 markji_sync({ action: "state" }) / markji_sync({ action: "progress" }) 查看;传 task_id 时只返回该任务的 { task },不传时返回 { active, tasks }。若 DSH 加载了 dsh-tool-jobs,start 还会返回 job_id:在 DSH 的后台任务列表里能直接看到进度,任务结束后会自动收到完成通知,也可以用 job_output 读取结果。
需要一次处理多张卡时用 markji_cards 的这些 action:
markji_cards({ action: "get", cards: ["<id1>", "<id2>"] })
markji_cards({ action: "validate", cards: ["<id1>", "<id2>"] }) // 校验已有卡片
markji_cards({ action: "validate", contents: ["<正文1>", "<正文2>"] }) // 校验未写入的正文
markji_cards({ action: "create", deck: "<deck>", chapter: "<chapter>", new_cards: [
{ content: "<正文1>" },
{ content: "<正文2>", deck: "<deck2>", chapter: "<chapter2>" },
] })
markji_cards({ action: "update", updates: [
{ card: "<id1>", content: "<新正文1>" },
{ card: "<id2>", content: "<新正文2>" },
] })
markji_cards({ action: "archive", cards: ["<id1>", "<id2>"] })
markji_cards({ action: "move", cards: ["<id1>", "<id2>"], to_chapter: "<chapter>" })contents / cards、new_cards、updates);归档和移动使用非空数组。action=get 会去重并保持请求顺序,未知 id 放入 missing,不会中断整批。action=create 的顶层 deck / chapter 是默认值,可以省略;每个 new_cards 项可写自己的 deck / chapter,所以一次调用能跨章节 / 卡组。未提供目标的项会逐条报错;每项的 order 相对于该项最终的目标章节。ok 和错误;写操作不是事务,坏项会被记录,其他项仍会排队。成功项仍需 markji_sync({ action: "start" }) 推送。action=get 默认不返回冗余的 text,需要时传 include_text: true。markji_sync 推送前仍会做媒体预检。maxOutputChars,内容多时请拆小批次。插件只注册 4 个工具,具体操作由 action 选择;省略时用各工具的默认 action。
工具 · 默认 action · 作用 markji_decks · list · 卡组列表 / 详情,章节列表 / 详情 / 新建 / 重命名 / 排序 markji_cards · list · 卡片列表过滤、读取、新建、修改、移动、归档、语法帮助与校验 markji_sync · start · 启动同步、查看进度 / 插件状态 / 失败、解决冲突 markji_file · 无(必填) · 上传文件到 Markji 或查询文件信息
工具 · action · 作用 markji_decks · list · 列出文件夹与卡组 markji_decks · get · 读一个卡组及其章节 markji_decks · chapter_list · 列出一个或全部卡组的章节 markji_decks · chapter_get · 读一个章节,可带卡片摘要 markji_decks · chapter_create / chapter_rename / chapter_sort · 新建 / 重命名 / 排序章节 markji_cards · list · 分页、按范围(全库 / 卡组 / 章节)过滤,支持子串、模糊、正则和 predicate 条件树;可同时按卡组名 / 章节名 / 更新时间过滤 markji_cards · get · 按 id 读一张或多张卡;未知 id 列入 missing markji_cards · create · 用 new_cards 批量新建,支持跨章节 / 卡组 markji_cards · update · 改一张(card + content)或多张(updates) markji_cards · move / archive · 移动到其他章节 / 归档到「归档」章节 markji_cards · validate / syntax_help · 校验正文或已有卡片 / 查原始 Markji 语法 markji_sync · start · 启动后台同步(pull / push / auto,可 resume) markji_sync · progress · 查看同步任务进度与历史 markji_sync · state · 插件与缓存总览 markji_sync · failures · 查看失败卡片、冲突与恢复建议 markji_sync · resolve · 解决冲突(keep_local / keep_remote / use_content) markji_file · upload / query · 上传文件 / 查询文件信息
search action / markji_search:所有搜索都通过 markji_cards 的 list、markji_decks 的 list / chapter_list 完成。query_mode=predicate 与 predicate 条件树,支持 all / any / not、13 种 op,以及点号路径(如 cloud.revision);条件只能读自有属性,有深度和节点数上限。scope 与 fields:可限定全库 / 卡组 / 章节范围以及 query 匹配的字段;不传 scope 时由 deck / chapter / folder_id 推断,显式冲突会报错。markji_sync({ action: "progress", task_id }) 只返回 { task };不传 task_id 时仍返回 { active, tasks }。total:指定 deck 返回 { deck_id, deck_name, chapters, total },不指定返回 { decks, total };过滤生效时省略空卡组分组。get 的 cards、create 的 new_cards、update 的 updates、validate 的 contents / cards、move / archive 的 cards 仍可一次处理多张卡。列表和同步类工具不再返回调用方通常用不到的环境信息与空字段,减少上下文占用:
list 只返回 { id, deck_name, chapter_name, snippet },仅当状态不是 synced 时才带 status;完整正文和时间戳用 action=get 读取。返回不再带 cache 和 filters。cache;卡组详情才带 description / updated_time,章节详情才带 revision / order / updated_time,默认值字段省略。folders 只保留 id / name / deck_count / 可选 parent_id,卡组通过 folder_id 关联。action=get 的批量结果改为 { cards, missing? },不再有 ok / requested / found 和逐项包装。state 去掉环境路径与定时器细节,failures 去掉重复的全局恢复说明(每项仍有自己的 recovery),任务历史最多返回 3 条。markji_cards 的列表查询(2.2.0 时由 search 提供,2.4.0 起合并为 list)新增 query_mode:
substring(默认):大小写不敏感的子串,保持 2.1.0 行为。fuzzy:近似匹配,容忍少量错字、插入或删除;结果带 score(0-1)并按分数从高到低排序,min_score 可调阈值(默认 0.6)。它比较的是整条 query;空格分词时各项都要近似命中,所以应一次搜一个短短语。若 3 个以上词没有全命中,则取最稀有的词作为锚点退回部分匹配(partial: true、fallback_terms),用 matched_terms 标明每张卡命中了哪些词。regex:把 query 当作 JavaScript 正则,regex_flags 支持 i / m / s / u(默认 i)。matched_by 会标明命中卡片正文还是 id。所有列表 action 共享同一套查询参数;没有结果时会额外返回 hint。模糊查询退化到部分匹配时还会返回 partial: true、fallback_terms,每张卡带 matched_terms。
markji_sync({ action: "start" }) 的返回值新增可选字段 job_id。当 DSH 加载了 dsh-tool-jobs 且调用来自一个活动会话时,插件会把同步任务登记为该会话拥有的 DSH 后台任务:任务出现在 DSH 的「正在运行的后台任务」中,进度随同步阶段更新,job_output 可读取任务结果,任务结束(完成 / 失败 / 被终止)时由 DSH 发送标准完成通知。
未加载 dsh-tool-jobs 或调用不是来自会话时,没有 job_id,行为与 2.0.0 相同。终止该 DSH 后台任务会在下一个检查点取消同步:已写入的队列不丢,任务状态变为 interrupted,之后可用 markji_sync({ action: "start", resume: "" }) 续跑。markji_sync 的 progress / state / failures / resolve 仍然可用。
2.0.0 不再注册 1.x 的细粒度工具;旧调用改成一个工具加 action。字段基本不变,只有新建批量字段改为 new_cards:
1.x 工具 · 2.0.0 调用 markji_chapters / markji_chapters_edit · markji_decks 的 chapter_list / chapter_get / chapter_create / chapter_rename / chapter_sort markji_search · markji_cards 的 list(用 query / query_mode / predicate / fields / scope 过滤) markji_card_get / markji_cards_get · markji_cards 的 get markji_card_syntax · markji_cards 的 validate 或 syntax_help markji_card_create / markji_cards_create · markji_cards 的 create(批量字段为 new_cards) markji_card_update · markji_cards 的 update markji_card_archive / markji_cards_move · markji_cards 的 archive / move markji_sync_status · markji_sync 的 progress markji_status · markji_sync 的 state markji_failures · markji_sync 的 failures markji_conflict_resolve · markji_sync 的 resolve markji_file · 不变(action 仍必填)
? 表示可选参数。id 都是 Markji OpenAPI id;尚未推送的新对象使用 local_* id。工具结果统一为 JSON,超过 maxOutputChars 会截断。下表的“参数”列只列该 action 用到的字段;同一工具的其他字段在别的 action 生效。
工具 / action · 参数 · 返回(主要字段) markji_decks / list · refresh? folder_id? scope? query? query_mode? predicate? fields? regex_flags? min_score? · {folders, decks, total};folders 为 {id, name, deck_count, parent_id?} markji_decks / get · deck refresh? · {deck, chapters};详情含 description / updated_time markji_decks / chapter_list · deck? scope? refresh? query? query_mode? predicate? fields? regex_flags? min_score? · 指定 deck:{deck_id, deck_name, chapters, total};不指定:{decks, total} markji_decks / chapter_get · chapter deck? with_cards? refresh? · {deck_id, deck_name, chapter, cards?, total?} markji_decks / chapter_create · deck name order? · {ok, action, chapter_id, deck_id, status, pending, message} markji_decks / chapter_rename · chapter name deck? · {ok, action, chapter_id, status, pending, message} markji_decks / chapter_sort · deck chapter_ids · {ok, action, deck_id, chapters, status, pending, message} markji_cards / list · scope? deck? chapter? query? query_mode? predicate? fields? regex_flags? min_score? deck_name? chapter_name? updated_after? updated_before? limit? offset? refresh? · {total, offset, limit, has_more, cards};部分匹配再带 partial / fallback_terms / hint markji_cards / get · card? cards?(1-50,去重)include_text? · {cards, missing?};missing 仅在存在未知 id 时出现 markji_cards / create · new_cards(1-50;每项 {content, deck?, chapter?, order?, grammar_version?});顶层 deck? chapter? 是默认值 · {ok, action, mode, deck_id, chapter_id, total, created, failed, partial, results};顶层默认值未提供时为 null markji_cards / update · card content grammar_version?,或 updates(1-50,每项 {card, content, grammar_version?}) · 单条:{ok, card_id, status, changed, pending, message};批量:{ok, action, mode, total, updated, unchanged, failed, partial, results} markji_cards / move · cards to_chapter deck? order? · {ok, moved, to_chapter_id, status, pending, message} markji_cards / archive · cards(非空数组) · {ok, archived, content_updated, already_archived, moved, chapters, status, pending, message} markji_cards / validate · content 或 contents 或 cards(只能一个) · 单条:{ok, errors, warnings, stats, media};批量:{ok, action, mode, source, total, valid, invalid, missing?, syntax_invalid?, results} markji_cards / syntax_help · format? · {ok, action, format, rules, nesting, formats} markji_sync / start · mode?: auto/pull/push full? resume? · {task, job_id?, already_running?};job_id 需要 DSH 后台任务支持 markji_sync / progress · task_id? · 有 task_id:{task};无:{active, tasks} markji_sync / state · 无 · {version, configured, cache_file, cache, pending, conflicts, last_sync, last_error, active_task, tasks, auto_sync} markji_sync / failures · card? deck? limit? · {ok, summary, conflicts, failed_cards, last_task_errors, truncated?};每项自带 recovery markji_sync / resolve · id strategy: keep_local/keep_remote/use_content content? · {ok, id, entity, strategy, state, changed} markji_file / upload · path deck? · {file} markji_file / query · ids expires? · {files}
predicate 的 field 可用点号读取嵌套自有属性;不同列表允许的根字段:
id、deck_id、deck_name、chapter_id、chapter_name、text、content、snippet、status、grammar_version、revision、updated_time、created_time、local、pending、conflict、last_error、cloud(可继续读 cloud.revision 等)。id、name、description、folder_id、card_count、chapter_count、is_private、source、status、missing、created_time、updated_time。id、deck_id、name、card_count、order、revision、local、pending、missing、updated_time(没有 description 和 deck_name)。各 list 的字段集按资源独立维护;例如给章节传 description / deck_name,或给卡组传 text / content,都会报 invalid_argument,不会被静默忽略。
字符串比较默认大小写不敏感(case_sensitive: true 可关闭);数字按数值比较;ISO 时间按字符串比较(同一时区偏移下顺序正确);未知字段、未知 op、超过深度 / 节点限制都会以 invalid_argument 报错。
用 markji_cards 的 validate / syntax_help 两种 action:
markji_cards({ action: "syntax_help" }) // 全部格式
markji_cards({ action: "syntax_help", format: "picture" }) // 只查图片
markji_cards({ action: "validate", content: "<Markji 富文本>" }) // 校验正文
markji_cards({ action: "validate", contents: ["<正文1>", "<正文2>"] }) // 批量校验正文
markji_cards({ action: "validate", cards: ["<id1>", "<id2>"] }) // 批量校验已有卡片批量校验时 content、contents、cards 只能传一个;每个结果独立返回,坏项不会中断其他项。cards 使用卡片的有效正文(有排队修改时用排队内容)。
action=syntax_help 时 format 可省略或写 all;写 basic 只返回通用规则和嵌套限制。支持的格式 id 为 text-style、cloze、audio、card-ref、paragraph、choice、picture、formula、answer-line,也接受对应标签(如 Pic)和中文别名(如 图片)。帮助内容是静态的,不会检查或改写正文;语法以 Markji 制卡语法指南 为准。
DeckView = {
id, name, card_count, chapter_count,
folder_id?, description?, updated_time?,
is_private?, source?, status?, missing?,
}
ChapterView = {
id, name, card_count,
pending?: { op, name }, local?, missing?,
revision?, order?, updated_time?,
}
CardSummary = {
id, deck_name, chapter_name, snippet,
status?: "dirty" | "new" | "conflict",
}
CardView = {
id, deck_id, deck_name, chapter_id, chapter_name,
content: string, text: string, grammar_version: number,
status, cloud: null | { revision, updated_time },
created_time, updated_time,
local?, pending?, conflict?, last_error?,
}
ConflictView = {
id, kind, entity, deck_id, chapter_id, card_id, op,
local, remote, state, detected_at,
deck_name, chapter_name, card_snippet,
recovery: { tool, arguments, strategies, hint },
}
SyncTask = {
task_id, status: "running" | "completed" | "failed" | "interrupted",
mode: "auto" | "pull" | "push", stage,
started_at, finished_at, resume_count,
progress: {
message, decks_total, decks_done,
chapters?, cards?, cards_deleted?, adopted_cards?, adopted_chapters?,
conflicts_created?, chapters_created?, chapters_renamed?, chapters_sorted?,
cards_created?, cards_updated?, cards_moved?, errors?,
},
full?, error?: { code, message }, hint?,
report?: SyncReport,
}
SyncReport = {
ok, mode, duration_ms,
pull?: {
decks?, chapters?, cards?, cards_deleted?,
adopted_cards?, adopted_chapters?, conflicts?,
},
push?: {
chapters_created?, chapters_renamed?, chapters_sorted?,
cards_created?, cards_updated?, cards_moved?, conflicts?,
errors?: [{ op, id, code, message }], errors_total?,
},
conflicts?: [ConflictView],
errors?: [{ op, id, code, message }], errors_total?,
}
Status = {
version, configured, cache_file,
cache: {
decks, chapters, cards, bytes, pulled_at,
stale?, error?, save_error?,
},
pending: {
cards_new?, cards_dirty?, chapters_new?, chapters_rename?,
moves?, sorts?, conflicts?,
},
conflicts: [ConflictView],
last_sync: { ok, mode, started_at, finished_at, duration_ms, conflicts, errors }|null,
last_error: null | { code, message },
active_task: string|null, tasks: [SyncTask],
auto_sync: { enabled, running },
}
Failures = {
ok, summary: { conflicts, failed_cards, last_task_errors, last_error },
conflicts: [ConflictView],
failed_cards: [{
id, deck_id, deck_name, chapter_id, chapter_name,
status, snippet,
error: { code, message },
recovery: { tool, arguments, strategies, hint },
}],
last_task_errors: [{ task_id, op, id, code, message }],
truncated?: boolean,
}markji_cards({ action: "archive" }) 的 chapters 是 [{ deck_id, chapter_id, chapter_name, local }]。markji_file 的 file 由 Markji 返回,常见字段为 id、url、mime、size、info、expire_time。
默认配置写在 bundle 的 cordis.patch.yml,都可用默认值:
字段 · 默认值 · 说明 enabled · true · 是否注册工具并启动自动同步 apiKey · '' · 可选的 token 覆盖(建议留空,用环境变量) baseUrl · https://open.maimemo.com/open · API 地址 dataDir · /.dsh/dsh-markji-bridge · 缓存目录 grammarVersion · 3 · 新卡默认 grammar_version requestTimeoutMs · 20000 · 单次请求超时 maxRetries · 3 · 网络错误 / 429 / 5xx 重试次数 pageSize · 100 · 拉取分页大小 pullTtlMs · 600000 · 缓存过期后读操作先增量拉取 autoSync · true · 空闲时自动推送并拉取 idleCheckMs · 30000 · 空闲检查周期 idleQuietMs · 120000 · 最近一次工具调用后的静默时间 autoPullMs · 900000 · 无待办时的后台拉取周期 retryBackoffMs · 300000 · 自动同步失败后的重试间隔 maxOutputChars · 20000 · 单个工具结果最大字符数
markji_sync mode=auto:先拉取远端并三方合并,再推送本地队列;推送前一定会先拉取,避免旧数据覆盖远端修改。POST /api/v1/markji/files/query 检查 Pic#ID/...、Pic#ID/...,MID/... 和 Audio#...,ID/... 里的文件 id。缺失的文件不会写入云端,卡片保持待推送状态,markji_sync({ action: "failures" }) 会给出错误与恢复建议。keep_local(保留本地,下次推送覆盖云端)、keep_remote(采用云端,丢弃本地改动)、use_content(用指定内容替换本地待推送内容)。interrupted,用 markji_sync resume= 续跑。markji_sync({ action: "failures" }) 查看,处理完再执行 markji_sync({ action: "start", mode: "push" })。pullTtlMs(默认 10 分钟)或显式 refresh: true 时先做增量拉取。unknown_chapter,未知卡片约 0.3 秒;旧版全量拉取约 12.5 秒。markji_sync({ action: "start", mode: "pull", full: true });全量拉取是后台任务,先返回 task,再用 markji_sync({ action: "progress" }) 查进度。idleQuietMs(默认 2 分钟)触发;无待办时每 autoPullMs(默认 15 分钟)拉取;失败后按 retryBackoffMs(默认 5 分钟)重试。markji_cards 的 list 的 limit 上限 200(2.4.0 起统一,旧 search 的 100 上限已移除),批量入口单次最多 50 项(归档 / 移动不受此限)。工具错误一般形如 markji: : (hint: ...);参数格式错误可能是普通的 markji_xxx: "field" ... 消息(没有 code)。常见 code:
code · 含义与处理 not_configured · 没有可用的 token。设置 MAIMEMO_MARKJI_OPENAPI_TOKEN 后完全退出并重启 DSH,让主机进程读到新环境变量。 unknown_deck / unknown_chapter / unknown_card / unknown_folder · 本地缓存里没有这个 id。兜底刷新已尝试过;如果对象是在其他设备新建的,执行 markji_sync({ action: "start", mode: "pull", full: true }) 后再试。 invalid_argument · 参数缺失、类型不对、时间格式不对、chapter_ids 没有列全、内容为空或超过 100000 字符等。按消息修正参数。 invalid_content · 卡片内容不合法(例如规范化后为空)。 invalid_card_content · 卡片正文未通过原始 Markji 语法校验。先用 markji_cards({ action: "validate" }) 看行号 / 列号和错误码,修正后再推送。 media_unavailable · 正文引用的图片 / 遮罩 / 音频文件 id 在 Markji 查不到。先用 markji_file 上传,拿到 file.id 后更新正文。 media_check_failed · 推送前查询媒体文件失败(网络、鉴权等)。卡片会保留在队列里,恢复后重试 markji_sync。 conflict_open · 这张卡有未解决冲突。先用 markji_sync({ action: "resolve" }) 处理。 unknown_conflict · 冲突 id 不存在或已解决。用 markji_sync({ action: "failures" }) 重新获取当前冲突列表。 unknown_task / task_not_resumable · resume 的任务 id 不存在,或该任务不是 interrupted / 已结束。用 markji_sync({ action: "progress" }) 查任务状态。 deck_mismatch · 目标章节或卡片不属于指定卡组。检查 id 与 deck 参数是否匹配。 cache_busy · 另一个 DSH 进程正持有 cache.write.lock。等它结束再重试;不要手工删除活动进程的锁。 engine_unavailable · 插件引擎没有加载完整。停用再启用插件,或重启 DSH。 conflict_resolve_failed · 解决冲突时写入失败,详情见消息;通常可重试或改用 keep_remote。 bad_response · Markji 返回了无法解析的数据。重试;持续出现时检查 baseUrl 和网络。 common_* · Markji 服务端错误码(如 common_unauthorized、common_invalid_res_id、common_revision_mismatch),以服务端返回的 code 和消息为准。 ENOENT · markji_file 上传的本地路径不存在。
常见问题:
setx 后必须重启 DSH;WSL / Linux 要在启动 DSH 的同一 shell 里 export,再重启 DSH。cordis.patch.yml 或提交到仓库。