ygc-dsh-sync 1.0.0 → 1.0.1
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/client.js +41 -0
- package/lib/plugins.js +112 -5
- package/package.json +2 -2
package/client.js
CHANGED
|
@@ -927,6 +927,32 @@ function makePanel(React) {
|
|
|
927
927
|
const others = ((detail && detail.others) || []).filter((p) => !p.installed);
|
|
928
928
|
const installedFromOthers = ((detail && detail.others) || []).filter((p) => p.installed);
|
|
929
929
|
const sources = (detail && detail.sources) || [];
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* 来源的人读形式。
|
|
933
|
+
*
|
|
934
|
+
* 需求是「备份来源」:npm 就记 npm,git 就记仓库地址。
|
|
935
|
+
* 这里只负责展示——数据来自清单里的 source 对象(由 lib/plugins.js 生成)。
|
|
936
|
+
*/
|
|
937
|
+
const sourceText = (src) => {
|
|
938
|
+
if (!src || typeof src !== 'object') return '';
|
|
939
|
+
if (src.type === 'npm') return `npm · ${src.spec || src.package || ''}`;
|
|
940
|
+
if (src.type === 'git') {
|
|
941
|
+
const ref = src.ref ? `#${src.ref}` : '';
|
|
942
|
+
return src.url ? `git · ${src.url}${ref}` : `git · ${src.raw || ''}`;
|
|
943
|
+
}
|
|
944
|
+
if (src.type === 'local') return `local · ${[src.form, src.path].filter(Boolean).join(' ')}`;
|
|
945
|
+
if (src.type === 'installation') return '随桌面端安装';
|
|
946
|
+
return src.type || '';
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
/** 来源标签的配色:git 用强调色,装不了的用警告色。 */
|
|
950
|
+
const sourceTagClass = (src) => {
|
|
951
|
+
if (!src || typeof src !== 'object') return 'dsb-tag';
|
|
952
|
+
if (src.type === 'git') return 'dsb-tag dsb-tag-ok';
|
|
953
|
+
if (src.type === 'local') return 'dsb-tag dsb-tag-warn';
|
|
954
|
+
return 'dsb-tag';
|
|
955
|
+
};
|
|
930
956
|
const last = st.lastResult || null;
|
|
931
957
|
return h(
|
|
932
958
|
'div',
|
|
@@ -1049,6 +1075,21 @@ function makePanel(React) {
|
|
|
1049
1075
|
h('span', { className: 'dsb-tag' }, (p.features || []).join(' / ')),
|
|
1050
1076
|
),
|
|
1051
1077
|
p.description ? h('div', { className: 'dsb-row-d' }, String(p.description).slice(0, 140)) : null,
|
|
1078
|
+
// 来源:npm 给 registry 坐标,git 给**仓库地址**与 ref
|
|
1079
|
+
sourceText(p.source)
|
|
1080
|
+
? h(
|
|
1081
|
+
'div',
|
|
1082
|
+
{ className: 'dsb-row-d', style: { marginTop: 2 } },
|
|
1083
|
+
'来源 ',
|
|
1084
|
+
h('span', { className: sourceTagClass(p.source) }, sourceText(p.source)),
|
|
1085
|
+
p.source && p.source.declared && p.source.type === 'npm'
|
|
1086
|
+
? h('span', { style: { opacity: 0.6 } }, ` (profile 里声明为 ${p.source.declared})`)
|
|
1087
|
+
: null,
|
|
1088
|
+
p.source && p.source.type === 'git' && p.source.raw && p.source.url !== p.source.raw
|
|
1089
|
+
? h('span', { style: { opacity: 0.6 } }, ` (原始写法 ${p.source.raw})`)
|
|
1090
|
+
: null,
|
|
1091
|
+
)
|
|
1092
|
+
: null,
|
|
1052
1093
|
),
|
|
1053
1094
|
h(
|
|
1054
1095
|
'button',
|
package/lib/plugins.js
CHANGED
|
@@ -29,6 +29,9 @@ import os from 'node:os';
|
|
|
29
29
|
|
|
30
30
|
/* ---------------------------------------------------------------- 路径 */
|
|
31
31
|
|
|
32
|
+
/** 默认 registry(写进来源里,让别人知道这个包是从哪查到的)。 */
|
|
33
|
+
export const DEFAULT_REGISTRY = 'https://registry.npmjs.org/';
|
|
34
|
+
|
|
32
35
|
/** 插件库默认根:D 盘 Ai工作空间 下的「插件库」。 */
|
|
33
36
|
export const DEFAULT_LIBRARY_ROOT = 'D:\\Ai工作空间\\插件库';
|
|
34
37
|
|
|
@@ -377,6 +380,82 @@ function bytesOf(file) {
|
|
|
377
380
|
}
|
|
378
381
|
}
|
|
379
382
|
|
|
383
|
+
/* ------------------------------------------------------------ 来源描述 */
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* 描述一个插件的**来源**(v1.0.1 新增)。
|
|
387
|
+
*
|
|
388
|
+
* 要记的是「这个插件从哪来」,不是「它的代码长什么样」——所以这里只给出
|
|
389
|
+
* 类型与坐标,绝不读、也不写任何源码。
|
|
390
|
+
*
|
|
391
|
+
* 四种形态:
|
|
392
|
+
* npm registry 包:registry 地址 + 包名@版本 + profile 里的原始写法
|
|
393
|
+
* git git 仓库:**仓库 URL** + 钉住的 ref(分支/标签/commit)+ 原始写法
|
|
394
|
+
* local 本地路径(file: / link: / workspace: / 直链 tar):形式 + 路径
|
|
395
|
+
* installation 随桌面端安装提供:没有可复现的来源,如实写明
|
|
396
|
+
*
|
|
397
|
+
* @param {{name?:string, kind?:string, spec?:string|null, version?:string|null}} plugin
|
|
398
|
+
* @returns {{type:string}}
|
|
399
|
+
*/
|
|
400
|
+
export function describeSource(plugin) {
|
|
401
|
+
const name = String(plugin?.name || '');
|
|
402
|
+
const kind = String(plugin?.kind || '');
|
|
403
|
+
const version = plugin?.version ? String(plugin.version) : '';
|
|
404
|
+
const spec = typeof plugin?.spec === 'string' ? plugin.spec.trim() : '';
|
|
405
|
+
|
|
406
|
+
if (kind === 'registry') {
|
|
407
|
+
return {
|
|
408
|
+
type: 'npm',
|
|
409
|
+
registry: DEFAULT_REGISTRY,
|
|
410
|
+
package: name,
|
|
411
|
+
version,
|
|
412
|
+
// 别人拿去安装的坐标,与 installSpec 一致
|
|
413
|
+
spec: version ? `${name}@${version}` : name,
|
|
414
|
+
// profile 里的原始写法(纯版本号 / ^ / ~ 都可能),能看出当初怎么声明的
|
|
415
|
+
declared: spec || undefined,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (kind === 'git') {
|
|
420
|
+
const parsed = parseGitSpec(spec);
|
|
421
|
+
return {
|
|
422
|
+
type: 'git',
|
|
423
|
+
// 仓库地址:优先归一成 https 形式,别人那台机器能直接 clone
|
|
424
|
+
url: (parsed && parsed.url) || spec || undefined,
|
|
425
|
+
// 钉住的 ref(分支 / 标签 / commit);没有就是跟默认分支
|
|
426
|
+
ref: (parsed && parsed.ref) || undefined,
|
|
427
|
+
owner: (parsed && parsed.owner) || undefined,
|
|
428
|
+
repo: (parsed && parsed.repo) || undefined,
|
|
429
|
+
// 原始写法照录:`github:o/r#v1` 与 `git+https://…` 归一后看不出区别
|
|
430
|
+
raw: spec || undefined,
|
|
431
|
+
version,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (kind === 'local') {
|
|
436
|
+
const m = /^(file|link|workspace|portal):(.*)$/i.exec(spec);
|
|
437
|
+
const form = m ? m[1].toLowerCase() : /^https?:\/\//i.test(spec) ? 'tarball' : 'unknown';
|
|
438
|
+
return {
|
|
439
|
+
type: 'local',
|
|
440
|
+
form,
|
|
441
|
+
// 路径照录:这是本机事实,别人装不了,但要知道它指向哪
|
|
442
|
+
path: m ? m[2].trim() : spec || undefined,
|
|
443
|
+
version,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (kind === 'installation') {
|
|
448
|
+
return {
|
|
449
|
+
type: 'installation',
|
|
450
|
+
// 随 DSH 桌面端一起发布,版本跟桌面端走,没有独立的可复现来源
|
|
451
|
+
bundledWith: 'dsh-desktop',
|
|
452
|
+
version: version || undefined,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return { type: 'unknown', kind: kind || undefined, spec: spec || undefined };
|
|
457
|
+
}
|
|
458
|
+
|
|
380
459
|
/* ------------------------------------------------------- 本机插件枚举 */
|
|
381
460
|
|
|
382
461
|
/**
|
|
@@ -417,6 +496,8 @@ export function collectLocalPlugins(options) {
|
|
|
417
496
|
installable: Boolean(installSpec),
|
|
418
497
|
description: meta.description || '',
|
|
419
498
|
homepage: meta.homepage || '',
|
|
499
|
+
// 来源:npm / git(含仓库地址与 ref)/ local / installation
|
|
500
|
+
source: describeSource({ name, kind, spec, version }),
|
|
420
501
|
reason: registryLike
|
|
421
502
|
? 'registry 来源,按名称+版本联网拉取'
|
|
422
503
|
: kind === 'git'
|
|
@@ -461,7 +542,8 @@ export async function syncMyFeature({ config, machineId, machineHash, machineNam
|
|
|
461
542
|
const purged = purgeLegacyBundles(root);
|
|
462
543
|
|
|
463
544
|
const manifest = {
|
|
464
|
-
|
|
545
|
+
// 3:新增 source(记录来源类型与坐标,不含任何源码)
|
|
546
|
+
schemaVersion: 3,
|
|
465
547
|
feature,
|
|
466
548
|
machine: machineName || '',
|
|
467
549
|
machineId: feature,
|
|
@@ -481,6 +563,10 @@ export async function syncMyFeature({ config, machineId, machineHash, machineNam
|
|
|
481
563
|
manifestOnly: true,
|
|
482
564
|
storesBundles: false,
|
|
483
565
|
installVia: 'registry',
|
|
566
|
+
// 来源记录的边界:只记「从哪来」,不记「代码长什么样」
|
|
567
|
+
recordsSource: true,
|
|
568
|
+
storesSourceCode: false,
|
|
569
|
+
sourceKinds: ['npm', 'git', 'local', 'installation'],
|
|
484
570
|
note: '特征文件夹只存清单;安装一律按 installSpec 联网拉取,不使用本地副本。',
|
|
485
571
|
},
|
|
486
572
|
plugins,
|
|
@@ -631,15 +717,22 @@ export function collectSharedPlugins({ config, machineId, publishedNames } = {})
|
|
|
631
717
|
: kind === 'git' && typeof plugin.spec === 'string'
|
|
632
718
|
? plugin.spec
|
|
633
719
|
: '';
|
|
720
|
+
const specOf = typeof plugin.spec === 'string' ? plugin.spec : '';
|
|
634
721
|
const record = {
|
|
635
722
|
name: plugin.name,
|
|
636
723
|
version,
|
|
637
724
|
kind,
|
|
638
|
-
spec:
|
|
725
|
+
spec: specOf,
|
|
639
726
|
installSpec,
|
|
640
727
|
installable: Boolean(installSpec),
|
|
641
728
|
description: typeof plugin.description === 'string' ? plugin.description : '',
|
|
642
729
|
homepage: typeof plugin.homepage === 'string' ? plugin.homepage : '',
|
|
730
|
+
// 来源:新清单自带就用它;老清单没这个字段就按现有字段现推一份,
|
|
731
|
+
// 这样别人那台跑旧版插件时,本机看到的来源也不会是空的
|
|
732
|
+
source:
|
|
733
|
+
plugin.source && typeof plugin.source === 'object'
|
|
734
|
+
? plugin.source
|
|
735
|
+
: describeSource({ name: plugin.name, kind, spec: specOf, version }),
|
|
643
736
|
features: [feature],
|
|
644
737
|
mine: isMine || Boolean(plugin.mine),
|
|
645
738
|
};
|
|
@@ -659,6 +752,7 @@ export function collectSharedPlugins({ config, machineId, publishedNames } = {})
|
|
|
659
752
|
old.installable = Boolean(old.installSpec);
|
|
660
753
|
old.description = record.description || old.description;
|
|
661
754
|
old.homepage = record.homepage || old.homepage;
|
|
755
|
+
old.source = record.source || old.source;
|
|
662
756
|
} else if (!old.installSpec && record.installSpec) {
|
|
663
757
|
old.installSpec = record.installSpec;
|
|
664
758
|
old.installable = true;
|
|
@@ -685,6 +779,19 @@ export function writeAggregate({ config, machineId, publishedNames, log } = {})
|
|
|
685
779
|
installSpec: plugin.installSpec || '',
|
|
686
780
|
}));
|
|
687
781
|
|
|
782
|
+
/** 来源的人读形式:git 给仓库 URL,npm 给 registry 坐标,local 给路径。 */
|
|
783
|
+
const sourceText = (src) => {
|
|
784
|
+
if (!src || typeof src !== 'object') return '-';
|
|
785
|
+
if (src.type === 'npm') return `npm · ${src.spec || src.package || ''}`;
|
|
786
|
+
if (src.type === 'git') {
|
|
787
|
+
const ref = src.ref ? `#${src.ref}` : '';
|
|
788
|
+
return src.url ? `git · ${src.url}${ref}` : `git · ${src.raw || '-'}`;
|
|
789
|
+
}
|
|
790
|
+
if (src.type === 'local') return `local · ${src.form || ''} ${src.path || ''}`.trim();
|
|
791
|
+
if (src.type === 'installation') return '随桌面端安装';
|
|
792
|
+
return src.type || '-';
|
|
793
|
+
};
|
|
794
|
+
|
|
688
795
|
const others = items.filter((item) => !item.mine);
|
|
689
796
|
const installable = others.filter((item) => !item.installed && item.installKind !== 'none');
|
|
690
797
|
|
|
@@ -732,11 +839,11 @@ export function writeAggregate({ config, machineId, publishedNames, log } = {})
|
|
|
732
839
|
'',
|
|
733
840
|
'## 他人插件',
|
|
734
841
|
'',
|
|
735
|
-
'| 插件 | 版本 | 来源 | 本机 | 安装方式 | 安装坐标 |',
|
|
736
|
-
'
|
|
842
|
+
'| 插件 | 版本 | 来源 | 出处 | 本机 | 安装方式 | 安装坐标 |',
|
|
843
|
+
'|---|---|---|---|---|---|---|',
|
|
737
844
|
...others.map(
|
|
738
845
|
(item) =>
|
|
739
|
-
`| \`${item.name}\` | ${item.version || '-'} | ${item.features.join(', ')} | ${
|
|
846
|
+
`| \`${item.name}\` | ${item.version || '-'} | ${item.features.join(', ')} | ${sourceText(item.source)} | ${
|
|
740
847
|
item.installed ? `已装 ${item.installedVersion}` : '无'
|
|
741
848
|
} | ${item.installKind} | ${item.installSpec ? `\`${item.installSpec}\`` : '-'} |`,
|
|
742
849
|
),
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ygc-dsh-sync",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "DSH
|
|
5
|
+
"description": "DSH 同步插件:会话按工作区落地、按机器隔离的跨机备份与拉取;技能本机↔母版双向同步(含记忆插件技能优化闭环);插件清单共享(只存清单与来源,不存源码,只能联网装)。",
|
|
6
6
|
"icon": "icon.svg",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"dsh",
|