dsh-desktop-kit 0.0.0-stage → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +152 -2
- package/client.js +4101 -0
- package/cordis.patch.yml +5 -0
- package/docs/ARCHITECTURE.md +114 -0
- package/docs/COMPATIBILITY.md +61 -0
- package/docs/INSTALL.md +84 -0
- package/docs/PRIVACY.md +50 -0
- package/docs/ROADMAP.md +57 -0
- package/docs/TOKENS.md +118 -0
- package/docs/TROUBLESHOOTING.md +68 -0
- package/docs/images/hero.png +0 -0
- package/index.js +84 -0
- package/package.json +70 -4
- package/plugins/dsh-gkb-ru-ui/cordis.patch.yml +6 -0
- package/plugins/dsh-gkb-ru-ui/lib/client.js +1331 -0
- package/plugins/dsh-gkb-ru-ui/lib/index.js +186 -0
- package/plugins/dsh-gkb-ru-ui/lib/skills.js +570 -0
- package/plugins/dsh-gkb-ru-ui/package.json +28 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/01-home-tiles-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/02-skills-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/03-skills-editor-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/04-skills-1440-dark.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/05-skills-390-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/06-home-tiles-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/README.md +70 -0
- package/plugins/dsh-peak-guard/client.js +741 -0
- package/plugins/dsh-peak-guard/cordis.patch.yml +5 -0
- package/plugins/dsh-peak-guard/icon.svg +12 -0
- package/plugins/dsh-peak-guard/index.js +2 -0
- package/plugins/dsh-peak-guard/locale/en.json +6 -0
- package/plugins/dsh-peak-guard/locale/ru.json +6 -0
- package/plugins/dsh-peak-guard/locale/zh.json +6 -0
- package/plugins/dsh-peak-guard/package.json +33 -0
- package/plugins/dsh-peak-guard/screenshots/01-real-time-peak-start.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/02-real-time-peak-end.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/03-peak-card-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/04-peak-card-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/05-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/05-peak-card-768-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/06-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/07-live-preview-check.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/07-pre-peak-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/08-peak-start-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/09-peak-end-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/10-pre-peak-card-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/11-pre-peak-card-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/12-pre-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/qa-report.json +74 -0
- package/plugins/dsh-profile-transfer/README.md +62 -0
- package/plugins/dsh-profile-transfer/bootstrap/01-INSTALL.cmd +7 -0
- package/plugins/dsh-profile-transfer/bootstrap/README-ARCHIVE.md +54 -0
- package/plugins/dsh-profile-transfer/bootstrap/README-/320/243/320/241/320/242/320/220/320/235/320/236/320/222/320/232/320/220.md +42 -0
- package/plugins/dsh-profile-transfer/bootstrap/install-on-clean-machine.ps1 +119 -0
- package/plugins/dsh-profile-transfer/bootstrap//320/243/320/241/320/242/320/220/320/235/320/236/320/222/320/230/320/242/320/254.cmd +5 -0
- package/plugins/dsh-profile-transfer/client.js +806 -0
- package/plugins/dsh-profile-transfer/cordis.patch.yml +3 -0
- package/plugins/dsh-profile-transfer/icon.svg +13 -0
- package/plugins/dsh-profile-transfer/index.js +375 -0
- package/plugins/dsh-profile-transfer/locale/en.json +6 -0
- package/plugins/dsh-profile-transfer/locale/ru.json +6 -0
- package/plugins/dsh-profile-transfer/locale/zh.json +6 -0
- package/plugins/dsh-profile-transfer/package.json +36 -0
- package/plugins/dsh-profile-transfer/screenshots/01-settings-transfer-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/02-settings-transfer-preview-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/03-settings-transfer-1440-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/04-settings-transfer-390-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/05-settings-transfer-768-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/06-settings-transfer-390-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/07-settings-transfer-export-result.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/08-settings-transfer-workspaces-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/09-settings-transfer-workspaces-1440-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/10-settings-transfer-workspaces-390-light.png +0 -0
- package/plugins/dsh-profile-transfer/transfer.js +1004 -0
- package/plugins/dsh-project-memory/README.md +71 -0
- package/plugins/dsh-project-memory/cordis.patch.yml +4 -0
- package/plugins/dsh-project-memory/icon.svg +12 -0
- package/plugins/dsh-project-memory/index.js +1240 -0
- package/plugins/dsh-project-memory/locale/en.json +6 -0
- package/plugins/dsh-project-memory/locale/ru.json +6 -0
- package/plugins/dsh-project-memory/locale/zh.json +6 -0
- package/plugins/dsh-project-memory/package.json +26 -0
- package/plugins/dsh-session-move/README.md +67 -0
- package/plugins/dsh-session-move/client.js +719 -0
- package/plugins/dsh-session-move/cordis.patch.yml +5 -0
- package/plugins/dsh-session-move/icon.svg +14 -0
- package/plugins/dsh-session-move/index.js +497 -0
- package/plugins/dsh-session-move/locale/en.json +6 -0
- package/plugins/dsh-session-move/locale/ru.json +6 -0
- package/plugins/dsh-session-move/locale/zh.json +6 -0
- package/plugins/dsh-session-move/package.json +31 -0
- package/plugins/dsh-session-move/screenshots/01-row-menu-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/02-dialog-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/03-dialog-390-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/04-dialog-1440-dark.png +0 -0
- package/plugins/dsh-session-move/screenshots/05-drag-highlight-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/06-drop-ungrouped-highlight.png +0 -0
- package/plugins/dsh-session-move/screenshots/07-after-drop-ungrouped.png +0 -0
- package/plugins/dsh-session-move/screenshots/08-move-dialog.png +0 -0
- package/plugins/dsh-session-move/screenshots/09-moved-to-second-workspace.png +0 -0
- package/plugins/dsh-session-move/screenshots/qa-report.json +62 -0
- package/plugins/dsh-skill-autopilot/cordis.patch.yml +3 -0
- package/plugins/dsh-skill-autopilot/index.js +444 -0
- package/plugins/dsh-skill-autopilot/package.json +19 -0
- package/plugins/dsh-token-lab/cordis.patch.yml +3 -0
- package/plugins/dsh-token-lab/lib/client.js +504 -0
- package/plugins/dsh-token-lab/lib/index.js +275 -0
- package/plugins/dsh-token-lab/package.json +26 -0
- package/plugins/dsh-token-lab/screenshots/01-token-lab-1440-light.png +0 -0
- package/plugins/dsh-token-lab/screenshots/02-token-lab-1440-dark.png +0 -0
- package/skills/code-review-checklist/SKILL.md +55 -0
- package/skills/content-writing/README.md +87 -0
- package/skills/content-writing/SKILL.md +85 -0
- package/skills/content-writing/assets/checklists/fact-check.md +53 -0
- package/skills/content-writing/assets/checklists/publish.md +65 -0
- package/skills/content-writing/assets/checklists/social.md +40 -0
- package/skills/content-writing/assets/templates/article.md +47 -0
- package/skills/content-writing/assets/templates/brief.md +44 -0
- package/skills/content-writing/assets/templates/doctor-card.md +41 -0
- package/skills/content-writing/assets/templates/image-prompts.md +128 -0
- package/skills/content-writing/assets/templates/post-telegram.md +36 -0
- package/skills/content-writing/assets/templates/post-vk.md +42 -0
- package/skills/content-writing/assets/templates/press-release.md +56 -0
- package/skills/content-writing/assets/templates/science-pop-note.md +55 -0
- package/skills/content-writing/assets/templates/service-card.md +46 -0
- package/skills/content-writing/references/anti-ai-style.md +248 -0
- package/skills/content-writing/references/editing.md +185 -0
- package/skills/content-writing/references/fact-checking.md +205 -0
- package/skills/content-writing/references/government-medical.md +246 -0
- package/skills/content-writing/references/images.md +212 -0
- package/skills/content-writing/references/journalism.md +252 -0
- package/skills/content-writing/references/process.md +123 -0
- package/skills/content-writing/references/quick-recipes.md +99 -0
- package/skills/content-writing/references/rules.md +85 -0
- package/skills/content-writing/references/science-popular.md +218 -0
- package/skills/content-writing/references/seo-content.md +172 -0
- package/skills/content-writing/references/social-media.md +253 -0
- package/skills/content-writing/references/sources.md +49 -0
- package/skills/content-writing/references/templates.md +291 -0
- package/skills/context-economy/SKILL.md +60 -0
- package/skills/context-economy/references/long-session.md +35 -0
- package/skills/context-economy/references/token-math.md +42 -0
- package/skills/context-economy/references/tool-recipes.md +34 -0
- package/skills/database-safety/SKILL.md +47 -0
- package/skills/debugging-playbook/SKILL.md +42 -0
- package/skills/design-critique/SKILL.md +65 -0
- package/skills/design-critique/references/a11y.md +25 -0
- package/skills/design-critique/references/diagnoses.md +33 -0
- package/skills/design-critique/references/quick-tests.md +30 -0
- package/skills/design-critique/references/report-template.md +63 -0
- package/skills/design-critique/references/rubric-fast.md +73 -0
- package/skills/dev-workflow/SKILL.md +48 -0
- package/skills/dev-workflow/references/definition-of-done.md +44 -0
- package/skills/dev-workflow/references/memory-and-boundaries.md +31 -0
- package/skills/dev-workflow/references/stages.md +67 -0
- package/skills/git-and-ci/SKILL.md +60 -0
- package/skills/pg-upgrade-verify/SKILL.md +67 -0
- package/skills/project-deep-dive/SKILL.md +45 -0
- package/skills/project-deep-dive/references/recon-commands.md +90 -0
- package/skills/project-deep-dive/references/section-templates.md +104 -0
- package/skills/project-deep-dive/references/tool-and-process.md +86 -0
- package/skills/project-memory-maintenance/SKILL.md +55 -0
- package/skills/project-memory-maintenance/references/api-and-checklist.md +99 -0
- package/skills/project-memory-maintenance/references/journal-template.md +50 -0
- package/skills/project-memory-maintenance/references/section-rules.md +59 -0
- package/skills/release-and-deploy/SKILL.md +53 -0
- package/skills/requirements-and-planning/SKILL.md +49 -0
- package/skills/requirements-and-planning/references/antipatterns-and-checklist.md +20 -0
- package/skills/requirements-and-planning/references/process-steps.md +52 -0
- package/skills/requirements-and-planning/references/templates-and-estimates.md +29 -0
- package/skills/security-review/SKILL.md +40 -0
- package/skills/seo/README.md +60 -0
- package/skills/seo/SKILL.md +77 -0
- package/skills/seo/assets/README.md +66 -0
- package/skills/seo/assets/scripts/LICENSE-playwright-core.txt +202 -0
- package/skills/seo/assets/scripts/README.md +179 -0
- package/skills/seo/assets/scripts/lib/args.mjs +105 -0
- package/skills/seo/assets/scripts/lib/browser.mjs +620 -0
- package/skills/seo/assets/scripts/lib/zip.mjs +191 -0
- package/skills/seo/assets/scripts/seo-audit.mjs +1675 -0
- package/skills/seo/assets/snippets/jsonld-breadcrumb.json +30 -0
- package/skills/seo/assets/snippets/jsonld-faq.json +39 -0
- package/skills/seo/assets/snippets/jsonld-medical-clinic.json +49 -0
- package/skills/seo/assets/snippets/jsonld-medical-webpage.json +33 -0
- package/skills/seo/assets/snippets/jsonld-organization.json +74 -0
- package/skills/seo/assets/snippets/jsonld-physician.json +46 -0
- package/skills/seo/assets/snippets/meta.blade.php +61 -0
- package/skills/seo/assets/snippets/robots.txt +61 -0
- package/skills/seo/assets/snippets/sitemap-urlset-example.xml +72 -0
- package/skills/seo/assets/snippets/sitemap.xml +36 -0
- package/skills/seo/references/antipatterns.md +66 -0
- package/skills/seo/references/audit-checklist.md +319 -0
- package/skills/seo/references/content-seo.md +204 -0
- package/skills/seo/references/eeat-medical.md +219 -0
- package/skills/seo/references/measurement.md +250 -0
- package/skills/seo/references/on-page.md +214 -0
- package/skills/seo/references/page-templates.md +56 -0
- package/skills/seo/references/stack-recipes.md +695 -0
- package/skills/seo/references/structured-data.md +487 -0
- package/skills/seo/references/technical-seo.md +296 -0
- package/skills/service-desk-conventions/SKILL.md +52 -0
- package/skills/service-desk-db-safety/SKILL.md +63 -0
- package/skills/service-desk-release/SKILL.md +61 -0
- package/skills/service-desk-testing/SKILL.md +61 -0
- package/skills/service-desk-ui/SKILL.md +52 -0
- package/skills/skills-router/SKILL.md +53 -0
- package/skills/skills-router/references/applying-skills.md +25 -0
- package/skills/skills-router/references/routing-matrix.md +52 -0
- package/skills/testing-strategy/SKILL.md +52 -0
- package/skills/ui-design-system/SKILL.md +50 -0
- package/skills/ui-design-system/references/accessibility-and-responsive.md +50 -0
- package/skills/ui-design-system/references/tokens-and-components.md +67 -0
- package/skills/ui-design-system/references/visual-qa.md +44 -0
- package/skills/web-research/SKILL.md +47 -0
- package/skills/web-research/references/answer-and-citations.md +60 -0
- package/skills/web-research/references/query-recipes.md +44 -0
- package/skills/wow-design/SKILL.md +89 -0
- package/skills/wow-design/assets/kit/README.md +195 -0
- package/skills/wow-design/assets/kit/assets/css/base.css +158 -0
- package/skills/wow-design/assets/kit/assets/css/components.css +1001 -0
- package/skills/wow-design/assets/kit/assets/css/fonts.css +73 -0
- package/skills/wow-design/assets/kit/assets/css/pages.css +737 -0
- package/skills/wow-design/assets/kit/assets/css/tokens.css +218 -0
- package/skills/wow-design/assets/kit/assets/fonts/LICENSE-golos-text.txt +93 -0
- package/skills/wow-design/assets/kit/assets/fonts/LICENSE-lora.txt +93 -0
- package/skills/wow-design/assets/kit/assets/fonts/LICENSE-unbounded.txt +93 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-700-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-700-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-400-italic.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-latin-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-latin-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-latin-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/js/kit.js +550 -0
- package/skills/wow-design/assets/kit/pages/dashboard.html +539 -0
- package/skills/wow-design/assets/kit/pages/hospital.html +430 -0
- package/skills/wow-design/assets/kit/showcase.html +1164 -0
- package/skills/wow-design/assets/scripts/LICENSE-axe-core.txt +362 -0
- package/skills/wow-design/assets/scripts/LICENSE-playwright-core.txt +202 -0
- package/skills/wow-design/assets/scripts/README.md +312 -0
- package/skills/wow-design/assets/scripts/audit.mjs +1200 -0
- package/skills/wow-design/assets/scripts/capture.mjs +337 -0
- package/skills/wow-design/assets/scripts/package.json +30 -0
- package/skills/wow-design/references/anti-patterns.md +252 -0
- package/skills/wow-design/references/critique-rubric.md +262 -0
- package/skills/wow-design/references/design-brief-template.md +48 -0
- package/skills/wow-design/references/inspiration.md +203 -0
- package/skills/wow-design/references/presets.md +99 -0
- package/skills/wow-design/references/principles.md +371 -0
- package/skills/wow-design/references/quick-presets.md +50 -0
- package/skills/wow-design/references/report-template.md +21 -0
- package/skills/wow-design/references/stack-recipes.md +96 -0
- package/skills/wow-design/references/visual-qa.md +82 -0
- package/skills/wow-design/references/wow-levers.md +43 -0
- package/tools/skill-cost.mjs +76 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="Transfer and backups">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="tr-a" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#2f7df6"/>
|
|
5
|
+
<stop offset="1" stop-color="#173a8f"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect x="2" y="2" width="60" height="60" rx="14" fill="url(#tr-a)"/>
|
|
9
|
+
<path d="M15 24h27l-6-6" fill="none" stroke="#ffffff" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round" opacity=".95"/>
|
|
10
|
+
<path d="M49 40H22l6 6" fill="none" stroke="#ffffff" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round" opacity=".95"/>
|
|
11
|
+
<rect x="13" y="30" width="13" height="4" rx="2" fill="#ffffff" opacity=".5"/>
|
|
12
|
+
<rect x="38" y="30" width="13" height="4" rx="2" fill="#ffffff" opacity=".5"/>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-profile-transfer — Host half.
|
|
3
|
+
*
|
|
4
|
+
* Authenticated Connection RPC routes for the Settings page:
|
|
5
|
+
* options — what can be exported and how big it is
|
|
6
|
+
* export — build a .tar.gz transfer archive
|
|
7
|
+
* inspect — read an archive manifest without applying it
|
|
8
|
+
* import — restore an archive into <DSH_HOME> with a backup
|
|
9
|
+
* install — run `pnpm install` in the restored profile
|
|
10
|
+
* reveal — open the archive folder in the OS file manager
|
|
11
|
+
*
|
|
12
|
+
* All file work lives in transfer.js (pure Node, testable on temporary homes).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from "node:fs";
|
|
16
|
+
import os from "node:os";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { spawn } from "node:child_process";
|
|
19
|
+
import {
|
|
20
|
+
applyBundle,
|
|
21
|
+
defaultHome,
|
|
22
|
+
describeSources,
|
|
23
|
+
exportBundle,
|
|
24
|
+
humanSize,
|
|
25
|
+
inspectBundle,
|
|
26
|
+
normalizeSelection,
|
|
27
|
+
} from "./transfer.js";
|
|
28
|
+
|
|
29
|
+
const ROUTE_BASE = "/api/dsh-profile-transfer";
|
|
30
|
+
const RPC_NAMESPACE = "dsh-profile-transfer";
|
|
31
|
+
const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
32
|
+
|
|
33
|
+
export const inject = ["connection"];
|
|
34
|
+
|
|
35
|
+
class TransferError extends Error {
|
|
36
|
+
constructor(code, message, details = {}) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.code = code;
|
|
39
|
+
this.details = details;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function environment(ctx) {
|
|
44
|
+
const home = defaultHome();
|
|
45
|
+
let profileName = process.env.DSH_PROFILE ?? "desktop";
|
|
46
|
+
if (typeof profileName !== "string" || profileName.length === 0) profileName = "desktop";
|
|
47
|
+
const profileDir = path.join(home, "profiles", profileName);
|
|
48
|
+
return { home, profileName, profileDir };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function suggestedDestination() {
|
|
52
|
+
return path.join(os.homedir(), "Documents", "DSH-обмены");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function lastSummary(home) {
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(fs.readFileSync(path.join(home, "profile-transfer-last.json"), "utf8"));
|
|
58
|
+
} catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Candidate pnpm invocations, most portable first: bundled runtime of the Desktop app, then PATH. */
|
|
64
|
+
function pnpmCandidates() {
|
|
65
|
+
const candidates = [];
|
|
66
|
+
const configured = process.env.PNPM_EXECUTABLE ?? process.env.DSH_PNPM;
|
|
67
|
+
if (typeof configured === "string" && configured.trim().length > 0) {
|
|
68
|
+
candidates.push({ command: configured.trim(), prefix: [], shell: false, label: "env" });
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
// The Desktop app bundles pnpm and runs it through its own Electron binary
|
|
72
|
+
// with ELECTRON_RUN_AS_NODE, exactly like resources/runtime/bin/node.cmd does.
|
|
73
|
+
const resources = path.join(path.dirname(process.execPath), "resources");
|
|
74
|
+
const pnpmCjs = path.join(resources, "runtime", "primary-runtime", "dependencies", "pnpm", "bin", "pnpm.cjs");
|
|
75
|
+
if (fs.existsSync(pnpmCjs)) {
|
|
76
|
+
candidates.push({ command: process.execPath, prefix: [pnpmCjs], shell: false, label: "bundled", env: { ELECTRON_RUN_AS_NODE: "1" } });
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
/* non-Desktop installs have no bundled runtime */
|
|
80
|
+
}
|
|
81
|
+
candidates.push({ command: "pnpm", prefix: [], shell: process.platform === "win32", label: "path" });
|
|
82
|
+
return candidates;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function runPnpmInstall(profileDir) {
|
|
86
|
+
const candidates = pnpmCandidates();
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
const started = Date.now();
|
|
89
|
+
let output = "";
|
|
90
|
+
let index = 0;
|
|
91
|
+
const attempt = () => {
|
|
92
|
+
const candidate = candidates[index];
|
|
93
|
+
if (candidate === undefined) {
|
|
94
|
+
resolve({ ok: false, code: "spawn-failed", output: `${output}\nни один из pnpm-кандидатов не запустился`, ms: Date.now() - started });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let child;
|
|
98
|
+
try {
|
|
99
|
+
child = spawn(candidate.command, [...candidate.prefix, "install"], {
|
|
100
|
+
cwd: profileDir,
|
|
101
|
+
shell: candidate.shell,
|
|
102
|
+
windowsHide: true,
|
|
103
|
+
env: { ...process.env, ...(candidate.env ?? {}) },
|
|
104
|
+
});
|
|
105
|
+
} catch (error) {
|
|
106
|
+
output += `\n[${candidate.label}] ${String(error)}`;
|
|
107
|
+
index += 1;
|
|
108
|
+
attempt();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
let settled = false;
|
|
112
|
+
const timer = setTimeout(() => {
|
|
113
|
+
try {
|
|
114
|
+
child.kill();
|
|
115
|
+
} catch {
|
|
116
|
+
/* ignore */
|
|
117
|
+
}
|
|
118
|
+
}, INSTALL_TIMEOUT_MS);
|
|
119
|
+
const finish = (result) => {
|
|
120
|
+
if (settled) return;
|
|
121
|
+
settled = true;
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
resolve({ ...result, executable: candidate.command, via: candidate.label });
|
|
124
|
+
};
|
|
125
|
+
const push = (chunk) => {
|
|
126
|
+
output += chunk.toString();
|
|
127
|
+
if (output.length > 20000) output = output.slice(-20000);
|
|
128
|
+
};
|
|
129
|
+
child.stdout?.on("data", push);
|
|
130
|
+
child.stderr?.on("data", push);
|
|
131
|
+
child.on("error", (error) => {
|
|
132
|
+
output += `\n[${candidate.label}] ${String(error)}`;
|
|
133
|
+
clearTimeout(timer);
|
|
134
|
+
if (settled) return;
|
|
135
|
+
settled = true;
|
|
136
|
+
index += 1;
|
|
137
|
+
attempt();
|
|
138
|
+
});
|
|
139
|
+
child.on("close", (code) => {
|
|
140
|
+
finish({ ok: code === 0, code: String(code), output: output.slice(-6000), ms: Date.now() - started });
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
attempt();
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function runReveal(target) {
|
|
148
|
+
try {
|
|
149
|
+
const platform = process.platform;
|
|
150
|
+
let child;
|
|
151
|
+
if (platform === "win32") {
|
|
152
|
+
child = spawn("explorer.exe", [fs.existsSync(target) && fs.statSync(target).isFile() ? `/select,${target}` : target], {
|
|
153
|
+
detached: true,
|
|
154
|
+
stdio: "ignore",
|
|
155
|
+
});
|
|
156
|
+
} else if (platform === "darwin") {
|
|
157
|
+
child = spawn("open", [fs.existsSync(target) && fs.statSync(target).isFile() ? path.dirname(target) : target], { detached: true, stdio: "ignore" });
|
|
158
|
+
} else {
|
|
159
|
+
child = spawn("xdg-open", [fs.existsSync(target) && fs.statSync(target).isFile() ? path.dirname(target) : target], { detached: true, stdio: "ignore" });
|
|
160
|
+
}
|
|
161
|
+
child.unref?.();
|
|
162
|
+
return true;
|
|
163
|
+
} catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ------------------------------------------------------------------ */
|
|
169
|
+
/* handlers */
|
|
170
|
+
/* ------------------------------------------------------------------ */
|
|
171
|
+
|
|
172
|
+
async function handleOptions(ctx) {
|
|
173
|
+
const { home, profileName, profileDir } = environment(ctx);
|
|
174
|
+
const description = describeSources({ home, profile: profileName });
|
|
175
|
+
return {
|
|
176
|
+
home,
|
|
177
|
+
profile: profileName,
|
|
178
|
+
profileExists: fs.existsSync(profileDir),
|
|
179
|
+
destination: suggestedDestination(),
|
|
180
|
+
selection: description.selection,
|
|
181
|
+
groups: description.groups,
|
|
182
|
+
last: lastSummary(home),
|
|
183
|
+
platform: process.platform,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function handleExport(ctx, payload) {
|
|
188
|
+
const { home, profileName } = environment(ctx);
|
|
189
|
+
const selection = normalizeSelection(payload?.selection);
|
|
190
|
+
const destinationDirectory = typeof payload?.destinationDirectory === "string" && payload.destinationDirectory.trim().length > 0
|
|
191
|
+
? path.resolve(payload.destinationDirectory.trim())
|
|
192
|
+
: suggestedDestination();
|
|
193
|
+
const result = exportBundle({ home, profile: profileName, selection, destinationDirectory });
|
|
194
|
+
return {
|
|
195
|
+
ok: true,
|
|
196
|
+
archivePath: result.archivePath,
|
|
197
|
+
human: result.human,
|
|
198
|
+
bytes: result.bytes,
|
|
199
|
+
manifest: {
|
|
200
|
+
createdAt: result.manifest.createdAt,
|
|
201
|
+
totals: result.manifest.totals,
|
|
202
|
+
selection: result.manifest.selection,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function handleInspect(ctx, payload) {
|
|
208
|
+
const archivePath = typeof payload?.archivePath === "string" ? payload.archivePath.trim() : "";
|
|
209
|
+
if (archivePath.length === 0) throw new TransferError("transfer/bad-request", "укажите путь к архиву");
|
|
210
|
+
if (!fs.existsSync(archivePath)) throw new TransferError("transfer/not-found", `архив не найден: ${archivePath}`);
|
|
211
|
+
const inspected = inspectBundle(archivePath);
|
|
212
|
+
return {
|
|
213
|
+
ok: true,
|
|
214
|
+
archivePath,
|
|
215
|
+
human: inspected.human,
|
|
216
|
+
manifest: {
|
|
217
|
+
createdAt: inspected.manifest.createdAt,
|
|
218
|
+
host: inspected.manifest.host,
|
|
219
|
+
platform: inspected.manifest.platform,
|
|
220
|
+
dshVersion: inspected.manifest.dshVersion,
|
|
221
|
+
profile: inspected.manifest.profile,
|
|
222
|
+
selection: inspected.manifest.selection,
|
|
223
|
+
totals: inspected.manifest.totals,
|
|
224
|
+
localPlugins: inspected.manifest.localPlugins,
|
|
225
|
+
},
|
|
226
|
+
files: inspected.files.length,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function handleImport(ctx, payload) {
|
|
231
|
+
const { home, profileName } = environment(ctx);
|
|
232
|
+
const archivePath = typeof payload?.archivePath === "string" ? payload.archivePath.trim() : "";
|
|
233
|
+
if (archivePath.length === 0) throw new TransferError("transfer/bad-request", "укажите путь к архиву");
|
|
234
|
+
if (!fs.existsSync(archivePath)) throw new TransferError("transfer/not-found", `архив не найден: ${archivePath}`);
|
|
235
|
+
const apply = applyBundle({
|
|
236
|
+
home,
|
|
237
|
+
profile: profileName,
|
|
238
|
+
archivePath,
|
|
239
|
+
selection: payload?.selection,
|
|
240
|
+
backup: payload?.backup !== false,
|
|
241
|
+
});
|
|
242
|
+
let install;
|
|
243
|
+
if (payload?.installDependencies === true) {
|
|
244
|
+
install = await runPnpmInstall(path.join(home, "profiles", profileName));
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
ok: true,
|
|
248
|
+
archivePath,
|
|
249
|
+
restored: apply.restored,
|
|
250
|
+
backupDir: apply.backupDir,
|
|
251
|
+
needsPnpm: apply.needsPnpm,
|
|
252
|
+
warnings: apply.warnings,
|
|
253
|
+
manifest: { createdAt: apply.manifest.createdAt, totals: apply.manifest.totals, host: apply.manifest.host },
|
|
254
|
+
install,
|
|
255
|
+
summaryPath: apply.summaryPath,
|
|
256
|
+
restartRequired: true,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function handleInstall(ctx) {
|
|
261
|
+
const { home, profileName, profileDir } = environment(ctx);
|
|
262
|
+
if (!fs.existsSync(profileDir)) throw new TransferError("transfer/not-found", `профиль не найден: ${profileDir}`);
|
|
263
|
+
const result = await runPnpmInstall(profileDir);
|
|
264
|
+
return { ok: result.ok, code: result.code, ms: result.ms, output: result.output, profileDir, home, profile: profileName };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function handleReveal(ctx, payload) {
|
|
268
|
+
const target = typeof payload?.path === "string" ? payload.path.trim() : "";
|
|
269
|
+
if (target.length === 0) throw new TransferError("transfer/bad-request", "укажите путь");
|
|
270
|
+
if (!fs.existsSync(target)) throw new TransferError("transfer/not-found", `путь не найден: ${target}`);
|
|
271
|
+
const opened = runReveal(target);
|
|
272
|
+
return { ok: opened, path: target };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function handleDiagnose(ctx) {
|
|
276
|
+
const { home, profileName, profileDir } = environment(ctx);
|
|
277
|
+
const manifest = (() => {
|
|
278
|
+
try {
|
|
279
|
+
return JSON.parse(fs.readFileSync(path.join(profileDir, "package.json"), "utf8"));
|
|
280
|
+
} catch {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
})();
|
|
284
|
+
return {
|
|
285
|
+
home,
|
|
286
|
+
profile: profileName,
|
|
287
|
+
profileExists: fs.existsSync(profileDir),
|
|
288
|
+
profileDependencies: manifest?.dependencies === null || typeof manifest?.dependencies !== "object" ? {} : manifest.dependencies,
|
|
289
|
+
nodeModules: fs.existsSync(path.join(profileDir, "node_modules")),
|
|
290
|
+
destination: suggestedDestination(),
|
|
291
|
+
last: lastSummary(home),
|
|
292
|
+
platform: process.platform,
|
|
293
|
+
node: process.version,
|
|
294
|
+
pnpmCandidates: pnpmCandidates().map((candidate) => candidate.label + ":" + candidate.command),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* ------------------------------------------------------------------ */
|
|
299
|
+
/* wire */
|
|
300
|
+
/* ------------------------------------------------------------------ */
|
|
301
|
+
|
|
302
|
+
function okEnvelope(rpcId, value) {
|
|
303
|
+
return Response.json({ type: "server-response", rpcId, result: { ok: true, value } });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function errorEnvelope(rpcId, code, message, details = {}) {
|
|
307
|
+
return Response.json({ type: "server-response", rpcId, result: { ok: false, error: { code, message, details } } });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function readEnvelope(request) {
|
|
311
|
+
let body;
|
|
312
|
+
try {
|
|
313
|
+
body = await request.json();
|
|
314
|
+
} catch {
|
|
315
|
+
throw new TransferError("transfer/bad-request", "тело запроса не JSON");
|
|
316
|
+
}
|
|
317
|
+
if (body === null || typeof body !== "object" || body.type !== "client-request" || typeof body.rpcId !== "string" || typeof body.method !== "string") {
|
|
318
|
+
throw new TransferError("transfer/bad-request", "некорректный конверт Connection");
|
|
319
|
+
}
|
|
320
|
+
return { rpcId: body.rpcId, method: body.method, payload: body.payload ?? {} };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function registerOperation(ctx, operation, handler) {
|
|
324
|
+
const route = {
|
|
325
|
+
path: `${ROUTE_BASE}/${operation}`,
|
|
326
|
+
methods: ["POST"],
|
|
327
|
+
requestBody: "buffered",
|
|
328
|
+
fetch: async (request) => {
|
|
329
|
+
let rpcId = "invalid-request";
|
|
330
|
+
try {
|
|
331
|
+
const envelope = await readEnvelope(request);
|
|
332
|
+
rpcId = envelope.rpcId;
|
|
333
|
+
if (envelope.method !== `${RPC_NAMESPACE}/${operation}`) {
|
|
334
|
+
return errorEnvelope(rpcId, "transfer/bad-request", "метод конверта не совпадает с endpoint", {});
|
|
335
|
+
}
|
|
336
|
+
const value = await handler(ctx, envelope.payload);
|
|
337
|
+
return okEnvelope(rpcId, value);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
const code = typeof error?.code === "string" ? error.code : "transfer/internal";
|
|
340
|
+
if (code === "transfer/internal") ctx.logger?.warn?.(`[profile-transfer] ${operation} failed: ${String(error)}`);
|
|
341
|
+
return errorEnvelope(rpcId, code, error instanceof Error ? error.message : String(error));
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
ctx.effect(() => {
|
|
346
|
+
const dispose = ctx.connection.fetch.register(route);
|
|
347
|
+
return () => {
|
|
348
|
+
try {
|
|
349
|
+
void Promise.resolve(dispose()).catch(() => {});
|
|
350
|
+
} catch {
|
|
351
|
+
/* carrier already unloading */
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}, `profile-transfer: ${operation} route`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export function apply(ctx) {
|
|
358
|
+
const logger = ctx.logger;
|
|
359
|
+
const { home, profileName } = environment(ctx);
|
|
360
|
+
try {
|
|
361
|
+
fs.mkdirSync(home, { recursive: true });
|
|
362
|
+
} catch (error) {
|
|
363
|
+
logger?.warn?.(`[profile-transfer] cannot prepare ${home}: ${String(error)}`);
|
|
364
|
+
}
|
|
365
|
+
registerOperation(ctx, "options", (context) => handleOptions(context));
|
|
366
|
+
registerOperation(ctx, "export", (context, payload) => handleExport(context, payload));
|
|
367
|
+
registerOperation(ctx, "inspect", (context, payload) => handleInspect(context, payload));
|
|
368
|
+
registerOperation(ctx, "import", (context, payload) => handleImport(context, payload));
|
|
369
|
+
registerOperation(ctx, "install", (context) => handleInstall(context));
|
|
370
|
+
registerOperation(ctx, "reveal", (context, payload) => handleReveal(context, payload));
|
|
371
|
+
registerOperation(ctx, "diagnose", (context) => handleDiagnose(context));
|
|
372
|
+
logger?.info?.(`[profile-transfer] ready: home ${home}, profile ${profileName}`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export { humanSize, normalizeSelection };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-profile-transfer",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Move a complete DSH setup between machines: pick what to export (profile, skills, memory, local plugins, sessions, secrets), pack one archive, restore it on a fresh install.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./index.js",
|
|
10
|
+
"./client": "./client.js",
|
|
11
|
+
"./transfer": "./transfer.js",
|
|
12
|
+
"./locale/*.json": "./locale/*.json",
|
|
13
|
+
"./icon": "./icon.svg"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"client.js",
|
|
18
|
+
"transfer.js",
|
|
19
|
+
"cordis.patch.yml",
|
|
20
|
+
"locale/*.json",
|
|
21
|
+
"icon.svg",
|
|
22
|
+
"README.md",
|
|
23
|
+
"bootstrap/*"
|
|
24
|
+
],
|
|
25
|
+
"dsh": {
|
|
26
|
+
"manifestVersion": 1,
|
|
27
|
+
"bundle": {
|
|
28
|
+
"patch": "./cordis.patch.yml"
|
|
29
|
+
},
|
|
30
|
+
"client": {
|
|
31
|
+
"platform": "web",
|
|
32
|
+
"immediately": true,
|
|
33
|
+
"inject": []
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/plugins/dsh-profile-transfer/screenshots/08-settings-transfer-workspaces-1440-light.png
ADDED
|
Binary file
|