@xiaoyuyu6420/dsh-backup 0.6.1 → 0.6.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/lib/client.js CHANGED
@@ -330,5 +330,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
330
330
  max-height: 10em;
331
331
  overflow: auto;
332
332
  }
333
- `;var Ld={name:"@xiaoyuyu6420/dsh-backup",version:"0.6.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 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\u4E0B\u8F7D\u4E0E GitHub \u540C\u6B65\uFF0C\u9644 Settings \u53EF\u89C6\u9762\u677F\u3002",type:"module",main:"lib/index.js",exports:{".":"./lib/index.js","./client":"./lib/client.js","./cordis.patch.yml":"./cordis.patch.yml","./package.json":"./package.json"},files:["lib","cordis.patch.yml","README.md","README.zh.md","LICENSE"],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"],peerDependencies:{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-commands":"^0.1.0-rc.6","@deepseek-ai/dsh-fs":"^0.1.0-rc.6","@deepseek-ai/dsh-subprocess":"^0.1.0-rc.6","@deepseek-ai/dsh-tools":"^0.1.0-rc.6","@deepseek-ai/dsh-typert-protocol":"^0.1.0-rc.6"},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 jn="settings.backupPanel",Wf=Ld.name,Xf=["slots","locale","remote"],qf=x.object({destination:x.string(),dshHome:x.string(),keepDefault:x.number().int(),autoHours:x.number().int(),lastAuto:x.string().nullable(),backups:x.array(x.object({name:x.string(),size:x.number().int().nullable()}))}),Hf=x.object({ok:x.boolean(),summary:x.string(),path:x.string(),sha:x.string(),stale:x.number().int(),keep:x.number().int()}),Yf=x.object({ok:x.boolean(),summary:x.string(),results:x.array(x.object({name:x.string(),ok:x.boolean(),note:x.string()}))}),Qf=x.object({ok:x.boolean(),dryRun:x.boolean(),summary:x.string(),archive:x.string().nullable().optional(),files:x.number().int().nullable().optional(),aside:x.string().nullable().optional(),snapshotPath:x.string().nullable().optional(),sample:x.array(x.string()).optional()}),eg=x.object({ok:x.boolean(),hours:x.number().int(),summary:x.string()}),tg=x.object({repoRaw:x.string().nullable(),repo:x.string().nullable(),tokenSet:x.boolean(),syncDir:x.string(),lastPush:x.string().nullable(),lastError:x.string().nullable()}),rg=x.object({ok:x.boolean(),summary:x.string(),pushed:x.boolean(),tooBig:x.array(x.string())}),ng=x.object({ok:x.boolean(),summary:x.string()}),ig=x.object({ok:x.boolean(),repo:x.string().nullable(),summary:x.string()}),og={name:"keep",wire:"keep",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#keep",schema:x.number().int().positive().optional()},acceptsUndefined:!0},Nu={name:"selector",wire:"selector",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#selector",schema:x.string().optional()},acceptsUndefined:!0},ag={name:"dryRun",wire:"dryRun",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#dryRun",schema:x.boolean().optional()},acceptsUndefined:!0},cg={name:"hours",wire:"hours",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#hours",schema:x.number().int().min(0).max(720)},acceptsUndefined:!0},ug={name:"repo",wire:"repo",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#repo",schema:x.string().optional()},acceptsUndefined:!0};function le(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 Cd=Object.freeze({package:"dsh-backup",descriptors:Object.freeze([le("status",[],qf,!1),le("backup",[og],Hf,!0),le("verify",[Nu],Yf,!0),le("restore",[Nu,ag],Qf,!0),le("setAuto",[cg],eg,!1),le("githubStatus",[],tg,!1),le("githubSyncNow",[],rg,!0),le("removeEntry",[Nu],ng,!0),le("setGithubRepo",[ug],ig,!1)])});function de(t){if(!t.ok){let r=t.error;throw new Error(r&&r.message?`${r.code}: ${r.message}`:"backupPanel \u8C03\u7528\u5931\u8D25")}return t.value}function sg(t){t.effect(()=>t.locale.register(jn,{zh:Ed,en:Ad}),"dsh-backup: dictionaries"),t.effect(()=>Rd(),"dsh-backup: stylesheet"),t.effect(()=>{let r=null,i=!0,o=!1;return(async()=>{try{r=await t.remote.$mount(Cd)}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(jn),o=()=>r.remote.backupPanel,e={status:async()=>de(await o().status()),backup:async n=>de(await o().backup(n)),verify:async n=>de(await o().verify(n)),restore:async(n,a)=>de(await o().restore(n,a)),setAuto:async n=>de(await o().setAuto(n)),githubStatus:async()=>de(await o().githubStatus()),githubSyncNow:async()=>de(await o().githubSyncNow()),removeEntry:async n=>de(await o().removeEntry(n)),setGithubRepo:async n=>de(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:jn,inject:()=>({panel:e})},Td))})}
333
+ `;var Ld={name:"@xiaoyuyu6420/dsh-backup",version:"0.6.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 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\u4E0B\u8F7D\u4E0E GitHub \u540C\u6B65\uFF0C\u9644 Settings \u53EF\u89C6\u9762\u677F\u3002",type:"module",main:"lib/index.js",exports:{".":"./lib/index.js","./client":"./lib/client.js","./cordis.patch.yml":"./cordis.patch.yml","./package.json":"./package.json"},files:["lib","cordis.patch.yml","README.md","README.zh.md","LICENSE"],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"],peerDependencies:{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-commands":"^0.1.0-rc.6","@deepseek-ai/dsh-fs":"^0.1.0-rc.6","@deepseek-ai/dsh-subprocess":"^0.1.0-rc.6","@deepseek-ai/dsh-tools":"^0.1.0-rc.6","@deepseek-ai/dsh-typert-protocol":"^0.1.0-rc.6"},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 jn="settings.backupPanel",Wf=Ld.name,Xf=["slots","locale","remote"],qf=x.object({destination:x.string(),dshHome:x.string(),keepDefault:x.number().int(),autoHours:x.number().int(),lastAuto:x.string().nullable(),backups:x.array(x.object({name:x.string(),size:x.number().int().nullable()}))}),Hf=x.object({ok:x.boolean(),summary:x.string(),path:x.string(),sha:x.string(),stale:x.number().int(),keep:x.number().int()}),Yf=x.object({ok:x.boolean(),summary:x.string(),results:x.array(x.object({name:x.string(),ok:x.boolean(),note:x.string()}))}),Qf=x.object({ok:x.boolean(),dryRun:x.boolean(),summary:x.string(),archive:x.string().nullable().optional(),files:x.number().int().nullable().optional(),aside:x.string().nullable().optional(),snapshotPath:x.string().nullable().optional(),sample:x.array(x.string()).optional()}),eg=x.object({ok:x.boolean(),hours:x.number().int(),summary:x.string()}),tg=x.object({repoRaw:x.string().nullable(),repo:x.string().nullable(),tokenSet:x.boolean(),syncDir:x.string(),lastPush:x.string().nullable(),lastError:x.string().nullable()}),rg=x.object({ok:x.boolean(),summary:x.string(),pushed:x.boolean(),tooBig:x.array(x.string())}),ng=x.object({ok:x.boolean(),summary:x.string()}),ig=x.object({ok:x.boolean(),repo:x.string().nullable(),summary:x.string()}),og={name:"keep",wire:"keep",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#keep",schema:x.number().int().positive().optional()},acceptsUndefined:!0},Nu={name:"selector",wire:"selector",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#selector",schema:x.string().optional()},acceptsUndefined:!0},ag={name:"dryRun",wire:"dryRun",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#dryRun",schema:x.boolean().optional()},acceptsUndefined:!0},cg={name:"hours",wire:"hours",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#hours",schema:x.number().int().min(0).max(720)},acceptsUndefined:!0},ug={name:"repo",wire:"repo",source:"json",codec:{mode:"strict",typeSymbol:"dsh-backup/types#repo",schema:x.string().optional()},acceptsUndefined:!0};function le(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 Cd=Object.freeze({package:"dsh-backup",descriptors:Object.freeze([le("status",[],qf,!1),le("backup",[og],Hf,!0),le("verify",[Nu],Yf,!0),le("restore",[Nu,ag],Qf,!0),le("setAuto",[cg],eg,!1),le("githubStatus",[],tg,!1),le("githubSyncNow",[],rg,!0),le("removeEntry",[Nu],ng,!0),le("setGithubRepo",[ug],ig,!1)])});function de(t){if(!t.ok){let r=t.error;throw new Error(r&&r.message?`${r.code}: ${r.message}`:"backupPanel \u8C03\u7528\u5931\u8D25")}return t.value}function sg(t){t.effect(()=>t.locale.register(jn,{zh:Ed,en:Ad}),"dsh-backup: dictionaries"),t.effect(()=>Rd(),"dsh-backup: stylesheet"),t.effect(()=>{let r=null,i=!0,o=!1;return(async()=>{try{r=await t.remote.$mount(Cd)}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(jn),o=()=>r.remote.backupPanel,e={status:async()=>de(await o().status()),backup:async n=>de(await o().backup(n)),verify:async n=>de(await o().verify(n)),restore:async(n,a)=>de(await o().restore(n,a)),setAuto:async n=>de(await o().setAuto(n)),githubStatus:async()=>de(await o().githubStatus()),githubSyncNow:async()=>de(await o().githubSyncNow()),removeEntry:async n=>de(await o().removeEntry(n)),setGithubRepo:async n=>de(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:jn,inject:()=>({panel:e})},Td))})}
334
334
  return module.exports; } });
package/lib/index.js CHANGED
@@ -230,6 +230,31 @@ export function apply(ctx, pluginConfig) {
230
230
  return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}${pad(now.getMilliseconds(), 3)}`;
231
231
  }
232
232
 
233
+ /**
234
+ * 从 http(s) URL 中剥离 userinfo(`user:pass@` 段,含 token-as-username
235
+ * 形态)。GitHub 同步的认证始终走环境变量 token → 同步工作树的
236
+ * .git-credentials,不依赖 URL 内嵌凭据;但用户可能把 PAT 写进 repo
237
+ * URL,此时 token 会明文落入 auto.json 并被 `/backup github status` 与
238
+ * 面板 repoRaw 回显。此处在存/显前剥离。
239
+ * 仅作用于 http(s)——ssh://git@、file://、owner/repo、本地路径等保持原样
240
+ * (不同认证模型,剥 username 会破坏如 ssh://git@host)。用 URL 解析处理
241
+ * port/IPv6/path 含已编码 @ 等;非 URL / 无 userinfo / 解析异常原样返回。
242
+ */
243
+ function stripUserinfo(raw) {
244
+ if (typeof raw !== 'string' || !/^https?:\/\//i.test(raw)) return raw;
245
+ try {
246
+ const u = new URL(raw);
247
+ if (u.username || u.password) {
248
+ u.username = '';
249
+ u.password = '';
250
+ return u.toString();
251
+ }
252
+ return raw;
253
+ } catch {
254
+ return raw;
255
+ }
256
+ }
257
+
233
258
  /**
234
259
  * 删除 dir 下的文件。纯 Node fs.unlink(零 shell):文件名来自备份目录
235
260
  * 内容,拼接进 cmd/rm 命令行会构成命令注入面(Windows 下 cmd 的
@@ -251,6 +276,25 @@ export function apply(ctx, pluginConfig) {
251
276
  await rename(`${dir}/${srcName}`, `${dir}/${dstName}`);
252
277
  }
253
278
 
279
+ /**
280
+ * 清理旧的恢复前快照(dsh-pre-restore-* 归档 + 边车),仅保留 keepName
281
+ * 这一份。恢复是低频操作,快照又被 listBackups 排除、用户不可见,故每
282
+ * 次恢复后清掉前一次的快照防隐藏累积(aside 目录仍由用户自行管理)。
283
+ */
284
+ async function prunePreRestoreSnapshots(keepName, signal) {
285
+ const { root } = paths();
286
+ let dirents;
287
+ try {
288
+ dirents = await readdir(root, { withFileTypes: true });
289
+ } catch {
290
+ return;
291
+ }
292
+ const stale = dirents
293
+ .filter((d) => d.name.startsWith('dsh-pre-restore-') && (d.name.endsWith('.tar.gz') || d.name.endsWith('.sha256')) && d.name !== keepName && d.name !== `${keepName}.sha256`)
294
+ .map((d) => d.name);
295
+ if (stale.length) await removeFiles(stale, root, signal);
296
+ }
297
+
254
298
  async function listBackups() {
255
299
  const { root } = paths();
256
300
  let dirents;
@@ -262,7 +306,10 @@ export function apply(ctx, pluginConfig) {
262
306
  }
263
307
  const backups = [];
264
308
  for (const d of dirents) {
265
- if (!d.name.startsWith('dsh-') || !d.name.endsWith('.tar.gz')) continue;
309
+ // 恢复前自动快照(dsh-pre-restore-*)是内部安全网,不进用户列表、
310
+ // 不参与轮换、不被 pickArchive 的 latest/前缀误选——经 listBackups
311
+ // 过滤即可同时收口这三处入口。
312
+ if (!d.name.startsWith('dsh-') || d.name.startsWith('dsh-pre-restore-') || !d.name.endsWith('.tar.gz')) continue;
266
313
  let size;
267
314
  try {
268
315
  size = (await fsStat(`${root}/${d.name}`)).size;
@@ -302,13 +349,14 @@ export function apply(ctx, pluginConfig) {
302
349
  return createHash('sha256').update(bytes).digest('hex');
303
350
  }
304
351
 
305
- async function doBackup(keep, signal) {
352
+ async function doBackup(keep, signal, opts = {}) {
306
353
  const { home, dshHome, root } = paths();
307
354
  const keepN = keep && keep > 0 ? Math.floor(keep) : defaultKeep();
308
355
  // 状态文件先写:writeText 会自动创建备份目录,替代 mkdir -p(Windows 无 mkdir.exe)。
309
356
  await saveAutoState();
310
357
 
311
- const name = `dsh-${stampNow()}.tar.gz`;
358
+ const prefix = typeof opts?.namePrefix === 'string' && opts.namePrefix ? opts.namePrefix : 'dsh-';
359
+ const name = `${prefix}${stampNow()}.tar.gz`;
312
360
  const out = `${root}/${name}`;
313
361
  const base = dshHome.split('/').pop();
314
362
  const parent = dshHome.slice(0, -(base.length + 1)) || '/';
@@ -353,7 +401,7 @@ export function apply(ctx, pluginConfig) {
353
401
  await saveAutoState();
354
402
  }
355
403
 
356
- return { path: out, sha: shaText, total: all.length, stale: stale.length, keep: keepN, sync };
404
+ return { path: out, name, sha: shaText, total: all.length, stale: stale.length, keep: keepN, sync };
357
405
  }
358
406
 
359
407
  // ---------- GitHub 同步 ----------
@@ -369,7 +417,7 @@ export function apply(ctx, pluginConfig) {
369
417
  const token = env?.get('DSH_BACKUP_GITHUB_TOKEN')?.value || env?.get('GITHUB_TOKEN')?.value;
370
418
  // 本地路径(测试/自托管)或 http(s) 全 URL 直接使用,否则视为 owner/repo。
371
419
  const repo = raw.includes('://') || /^[A-Za-z]:[\\/]|^\//.test(raw) ? raw : `https://github.com/${raw}.git`;
372
- return { repo: repo.split('\\').join('/'), token };
420
+ return { repo: stripUserinfo(repo.split('\\').join('/')), token };
373
421
  }
374
422
 
375
423
  /** 校验仓库地址格式(owner/repo、完整 URL 或本地路径),非法返回原因。 */
@@ -638,7 +686,10 @@ export function apply(ctx, pluginConfig) {
638
686
  if (dryRun) return { archive, files: entries.length, sample: entries.slice(0, 12), aside: null, snapshotPath: null, dryRun: true };
639
687
 
640
688
  // 恢复前自动快照当前数据,并把当前数据移到旁边(而非合并覆盖)。
641
- const snapshot = await doBackup(undefined, signal);
689
+ // 快照用 dsh-pre-restore- 前缀与用户常规备份区分,经 listBackups 过滤
690
+ // 后不进列表/轮换/latest 选择;每次恢复清掉上一次的快照防累积。
691
+ const snapshot = await doBackup(undefined, signal, { namePrefix: 'dsh-pre-restore-' });
692
+ await prunePreRestoreSnapshots(snapshot.name, signal);
642
693
  let aside = null;
643
694
  let current = false;
644
695
  try {
@@ -689,9 +740,11 @@ export function apply(ctx, pluginConfig) {
689
740
  lastError: typeof parsed.github.lastError === 'string' ? parsed.github.lastError : null,
690
741
  };
691
742
  }
692
- lastAutoAt = typeof parsed?.lastAutoAt === 'string' && !Number.isNaN(Date.parse(parsed.lastAutoAt))
693
- ? parsed.lastAutoAt
694
- : null;
743
+ // lastAutoAt 是「上次执行」时间戳,不可能在未来。未来日期(注入或时钟
744
+ // 漂移)会使 nextAutoAt 算出超大延迟、auto 静默失效却仍显示已开启,
745
+ // 故一律视为非法 → null,从当前时间起算 catch-up。
746
+ const lastTs = typeof parsed?.lastAutoAt === 'string' ? Date.parse(parsed.lastAutoAt) : NaN;
747
+ lastAutoAt = Number.isNaN(lastTs) || lastTs > Date.now() ? null : parsed.lastAutoAt;
695
748
  return Number.isFinite(h) && h >= 1 && h <= 720 ? Math.floor(h) : 0;
696
749
  } catch (err) {
697
750
  console.warn(`[dsh-backup] auto.json 无法读取,自动备份计划已重置: ${String(err && err.message ? err.message : err)}`);
@@ -808,9 +861,10 @@ export function apply(ctx, pluginConfig) {
808
861
  }
809
862
  const invalid = validateRepo(value);
810
863
  if (invalid) return { kind: 'error', text: invalid };
811
- githubState = { ...githubState, repo: value, lastError: null };
864
+ const clean = stripUserinfo(value);
865
+ githubState = { ...githubState, repo: clean, lastError: null };
812
866
  await saveAutoState();
813
- return { kind: 'success', text: `GitHub 同步仓库已设为: ${value}\n${autoSummary()}` };
867
+ return { kind: 'success', text: `GitHub 同步仓库已设为: ${clean}\n${autoSummary()}` };
814
868
  }
815
869
  if (arg === 'sync') {
816
870
  const s = await githubSync(invocation.signal);
@@ -1009,7 +1063,8 @@ export function apply(ctx, pluginConfig) {
1009
1063
  const { root } = paths();
1010
1064
  return {
1011
1065
  // repoRaw 是用户原始输入(运行时值优先,否则 cordis.yml 默认),供面板编辑框回填
1012
- repoRaw: githubState.repo ?? (typeof pluginConfig?.githubRepo === 'string' ? pluginConfig.githubRepo : null),
1066
+ // 两路都经 stripUserinfo:避免 cordis.yml config.githubRepo 内嵌 token 时经面板泄露
1067
+ repoRaw: githubState.repo ?? (typeof pluginConfig?.githubRepo === 'string' ? stripUserinfo(pluginConfig.githubRepo) : null),
1013
1068
  repo: cfg ? cfg.repo : null,
1014
1069
  tokenSet: Boolean(cfg?.token),
1015
1070
  syncDir: `${root}/${SYNC_DIR}`,
@@ -1052,9 +1107,10 @@ export function apply(ctx, pluginConfig) {
1052
1107
  }
1053
1108
  const invalid = validateRepo(raw);
1054
1109
  if (invalid) return { ok: false, summary: invalid };
1055
- githubState = { ...githubState, repo: raw, lastError: null };
1110
+ const clean = stripUserinfo(raw);
1111
+ githubState = { ...githubState, repo: clean, lastError: null };
1056
1112
  await saveAutoState();
1057
- return { ok: true, repo: raw, summary: `GitHub 同步仓库已设为: ${raw}` };
1113
+ return { ok: true, repo: clean, summary: `GitHub 同步仓库已设为: ${clean}` };
1058
1114
  },
1059
1115
  };
1060
1116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaoyuyu6420/dsh-backup",
3
- "version": "0.6.1",
3
+ "version": "0.6.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 and a visual Settings panel. Cross-platform (macOS/Linux/Windows). 一键备份与恢复 DSH 数据:定时自动备份、完整性校验、下载与 GitHub 同步,附 Settings 可视面板。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",