@xiaoyuyu6420/dsh-backup 0.11.1 → 0.11.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/README.md CHANGED
@@ -131,6 +131,7 @@ Tried it? Tell us what broke, what's missing, what you liked — it directly sha
131
131
  <details>
132
132
  <summary>Recent releases</summary>
133
133
 
134
+ - **0.11.2** — doctor line-level SessionHeader validation aligned with the host `isHeaderLine` (closes the known gap from 0.11.0): `version`/`createdAt`/`delegationDepth` type + non-negative-safe-integer checks (including `-0` rejection), optional `seedLength`/`origin`/`agentPreset` branches, and retired `sandboxMode`/`approvalPolicy` fields the host reader rejects — headers the host refuses to load are now flagged instead of reported healthy. Verified field-by-field against the compiled host source (both trains; the rc.1 checker is the strict superset) with an independent review verdict of ALIGN; 11 bad-header negative samples + repair round-trip added to the suite.
134
135
  - **0.11.1** — dsh `0.1.2-rc.1` compatibility: adapts to the removal of `settingsNamespace` from `@deepseek-ai/dsh-settings` (settings now register under the plain `dsh-backup` namespace — value-identical, so existing settings, backups and archives survive an in-place update), works behind the new forced web auth (303 + HttpOnly cookie), and widens peer ranges to `^0.1.1-rc.2 || ^0.1.2-rc.1`. Verified on both trains with real-host e2e (32/32 each) plus an in-place 0.11.0 → 0.11.1 upgrade test (settings preserved, old archives restorable).
135
136
  - **0.11.0** — doctor container-contract check: the first zstd frame must decode to exactly one header line, byte-precise (non-empty, first newline at the last byte — aligned with the host reader). Single-frame rewrites, stray blank lines in the first frame, a missing trailing newline and skippable frames are now flagged corrupt (previously reported healthy while the host refused to load them); the rescue console checks in sync. Found via a community audit on deepseek-harness #1047.
136
137
  - **0.10.0** — typed backups: back up just what you need (`/backup --types skills,sessions`) and merge-restore a subset (`/backup restore <archive> --types skills`); per-type archives rotate in their own bucket. Credential-type archives stay out of GitHub sync; cross-machine guardrails unchanged.
package/README.zh.md CHANGED
@@ -131,6 +131,7 @@ dsh plugin --profile web add github:xiaoyuyu6420/dsh-backup
131
131
  <details>
132
132
  <summary>最近的版本</summary>
133
133
 
134
+ - **0.11.2** —— doctor 行级 SessionHeader 校验对齐宿主 `isHeaderLine`(闭环 0.11.0 已知遗留):补 `version`/`createdAt`/`delegationDepth` 类型与非负安全整数三连(含 `-0` 拒绝)、`seedLength`/`origin`/`agentPreset` 可选分支、退役字段 `sandboxMode`/`approvalPolicy`——宿主拒载的 header 不再被误报健康。对照双列车宿主编译产物逐字段验证(rc.1 为严格超集),独立复核结论 ALIGN;测试套新增 11 种坏 header 负样本 + 修复往返。
134
135
  - **0.11.1** —— 适配 dsh `0.1.2-rc.1`:跟随 `@deepseek-ai/dsh-settings` 移除 `settingsNamespace`(改用普通 `dsh-backup` 命名空间注册——取值完全一致,原地更新后设置、备份与旧归档全部无损),支持 Web 强制鉴权(303 + HttpOnly cookie),peer 范围放宽为 `^0.1.1-rc.2 || ^0.1.2-rc.1`。双列车真宿主 e2e 各 32/32 通过,另做了 0.11.0 → 0.11.1 原地升级测试(设置保留、旧归档可恢复)。
135
136
  - **0.11.0** —— doctor 容器契约校验:首帧必须解出「恰好一行 header、单个换行结尾」(字节精确,对齐宿主读端)。单帧重写、首帧多余空行、缺行尾、skippable 帧现在都会判损坏(此前报健康但宿主拒载);救援台同步。来自 deepseek-harness 官方讨论区 #1047 的社区审计。
136
137
  - **0.10.0** —— 分类型备份:只备份需要的部分(`/backup --types skills,sessions`),按类型 merge 恢复(`/backup restore <归档> --types skills`);类型归档独立轮换不挤全量配额。含凭据的类型归档永不进 GitHub 同步,跨机护栏不变。
package/lib/client.js CHANGED
@@ -450,5 +450,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
450
450
  color: var(--dsw-alias-state-error-primary, #e53e3e);
451
451
  line-height: 1.5;
452
452
  }
453
- `;var pm={name:"@xiaoyuyu6420/dsh-backup",version:"0.11.1",description:"Backup, restore, download and GitHub-sync DeepSeek Harness user data (~/.dsh): /backup, scheduled auto-backup that survives restarts, sha256 checksums, integrity verify, rotation, credential redaction with a local vault (plaintext never leaves the machine), cross-machine restore preflight with github pull, and a visual Settings panel. Cross-platform (macOS/Linux/Windows). \u4E00\u952E\u5907\u4EFD\u4E0E\u6062\u590D DSH \u6570\u636E\uFF1A\u5B9A\u65F6\u81EA\u52A8\u5907\u4EFD\u3001\u5B8C\u6574\u6027\u6821\u9A8C\u3001\u51ED\u636E\u9ED8\u8BA4\u8131\u654F\uFF08\u660E\u6587\u53EA\u5B58\u672C\u673A vault\uFF09\u3001\u8DE8\u673A\u6062\u590D\u9884\u68C0\u4E0E github pull \u62C9\u53D6\uFF0C\u9644 Settings \u53EF\u89C6\u9762\u677F\u3002",type:"module",packageManager:"pnpm@11.24.0",main:"lib/index.js",exports:{".":"./lib/index.js","./client":"./lib/client.js","./cordis.patch.yml":"./cordis.patch.yml","./package.json":"./package.json"},files:["lib","rescue","cordis.patch.yml","README.md","README.zh.md","LICENSE"],bin:{"dsh-rescue":"./rescue/rescue.mjs"},dsh:{bundle:{patch:"./cordis.patch.yml"},client:{platform:"web",inject:["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-client-ui-settings"]}},keywords:["dsh","dsh-plugin","deepseek-harness","backup","\u5907\u4EFD","restore","\u6062\u590D","schedule","\u81EA\u52A8\u5907\u4EFD","cross-platform","rescue","doctor","upgrade-snapshot","\u6570\u636E\u5B89\u5168"],peerDependencies:{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-commands":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-fs":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-settings":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-subprocess":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-tools":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-typert-protocol":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/schemastery":"^3.18.1"},devDependencies:{esbuild:"^0.25.0",react:"^19.0.0","react-dom":"^19.0.0",zod:"^4.4.3"},engines:{node:">=20"},license:"MIT",repository:{type:"git",url:"git+https://github.com/xiaoyuyu6420/dsh-backup.git"},homepage:"https://github.com/xiaoyuyu6420/dsh-backup#readme",bugs:{url:"https://github.com/xiaoyuyu6420/dsh-backup/issues"},publishConfig:{access:"public"}};var Cn="settings.backupPanel",Ig=pm.name,jg=["slots","locale","remote"],Pg=y.object({destination:y.string(),dshHome:y.string(),keepDefault:y.number().int(),autoHours:y.number().int(),lastAuto:y.string().nullable(),backups:y.array(y.object({name:y.string(),size:y.number().int().nullable()})),typedBackups:y.array(y.object({name:y.string(),size:y.number().int().nullable(),types:y.array(y.string())})).optional()}),Ug=y.object({ok:y.boolean(),summary:y.string(),path:y.string(),sha:y.string(),stale:y.number().int(),keep:y.number().int(),types:y.array(y.string()).optional(),hasCredentials:y.boolean().optional()}),Dg=y.object({ok:y.boolean(),summary:y.string(),results:y.array(y.object({name:y.string(),ok:y.boolean(),note:y.string()}))}),Og=y.object({ok:y.boolean(),dryRun:y.boolean(),summary:y.string(),archive:y.string().nullable().optional(),files:y.number().int().nullable().optional(),aside:y.string().nullable().optional(),snapshotPath:y.string().nullable().optional(),sample:y.array(y.string()).optional(),preflight:y.array(y.string()).optional(),targetExists:y.boolean().nullable().optional(),merge:y.boolean().optional(),types:y.array(y.string()).optional(),willOverwrite:y.array(y.string()).optional(),restored:y.number().int().optional(),kept:y.array(y.string()).optional()}),Ng=y.object({ok:y.boolean(),hours:y.number().int(),summary:y.string()}),Zg=y.object({repoRaw:y.string().nullable(),repo:y.string().nullable(),tokenSet:y.boolean(),syncDir:y.string(),lastPush:y.string().nullable(),lastError:y.string().nullable()}),Tg=y.object({ok:y.boolean(),summary:y.string(),pushed:y.boolean(),tooBig:y.array(y.string())}),Eg=y.object({ok:y.boolean(),summary:y.string(),pulled:y.array(y.string()),corrupt:y.array(y.string()),total:y.number().int()}),Ag=y.object({ok:y.boolean(),summary:y.string()}),Lg=y.object({ok:y.boolean(),repo:y.string().nullable(),summary:y.string()}),Rg={name:"keep",wire:"keep",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#keep",schema:y.number().int().positive().optional()},acceptsUndefined:!0},Hu={name:"selector",wire:"selector",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#selector",schema:y.string().optional()},acceptsUndefined:!0},Cg={name:"dryRun",wire:"dryRun",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#dryRun",schema:y.boolean().optional()},acceptsUndefined:!0},Fg={name:"syncDeps",wire:"syncDeps",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#syncDeps",schema:y.boolean().optional()},acceptsUndefined:!0},Jg={name:"hours",wire:"hours",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#hours",schema:y.number().int().min(0).max(720)},acceptsUndefined:!0},Vg={name:"repo",wire:"repo",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#repo",schema:y.string().optional()},acceptsUndefined:!0},fm={name:"types",wire:"types",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#types",schema:y.array(y.string()).optional()},acceptsUndefined:!0};function de(t,r,i,o){return Object.freeze({id:`dsh-backup#backupPanel/${t}`,service:"backupPanel",namespace:"backupPanel",method:t,invocation:Object.freeze({kind:"direct"}),parameters:Object.freeze(r.map(e=>Object.freeze({...e,codec:Object.freeze(e.codec)}))),...o?{cancellation:Object.freeze({parameter:"signal"})}:{},result:Object.freeze({mode:"strict",typeSymbol:`dsh-backup/types#${t}Result`,schema:i})})}var gm=Object.freeze({package:"dsh-backup",descriptors:Object.freeze([de("status",[],Pg,!1),de("backup",[Rg,fm],Ug,!0),de("verify",[Hu],Dg,!0),de("restore",[Hu,Cg,fm,Fg],Og,!0),de("setAuto",[Jg],Ng,!1),de("githubStatus",[],Zg,!1),de("githubSyncNow",[],Tg,!0),de("githubPull",[],Eg,!0),de("removeEntry",[Hu],Ag,!0),de("setGithubRepo",[Vg],Lg,!1)])});function me(t){if(!t.ok){let r=t.error,i=r&&r.message?`${r.code}: ${r.message}`:"backupPanel \u8C03\u7528\u5931\u8D25";throw new Error(`\u64CD\u4F5C\u6CA1\u6709\u5B8C\u6210\uFF1A\u9762\u677F\u6682\u65F6\u8FDE\u4E0D\u4E0A\u540E\u53F0\uFF0C\u8BF7\u91CD\u8BD5\uFF1B\u82E5\u53CD\u590D\u51FA\u73B0\u8BF7\u91CD\u542F dsh web\uFF08\u6280\u672F\u7EC6\u8282: ${i}\uFF09`)}return t.value}function Mg(t){t.effect(()=>t.locale.register(Cn,{zh:lm,en:dm}),"dsh-backup: dictionaries"),t.effect(()=>mm(),"dsh-backup: stylesheet"),t.effect(()=>{let r=null,i=!0,o=!1;return(async()=>{try{r=await t.remote.$mount(gm)}catch(e){console.error("dsh-backup: backupPanel mount failed:",e)}i=!1,o&&r?.()})(),()=>{o=!0,i||r?.()}},"dsh-backup: remote contribution"),t.inject(["remote.backupPanel"],r=>{let i=r.locale.bind(Cn),o=()=>r.remote.backupPanel,e={status:async()=>me(await o().status()),backup:async(n,a)=>me(await o().backup(n,a)),verify:async n=>me(await o().verify(n)),restore:async(n,a,c,s)=>me(await o().restore(n,a,c,s)),setAuto:async n=>me(await o().setAuto(n)),githubStatus:async()=>me(await o().githubStatus()),githubSyncNow:async()=>me(await o().githubSyncNow()),githubPull:async()=>me(await o().githubPull()),removeEntry:async n=>me(await o().removeEntry(n)),setGithubRepo:async n=>me(await o().setGithubRepo(n))};r.slots.inject("settings.plugins.tab",()=>r.slots.register({name:"settings.plugins.tab",id:"backup",order:35,label:()=>i("tab"),locale:Cn,inject:()=>({panel:e})},sm))})}
453
+ `;var pm={name:"@xiaoyuyu6420/dsh-backup",version:"0.11.2",description:"Backup, restore, download and GitHub-sync DeepSeek Harness user data (~/.dsh): /backup, scheduled auto-backup that survives restarts, sha256 checksums, integrity verify, rotation, credential redaction with a local vault (plaintext never leaves the machine), cross-machine restore preflight with github pull, and a visual Settings panel. Cross-platform (macOS/Linux/Windows). \u4E00\u952E\u5907\u4EFD\u4E0E\u6062\u590D DSH \u6570\u636E\uFF1A\u5B9A\u65F6\u81EA\u52A8\u5907\u4EFD\u3001\u5B8C\u6574\u6027\u6821\u9A8C\u3001\u51ED\u636E\u9ED8\u8BA4\u8131\u654F\uFF08\u660E\u6587\u53EA\u5B58\u672C\u673A vault\uFF09\u3001\u8DE8\u673A\u6062\u590D\u9884\u68C0\u4E0E github pull \u62C9\u53D6\uFF0C\u9644 Settings \u53EF\u89C6\u9762\u677F\u3002",type:"module",packageManager:"pnpm@11.24.0",main:"lib/index.js",exports:{".":"./lib/index.js","./client":"./lib/client.js","./cordis.patch.yml":"./cordis.patch.yml","./package.json":"./package.json"},files:["lib","rescue","cordis.patch.yml","README.md","README.zh.md","LICENSE"],bin:{"dsh-rescue":"./rescue/rescue.mjs"},dsh:{bundle:{patch:"./cordis.patch.yml"},client:{platform:"web",inject:["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-client-ui-settings"]}},keywords:["dsh","dsh-plugin","deepseek-harness","backup","\u5907\u4EFD","restore","\u6062\u590D","schedule","\u81EA\u52A8\u5907\u4EFD","cross-platform","rescue","doctor","upgrade-snapshot","\u6570\u636E\u5B89\u5168"],peerDependencies:{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-commands":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-fs":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-settings":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-subprocess":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-tools":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/dsh-typert-protocol":"^0.1.1-rc.2 || ^0.1.2-rc.1","@deepseek-ai/schemastery":"^3.18.1"},devDependencies:{esbuild:"^0.25.0",react:"^19.0.0","react-dom":"^19.0.0",zod:"^4.4.3"},engines:{node:">=20"},license:"MIT",repository:{type:"git",url:"git+https://github.com/xiaoyuyu6420/dsh-backup.git"},homepage:"https://github.com/xiaoyuyu6420/dsh-backup#readme",bugs:{url:"https://github.com/xiaoyuyu6420/dsh-backup/issues"},publishConfig:{access:"public"}};var Cn="settings.backupPanel",Ig=pm.name,jg=["slots","locale","remote"],Pg=y.object({destination:y.string(),dshHome:y.string(),keepDefault:y.number().int(),autoHours:y.number().int(),lastAuto:y.string().nullable(),backups:y.array(y.object({name:y.string(),size:y.number().int().nullable()})),typedBackups:y.array(y.object({name:y.string(),size:y.number().int().nullable(),types:y.array(y.string())})).optional()}),Ug=y.object({ok:y.boolean(),summary:y.string(),path:y.string(),sha:y.string(),stale:y.number().int(),keep:y.number().int(),types:y.array(y.string()).optional(),hasCredentials:y.boolean().optional()}),Dg=y.object({ok:y.boolean(),summary:y.string(),results:y.array(y.object({name:y.string(),ok:y.boolean(),note:y.string()}))}),Og=y.object({ok:y.boolean(),dryRun:y.boolean(),summary:y.string(),archive:y.string().nullable().optional(),files:y.number().int().nullable().optional(),aside:y.string().nullable().optional(),snapshotPath:y.string().nullable().optional(),sample:y.array(y.string()).optional(),preflight:y.array(y.string()).optional(),targetExists:y.boolean().nullable().optional(),merge:y.boolean().optional(),types:y.array(y.string()).optional(),willOverwrite:y.array(y.string()).optional(),restored:y.number().int().optional(),kept:y.array(y.string()).optional()}),Ng=y.object({ok:y.boolean(),hours:y.number().int(),summary:y.string()}),Zg=y.object({repoRaw:y.string().nullable(),repo:y.string().nullable(),tokenSet:y.boolean(),syncDir:y.string(),lastPush:y.string().nullable(),lastError:y.string().nullable()}),Tg=y.object({ok:y.boolean(),summary:y.string(),pushed:y.boolean(),tooBig:y.array(y.string())}),Eg=y.object({ok:y.boolean(),summary:y.string(),pulled:y.array(y.string()),corrupt:y.array(y.string()),total:y.number().int()}),Ag=y.object({ok:y.boolean(),summary:y.string()}),Lg=y.object({ok:y.boolean(),repo:y.string().nullable(),summary:y.string()}),Rg={name:"keep",wire:"keep",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#keep",schema:y.number().int().positive().optional()},acceptsUndefined:!0},Hu={name:"selector",wire:"selector",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#selector",schema:y.string().optional()},acceptsUndefined:!0},Cg={name:"dryRun",wire:"dryRun",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#dryRun",schema:y.boolean().optional()},acceptsUndefined:!0},Fg={name:"syncDeps",wire:"syncDeps",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#syncDeps",schema:y.boolean().optional()},acceptsUndefined:!0},Jg={name:"hours",wire:"hours",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#hours",schema:y.number().int().min(0).max(720)},acceptsUndefined:!0},Vg={name:"repo",wire:"repo",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#repo",schema:y.string().optional()},acceptsUndefined:!0},fm={name:"types",wire:"types",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#types",schema:y.array(y.string()).optional()},acceptsUndefined:!0};function de(t,r,i,o){return Object.freeze({id:`dsh-backup#backupPanel/${t}`,service:"backupPanel",namespace:"backupPanel",method:t,invocation:Object.freeze({kind:"direct"}),parameters:Object.freeze(r.map(e=>Object.freeze({...e,codec:Object.freeze(e.codec)}))),...o?{cancellation:Object.freeze({parameter:"signal"})}:{},result:Object.freeze({mode:"strict",typeSymbol:`dsh-backup/types#${t}Result`,schema:i})})}var gm=Object.freeze({package:"dsh-backup",descriptors:Object.freeze([de("status",[],Pg,!1),de("backup",[Rg,fm],Ug,!0),de("verify",[Hu],Dg,!0),de("restore",[Hu,Cg,fm,Fg],Og,!0),de("setAuto",[Jg],Ng,!1),de("githubStatus",[],Zg,!1),de("githubSyncNow",[],Tg,!0),de("githubPull",[],Eg,!0),de("removeEntry",[Hu],Ag,!0),de("setGithubRepo",[Vg],Lg,!1)])});function me(t){if(!t.ok){let r=t.error,i=r&&r.message?`${r.code}: ${r.message}`:"backupPanel \u8C03\u7528\u5931\u8D25";throw new Error(`\u64CD\u4F5C\u6CA1\u6709\u5B8C\u6210\uFF1A\u9762\u677F\u6682\u65F6\u8FDE\u4E0D\u4E0A\u540E\u53F0\uFF0C\u8BF7\u91CD\u8BD5\uFF1B\u82E5\u53CD\u590D\u51FA\u73B0\u8BF7\u91CD\u542F dsh web\uFF08\u6280\u672F\u7EC6\u8282: ${i}\uFF09`)}return t.value}function Mg(t){t.effect(()=>t.locale.register(Cn,{zh:lm,en:dm}),"dsh-backup: dictionaries"),t.effect(()=>mm(),"dsh-backup: stylesheet"),t.effect(()=>{let r=null,i=!0,o=!1;return(async()=>{try{r=await t.remote.$mount(gm)}catch(e){console.error("dsh-backup: backupPanel mount failed:",e)}i=!1,o&&r?.()})(),()=>{o=!0,i||r?.()}},"dsh-backup: remote contribution"),t.inject(["remote.backupPanel"],r=>{let i=r.locale.bind(Cn),o=()=>r.remote.backupPanel,e={status:async()=>me(await o().status()),backup:async(n,a)=>me(await o().backup(n,a)),verify:async n=>me(await o().verify(n)),restore:async(n,a,c,s)=>me(await o().restore(n,a,c,s)),setAuto:async n=>me(await o().setAuto(n)),githubStatus:async()=>me(await o().githubStatus()),githubSyncNow:async()=>me(await o().githubSyncNow()),githubPull:async()=>me(await o().githubPull()),removeEntry:async n=>me(await o().removeEntry(n)),setGithubRepo:async n=>me(await o().setGithubRepo(n))};r.slots.inject("settings.plugins.tab",()=>r.slots.register({name:"settings.plugins.tab",id:"backup",order:35,label:()=>i("tab"),locale:Cn,inject:()=>({panel:e})},sm))})}
454
454
  return module.exports; } });
package/lib/index.js CHANGED
@@ -1944,9 +1944,20 @@ export function apply(ctx, pluginConfig) {
1944
1944
  } catch {
1945
1945
  return `首行不是 JSON(${snippet(String(lines[0]))})`;
1946
1946
  }
1947
- if (!header || typeof header !== 'object' || header.type !== 'session' || typeof header.id !== 'string') {
1948
- return '首行不是有效的 SessionHeader(缺 type:"session" id)';
1949
- }
1947
+ if (!header || typeof header !== 'object') return '首行不是对象';
1948
+ // 行级 SessionHeader 校验对齐宿主 isHeaderLine(0.1.2-rc.1 是 0.1.1-rc.2 的严格
1949
+ // 超集——多查 seedLength;以严格者为准覆盖双列车)。宿主读端 isHeaderLine 通过后,
1950
+ // fromHeaderLine 还会拒绝退役字段 sandboxMode/approvalPolicy——两者合并即"能否加载",
1951
+ // 故 doctor 全检。字段语义取自 @deepseek-ai/dsh-session-persistence-jsonl format.ts。
1952
+ if (header.type !== 'session') return `首行 type 不是 "session"(${snippet(JSON.stringify(header.type) ?? String(header.type))})`;
1953
+ if (typeof header.version !== 'number') return `首行 version 不是 number(${snippet(typeof header.version)})`;
1954
+ if (typeof header.id !== 'string') return '首行 id 不是 string';
1955
+ if (typeof header.createdAt !== 'number' || !Number.isSafeInteger(header.createdAt) || header.createdAt < 0 || Object.is(header.createdAt, -0)) return `首行 createdAt 非法(须非负安全整数,实际 ${snippet(String(header.createdAt))})`;
1956
+ if (typeof header.delegationDepth !== 'number' || !Number.isSafeInteger(header.delegationDepth) || header.delegationDepth < 0 || Object.is(header.delegationDepth, -0)) return `首行 delegationDepth 非法(须非负安全整数,实际 ${snippet(String(header.delegationDepth))})`;
1957
+ if (header.seedLength !== undefined && (typeof header.seedLength !== 'number' || !Number.isSafeInteger(header.seedLength) || header.seedLength < 0 || Object.is(header.seedLength, -0))) return '首行 seedLength 非法(须 undefined 或非负安全整数)';
1958
+ if (header.origin !== undefined && header.origin !== 'subagent') return '首行 origin 非法(须 undefined 或 "subagent")';
1959
+ if (header.agentPreset !== undefined && typeof header.agentPreset !== 'string') return '首行 agentPreset 非法(须 undefined 或 string)';
1960
+ if (Object.hasOwn(header, 'sandboxMode') || Object.hasOwn(header, 'approvalPolicy')) return '首行含退役字段 sandboxMode/approvalPolicy——宿主读端拒绝';
1950
1961
  let nextSeq = 0;
1951
1962
  for (let i = 1; i < lines.length; i++) {
1952
1963
  let rec;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaoyuyu6420/dsh-backup",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
4
4
  "description": "Backup, restore, download and GitHub-sync DeepSeek Harness user data (~/.dsh): /backup, scheduled auto-backup that survives restarts, sha256 checksums, integrity verify, rotation, credential redaction with a local vault (plaintext never leaves the machine), cross-machine restore preflight with github pull, and a visual Settings panel. Cross-platform (macOS/Linux/Windows). 一键备份与恢复 DSH 数据:定时自动备份、完整性校验、凭据默认脱敏(明文只存本机 vault)、跨机恢复预检与 github pull 拉取,附 Settings 可视面板。",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.24.0",
package/rescue/rescue.mjs CHANGED
@@ -283,9 +283,20 @@ function validateSessionLines(lines) {
283
283
  } catch {
284
284
  return `首行不是 JSON(${snippet(String(lines[0]))})`;
285
285
  }
286
- if (!header || typeof header !== 'object' || header.type !== 'session' || typeof header.id !== 'string') {
287
- return '首行不是有效的 SessionHeader(缺 type:"session" id)';
288
- }
286
+ if (!header || typeof header !== 'object') return '首行不是对象';
287
+ // 行级 SessionHeader 校验对齐宿主 isHeaderLine(0.1.2-rc.1 是 0.1.1-rc.2 的严格
288
+ // 超集——多查 seedLength;以严格者为准覆盖双列车)。宿主读端 isHeaderLine 通过后,
289
+ // fromHeaderLine 还会拒绝退役字段 sandboxMode/approvalPolicy——两者合并即"能否加载",
290
+ // 故 doctor 全检。字段语义取自 @deepseek-ai/dsh-session-persistence-jsonl format.ts。
291
+ if (header.type !== 'session') return `首行 type 不是 "session"(${snippet(JSON.stringify(header.type) ?? String(header.type))})`;
292
+ if (typeof header.version !== 'number') return `首行 version 不是 number(${snippet(typeof header.version)})`;
293
+ if (typeof header.id !== 'string') return '首行 id 不是 string';
294
+ if (typeof header.createdAt !== 'number' || !Number.isSafeInteger(header.createdAt) || header.createdAt < 0 || Object.is(header.createdAt, -0)) return `首行 createdAt 非法(须非负安全整数,实际 ${snippet(String(header.createdAt))})`;
295
+ if (typeof header.delegationDepth !== 'number' || !Number.isSafeInteger(header.delegationDepth) || header.delegationDepth < 0 || Object.is(header.delegationDepth, -0)) return `首行 delegationDepth 非法(须非负安全整数,实际 ${snippet(String(header.delegationDepth))})`;
296
+ if (header.seedLength !== undefined && (typeof header.seedLength !== 'number' || !Number.isSafeInteger(header.seedLength) || header.seedLength < 0 || Object.is(header.seedLength, -0))) return '首行 seedLength 非法(须 undefined 或非负安全整数)';
297
+ if (header.origin !== undefined && header.origin !== 'subagent') return '首行 origin 非法(须 undefined 或 "subagent")';
298
+ if (header.agentPreset !== undefined && typeof header.agentPreset !== 'string') return '首行 agentPreset 非法(须 undefined 或 string)';
299
+ if (Object.hasOwn(header, 'sandboxMode') || Object.hasOwn(header, 'approvalPolicy')) return '首行含退役字段 sandboxMode/approvalPolicy——宿主读端拒绝';
289
300
  let nextSeq = 0;
290
301
  for (let i = 1; i < lines.length; i++) {
291
302
  let rec;